@pie-players/pie-section-player 0.3.36 → 0.3.38
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 +85 -68
- package/dist/chunks/tool-annotation-toolbar-db5bfbec.js +197 -197
- package/dist/contracts/runtime-host-contract.d.ts +0 -13
- package/dist/contracts/runtime-host-contract.d.ts.map +1 -1
- package/dist/pie-section-player.d.ts +2 -2
- package/dist/pie-section-player.d.ts.map +1 -1
- package/dist/pie-section-player.js +8090 -7960
- package/dist/policies/index.d.ts +1 -8
- package/dist/policies/index.d.ts.map +1 -1
- package/dist/policies/types.d.ts +0 -21
- package/dist/policies/types.d.ts.map +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -32,6 +32,73 @@ entrypoints under `@pie-players/pie-section-player/components/*`.
|
|
|
32
32
|
Standalone browser variants for this package are intentionally deferred; the
|
|
33
33
|
current supported contract is the default bundler entrypoints under `dist`.
|
|
34
34
|
|
|
35
|
+
## SectionController
|
|
36
|
+
|
|
37
|
+
`SectionController` is the domain authority inside a section player. It owns
|
|
38
|
+
in-section navigation state, the canonical aggregation of per-item sessions,
|
|
39
|
+
and the host-facing persistence snapshot. The layout custom elements
|
|
40
|
+
(`pie-section-player-splitpane` / `-vertical` / `-tabbed`) are transport
|
|
41
|
+
adapters around it. See
|
|
42
|
+
[`docs/section-player/controller-boundaries.md`](../../docs/section-player/controller-boundaries.md)
|
|
43
|
+
for the rationale behind that split, and
|
|
44
|
+
[`docs/section-player/client-architecture-tutorial.md`](../../docs/section-player/client-architecture-tutorial.md)
|
|
45
|
+
for the end-to-end walkthrough.
|
|
46
|
+
|
|
47
|
+
The handle implements `SectionControllerHandle` from
|
|
48
|
+
`@pie-players/pie-assessment-toolkit`; see the JSDoc on that interface for
|
|
49
|
+
the per-method contract.
|
|
50
|
+
|
|
51
|
+
### Obtaining the handle
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const host = document.querySelector("pie-section-player-splitpane") as any;
|
|
55
|
+
const controller = await host.waitForSectionController?.(5000);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`waitForSectionController(timeoutMs)` resolves when the layout CE has wired
|
|
59
|
+
its controller (the same anchor `pie-stage-change` reaches with
|
|
60
|
+
`detail.stage === "engine-ready"`). Use `getSectionController()` if you've
|
|
61
|
+
already passed the readiness anchor synchronously. The legacy
|
|
62
|
+
`section-controller-ready` event was removed; either of these helpers
|
|
63
|
+
replaces it.
|
|
64
|
+
|
|
65
|
+
### Session lifecycle
|
|
66
|
+
|
|
67
|
+
A typical host flow:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
controller?.configureSessionPersistence?.({ context, strategy });
|
|
71
|
+
await controller?.hydrate?.();
|
|
72
|
+
const unsubscribe = controller?.subscribe?.(handleEvent);
|
|
73
|
+
// ...later, on save / unload:
|
|
74
|
+
await controller?.persist?.();
|
|
75
|
+
unsubscribe?.();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`getSession()` / `applySession(session, { mode })` / `updateItemSession(itemId,
|
|
79
|
+
detail)` are the direct read/write surfaces and exchange the same
|
|
80
|
+
`SectionControllerSessionState` shape the persistence strategy load/save
|
|
81
|
+
methods receive. See [Item session management](#item-session-management) for
|
|
82
|
+
worked examples.
|
|
83
|
+
|
|
84
|
+
### Event stream
|
|
85
|
+
|
|
86
|
+
The controller's typed event stream (`SectionControllerEvent` discriminated
|
|
87
|
+
union) is the single source of truth for in-section change. Hosts usually
|
|
88
|
+
subscribe through `ToolkitCoordinator.subscribeItemEvents` /
|
|
89
|
+
`subscribeSectionLifecycleEvents` (cohort-aware filtering, survives
|
|
90
|
+
navigation) — see [JS API example for advanced host
|
|
91
|
+
policy](#js-api-example-for-advanced-host-policy). Key event types:
|
|
92
|
+
|
|
93
|
+
- `item-selected` — item navigation within the current section.
|
|
94
|
+
- `item-session-data-changed` / `item-session-meta-changed` — per-item
|
|
95
|
+
session updates the persistence layer should observe.
|
|
96
|
+
- `content-loaded` — passage / item / rubric finished loading.
|
|
97
|
+
- `section-loading-complete` — every renderable in the section finished
|
|
98
|
+
loading.
|
|
99
|
+
- `section-items-complete-changed` — aggregate completion flip.
|
|
100
|
+
- `section-navigation-change` — the controller's section identity changed.
|
|
101
|
+
|
|
35
102
|
## Usage
|
|
36
103
|
|
|
37
104
|
Import the custom-element registration entrypoint in consumers:
|
|
@@ -176,50 +243,25 @@ host.hooks = {
|
|
|
176
243
|
|
|
177
244
|
Advanced CE props are still supported as escape hatches (`runtime`, `coordinator`, etc.), but hosts should prefer JS/controller composition for non-standard behavior. Note: `createSectionController` is **runtime-only** — set it on `runtime.createSectionController` rather than as a top-level CE prop (the prop alias was removed in the broad architecture review compat sweep).
|
|
178
245
|
|
|
179
|
-
###
|
|
180
|
-
|
|
181
|
-
Section-player owns its own focus contract so hosts can integrate a
|
|
182
|
-
"Skip to Main" affordance and keyboard navigation without re-encoding
|
|
183
|
-
the passage-first rule.
|
|
184
|
-
|
|
185
|
-
**Public focus targets.** Both card custom elements are `tabindex="-1"`
|
|
186
|
-
(programmatically focusable, never in sequential tab order) and expose a
|
|
187
|
-
`:focus-visible` outline:
|
|
188
|
-
|
|
189
|
-
- `<pie-section-player-passage-card>` (when the section has a passage)
|
|
190
|
-
- `<pie-section-player-item-card>`
|
|
191
|
-
|
|
192
|
-
The inner `.pie-section-player-content-card[data-section-item-card]`
|
|
193
|
-
selector is an internal back-compat hook and is not part of the public
|
|
194
|
-
contract — prefer querying the custom element tags.
|
|
195
|
-
|
|
196
|
-
**Declarative control.** The `SectionPlayerFocusPolicy.autoFocus` strategy
|
|
197
|
-
governs focus on every navigation event (Next / Back / `navigateTo`) and is
|
|
198
|
-
also honored by the imperative `focusStart()`:
|
|
199
|
-
|
|
200
|
-
- `"start-of-content"` *(default)* — focus the passage card when present,
|
|
201
|
-
otherwise the first item card. Matches the canonical assessment UX where
|
|
202
|
-
Skip-to-Main and navigation land in the same place. Best for
|
|
203
|
-
one-item-per-page layouts (splitpane, tabbed).
|
|
204
|
-
- `"current-item"` — focus the newly-active item card (queried as
|
|
205
|
-
`pie-section-player-item-card[is-current]`). Best for stacked/list
|
|
206
|
-
layouts where multiple items are visible at once (vertical, custom
|
|
207
|
-
kernel-host variants). Works in both paginated and keep-together
|
|
208
|
-
sections — QTI 3 `keep-together` is a pagination hint only and does
|
|
209
|
-
not disable item-level navigation or current-item tracking.
|
|
210
|
-
- `"none"` — framework never moves focus on navigation; the host owns it
|
|
211
|
-
entirely. `focusStart()` still moves focus (defaults to
|
|
212
|
-
start-of-content) because hosts only call it when they *want* focus to
|
|
213
|
-
move.
|
|
246
|
+
### Host-owned focus
|
|
214
247
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
248
|
+
Section-player does not move focus on behalf of host-level affordances such
|
|
249
|
+
as "Skip to Main", nor does it make passage/question containers tab stops.
|
|
250
|
+
Hosts own page chrome, skip links, landmarks, and any special focus placement.
|
|
251
|
+
For example, Quiz Engine's Fixed Player shell can focus its own
|
|
252
|
+
`main#main-content`; the next Tab then follows the browser's natural order
|
|
253
|
+
into the first actionable control rendered inside the section player.
|
|
254
|
+
|
|
255
|
+
The passage and item card custom elements are content/layout surfaces, not
|
|
256
|
+
public focus targets. Splitpane passage content remains scrollable through the
|
|
257
|
+
pane's native scroll behavior, but the passage pane itself is not inserted into
|
|
258
|
+
sequential keyboard navigation.
|
|
259
|
+
|
|
260
|
+
```html
|
|
261
|
+
<a href="#main-content" class="skip-link">Skip to Main</a>
|
|
262
|
+
<main id="main-content" tabindex="-1">
|
|
263
|
+
<pie-section-player-splitpane></pie-section-player-splitpane>
|
|
264
|
+
</main>
|
|
223
265
|
```
|
|
224
266
|
|
|
225
267
|
**Wired policy toggles.** Each `SectionPlayerPolicies` field has a real
|
|
@@ -232,8 +274,6 @@ runtime effect; nothing in this surface is decorative.
|
|
|
232
274
|
still mount and item-players register their own elements on demand. Use
|
|
233
275
|
this to disable section pre-warm when the host already owns element
|
|
234
276
|
registration end-to-end. Default: `true`.
|
|
235
|
-
- `focus.autoFocus` — focus strategy on navigation; see "Focus management"
|
|
236
|
-
above.
|
|
237
277
|
- `telemetry.enabled` — when `false`, the layout custom elements skip
|
|
238
278
|
`attachInstrumentationEventBridge` setup, so no `pie-section-*`
|
|
239
279
|
telemetry events flow through the bridge. Hosts that want a different
|
|
@@ -245,29 +285,6 @@ helpers read these toggles with the documented default-true semantics, so
|
|
|
245
285
|
host code that needs to mirror the same gate (e.g. when composing a custom
|
|
246
286
|
layout host) can call them directly.
|
|
247
287
|
|
|
248
|
-
**Imperative escape hatch (host-owned focus moments).** Every layout
|
|
249
|
-
element (`pie-section-player-splitpane`, `-vertical`, `-tabbed`,
|
|
250
|
-
`-kernel-host`, and `-base`) exposes a `focusStart(): boolean` method.
|
|
251
|
-
Call it from host-owned affordances the framework cannot observe — most
|
|
252
|
-
commonly a "Skip to Main" button in the host's ribbon:
|
|
253
|
-
|
|
254
|
-
```ts
|
|
255
|
-
document.querySelector("pie-section-player-splitpane")?.focusStart();
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
`focusStart()` honors the `autoFocus` strategy so Skip-to-Main lands
|
|
259
|
-
wherever the host has opted in for navigation focus:
|
|
260
|
-
|
|
261
|
-
- `"start-of-content"` *(default)* → passage card when present, else first
|
|
262
|
-
item card.
|
|
263
|
-
- `"current-item"` → the item card currently marked `is-current`, falling
|
|
264
|
-
back to start-of-content if no current item is resolvable.
|
|
265
|
-
- `"none"` → start-of-content (hosts call `focusStart()` precisely
|
|
266
|
-
because they want focus to move).
|
|
267
|
-
|
|
268
|
-
For Next / Back / question-number navigation the host does **not** call
|
|
269
|
-
`focusStart()` — the `autoFocus` policy fires automatically.
|
|
270
|
-
|
|
271
288
|
### Navigation signals
|
|
272
289
|
|
|
273
290
|
- `item-selected`: item-level navigation change within the current section in the `SectionController` broadcast stream (`itemIndex`, `currentItemId`, `totalItems`).
|