@kerusiweb/core 2.3.0 → 3.1.0
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/LICENSE +22 -22
- package/README.md +74 -68
- package/dist/kerusi/index.d.ts +4 -3
- package/dist/kerusi/index.d.ts.map +1 -1
- package/dist/kerusi/index.js +4 -3
- package/dist/kerusi/index.js.map +1 -1
- package/dist/kerusi/kerusi-geometry.d.ts +47 -0
- package/dist/kerusi/kerusi-geometry.d.ts.map +1 -0
- package/dist/kerusi/kerusi-geometry.js +111 -0
- package/dist/kerusi/kerusi-geometry.js.map +1 -0
- package/dist/kerusi/kerusi-map.model.d.ts +121 -68
- package/dist/kerusi/kerusi-map.model.d.ts.map +1 -1
- package/dist/kerusi/kerusi-map.model.js +6 -2
- package/dist/kerusi/kerusi-map.model.js.map +1 -1
- package/dist/kerusi/kerusi-rows.d.ts +2 -2
- package/dist/kerusi/kerusi-rows.js +2 -2
- package/dist/kerusi/kerusi-validator.d.ts +18 -9
- package/dist/kerusi/kerusi-validator.d.ts.map +1 -1
- package/dist/kerusi/kerusi-validator.js +301 -122
- package/dist/kerusi/kerusi-validator.js.map +1 -1
- package/dist/render/build-render-model.d.ts +8 -4
- package/dist/render/build-render-model.d.ts.map +1 -1
- package/dist/render/build-render-model.js +147 -45
- package/dist/render/build-render-model.js.map +1 -1
- package/dist/render/index.d.ts +1 -1
- package/dist/render/index.d.ts.map +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/index.js.map +1 -1
- package/dist/render/map-layout.d.ts +92 -0
- package/dist/render/map-layout.d.ts.map +1 -0
- package/dist/render/map-layout.js +93 -0
- package/dist/render/map-layout.js.map +1 -0
- package/dist/render/navigation-order.d.ts +20 -10
- package/dist/render/navigation-order.d.ts.map +1 -1
- package/dist/render/navigation-order.js +72 -57
- package/dist/render/navigation-order.js.map +1 -1
- package/dist/render/render-model.d.ts +48 -26
- package/dist/render/render-model.d.ts.map +1 -1
- package/dist/render/seat-shapes.d.ts +3 -3
- package/dist/render/seat-shapes.js +3 -3
- package/dist/view/index.d.ts +6 -2
- package/dist/view/index.d.ts.map +1 -1
- package/dist/view/index.js +6 -2
- package/dist/view/index.js.map +1 -1
- package/dist/view/rtl.d.ts +1 -1
- package/dist/view/rtl.js +1 -1
- package/dist/view/seat-aria.d.ts +6 -0
- package/dist/view/seat-aria.d.ts.map +1 -1
- package/dist/view/seat-aria.js +8 -0
- package/dist/view/seat-aria.js.map +1 -1
- package/dist/view/seat-card-placement.d.ts +60 -0
- package/dist/view/seat-card-placement.d.ts.map +1 -0
- package/dist/view/seat-card-placement.js +94 -0
- package/dist/view/seat-card-placement.js.map +1 -0
- package/dist/view/seat-card.d.ts +83 -0
- package/dist/view/seat-card.d.ts.map +1 -0
- package/dist/view/seat-card.js +68 -0
- package/dist/view/seat-card.js.map +1 -0
- package/dist/view/seat-glyph.d.ts +1 -1
- package/dist/view/seat-glyph.d.ts.map +1 -1
- package/dist/view/seat-hover.d.ts +90 -0
- package/dist/view/seat-hover.d.ts.map +1 -0
- package/dist/view/seat-hover.js +205 -0
- package/dist/view/seat-hover.js.map +1 -0
- package/dist/view/viewport.d.ts +35 -7
- package/dist/view/viewport.d.ts.map +1 -1
- package/dist/view/viewport.js +56 -7
- package/dist/view/viewport.js.map +1 -1
- package/package.json +42 -42
- package/dist/kerusi/kerusi-layout-mode.d.ts +0 -58
- package/dist/kerusi/kerusi-layout-mode.d.ts.map +0 -1
- package/dist/kerusi/kerusi-layout-mode.js +0 -117
- package/dist/kerusi/kerusi-layout-mode.js.map +0 -1
- package/dist/render/section-layout.d.ts +0 -66
- package/dist/render/section-layout.d.ts.map +0 -1
- package/dist/render/section-layout.js +0 -186
- package/dist/render/section-layout.js.map +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { elementStyle } from './element-shapes.js';
|
|
2
|
+
/**
|
|
3
|
+
* Places every seat and element of a map on its canvas.
|
|
4
|
+
*
|
|
5
|
+
* The canvas never shrinks to fit what is drawn: a section left out of the
|
|
6
|
+
* render model, or hidden by a binding, leaves its space empty, because closing
|
|
7
|
+
* the gap would move the sections around it.
|
|
8
|
+
*/
|
|
9
|
+
export function computeMapLayout(map, options = {}) {
|
|
10
|
+
const { width, height } = map.canvas;
|
|
11
|
+
const mirror = options.rtl ? (x) => width - x : (x) => x;
|
|
12
|
+
const turn = (rotation) => (options.rtl ? -(rotation ?? 0) : (rotation ?? 0)) + 0;
|
|
13
|
+
const sections = map.sections.map((section) => {
|
|
14
|
+
const bounds = mirrorBounds(section.bounds, options.rtl ? width : undefined);
|
|
15
|
+
return {
|
|
16
|
+
section,
|
|
17
|
+
seats: section.seats.map((seat) => placeSeat(seat, mirror(seat.x), seat.y, turn(seat.rotation))),
|
|
18
|
+
elements: section.elements.map((element) => placeElement(element, mirror(element.x), element.y, turn(element.rotation), section.seatSize)),
|
|
19
|
+
bounds,
|
|
20
|
+
labelAnchor: labelAnchorOf(bounds, section.seatSize, !!options.rtl),
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
return { width, height, sections };
|
|
24
|
+
}
|
|
25
|
+
function placeSeat(seat, centerX, centerY, rotation) {
|
|
26
|
+
const size = seat.size;
|
|
27
|
+
return {
|
|
28
|
+
seat,
|
|
29
|
+
x: centerX - size / 2,
|
|
30
|
+
y: centerY - size / 2,
|
|
31
|
+
width: size,
|
|
32
|
+
height: size,
|
|
33
|
+
centerX,
|
|
34
|
+
centerY,
|
|
35
|
+
fontSize: size * 0.4,
|
|
36
|
+
rotation,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function placeElement(element, centerX, centerY, rotation, seatSize) {
|
|
40
|
+
const { width, height } = element;
|
|
41
|
+
return {
|
|
42
|
+
element,
|
|
43
|
+
x: centerX - width / 2,
|
|
44
|
+
y: centerY - height / 2,
|
|
45
|
+
width,
|
|
46
|
+
height,
|
|
47
|
+
centerX,
|
|
48
|
+
centerY,
|
|
49
|
+
fontSize: elementFontSize(element, width, height, seatSize),
|
|
50
|
+
rotation,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** The label's inset from the bounds' corner and its size, as fractions of the seat size. */
|
|
54
|
+
const LABEL_INSET_RATIO = 0.25;
|
|
55
|
+
const LABEL_FONT_RATIO = 0.5;
|
|
56
|
+
function labelAnchorOf(bounds, seatSize, rtl) {
|
|
57
|
+
const inset = seatSize * LABEL_INSET_RATIO;
|
|
58
|
+
const fontSize = seatSize * LABEL_FONT_RATIO;
|
|
59
|
+
const above = bounds.y - inset - fontSize;
|
|
60
|
+
const edge = above >= 0 ? 0 : inset;
|
|
61
|
+
return {
|
|
62
|
+
x: rtl ? bounds.x + bounds.width - edge : bounds.x + edge,
|
|
63
|
+
y: above >= 0 ? above : bounds.y + inset,
|
|
64
|
+
fontSize,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** A box, mirrored about a canvas of width `canvasWidth` when one is given. */
|
|
68
|
+
function mirrorBounds(bounds, canvasWidth) {
|
|
69
|
+
if (canvasWidth === undefined) {
|
|
70
|
+
return { ...bounds };
|
|
71
|
+
}
|
|
72
|
+
return { ...bounds, x: canvasWidth - bounds.x - bounds.width };
|
|
73
|
+
}
|
|
74
|
+
/** Average glyph advance as a fraction of the font size, for fitting text to a width. */
|
|
75
|
+
const GLYPH_WIDTH = 0.6;
|
|
76
|
+
/**
|
|
77
|
+
* The smallest element font, as a fraction of the section's seat size. Canvas
|
|
78
|
+
* units are abstract, so a floor in units would be huge on one map and
|
|
79
|
+
* invisible on another; the seat size is the one length every map has.
|
|
80
|
+
*/
|
|
81
|
+
const MIN_FONT_RATIO = 0.2;
|
|
82
|
+
function elementFontSize(element, width, height, seatSize) {
|
|
83
|
+
const floor = seatSize * MIN_FONT_RATIO;
|
|
84
|
+
if (elementStyle(element.kind).shape !== 'text') {
|
|
85
|
+
return Math.max(Math.min(height * 0.5, width * 0.18), floor);
|
|
86
|
+
}
|
|
87
|
+
// A text element has no box to read from, so the height sets the size and the
|
|
88
|
+
// width only shrinks it, so the whole label fits however long it is.
|
|
89
|
+
const base = Math.max(height * 0.6, floor);
|
|
90
|
+
const length = element.label?.length ?? 0;
|
|
91
|
+
return length ? Math.min(base, width / (length * GLYPH_WIDTH)) : base;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=map-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-layout.js","sourceRoot":"","sources":["../../src/render/map-layout.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,15 +2,24 @@ import { RenderSection } from './render-model.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Keyboard navigation order over a seat map.
|
|
4
4
|
*
|
|
5
|
-
* §4.3.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* §4.3.5 makes `row` and `col` the source of truth for "next to": two seats are
|
|
6
|
+
* neighbours when they share a row and their cols differ by one, whatever the
|
|
7
|
+
* drawing looks like. That is exactly what "the seat to my right" must mean for
|
|
8
|
+
* a keyboard or a screen reader, so a curved balcony or a round table still
|
|
9
|
+
* reads seat 1, 2, 3.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* - Left/right moves along the row in `col` order. At an aisle, a skipped
|
|
12
|
+
* `col`, it jumps to the next seat in the row rather than stopping, so a
|
|
13
|
+
* keyboard user is never stranded; `RenderSeat.besideAisle` lets the seat's
|
|
14
|
+
* description say the aisle is there.
|
|
15
|
+
* - Up/down moves to the next row in the §4.2.1 order and picks the seat
|
|
16
|
+
* nearest on the canvas.
|
|
17
|
+
* - Seats with no `row` have no adjacency, so they fall back to a heuristic:
|
|
18
|
+
* banded by `y` and ordered by `x`.
|
|
19
|
+
*
|
|
20
|
+
* The arrows follow the screen. A row whose cols run right to left on the
|
|
21
|
+
* canvas, or a mirrored canvas (D3), swaps left and right; rows ordered up the
|
|
22
|
+
* screen swap up and down.
|
|
14
23
|
*/
|
|
15
24
|
/** The seats reachable from a given seat, by id. */
|
|
16
25
|
export interface SeatNeighbours {
|
|
@@ -32,7 +41,7 @@ export interface NavigationGraph {
|
|
|
32
41
|
last?: string;
|
|
33
42
|
}
|
|
34
43
|
export interface NavigationOptions {
|
|
35
|
-
/**
|
|
44
|
+
/** The canvas is mirrored for a right-to-left locale, so left and right swap with it. */
|
|
36
45
|
rtl?: boolean;
|
|
37
46
|
/**
|
|
38
47
|
* Restrict traversal to seats passing this test — e.g. to skip unselectable
|
|
@@ -43,7 +52,8 @@ export interface NavigationOptions {
|
|
|
43
52
|
}
|
|
44
53
|
/**
|
|
45
54
|
* Builds the navigation graph for one section. Sections are navigated
|
|
46
|
-
* independently: Tab moves between them, arrows move within.
|
|
55
|
+
* independently: Tab moves between them, arrows move within. Seats in
|
|
56
|
+
* different sections are never neighbours (§4.3.5).
|
|
47
57
|
*/
|
|
48
58
|
export declare function buildNavigationGraph(section: RenderSection, options?: NavigationOptions): NavigationGraph;
|
|
49
59
|
//# sourceMappingURL=navigation-order.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-order.d.ts","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"navigation-order.d.ts","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,2BAA2B;IAC3B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAChD,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,CAwCjB"}
|
|
@@ -1,27 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Builds the navigation graph for one section. Sections are navigated
|
|
3
|
-
* independently: Tab moves between them, arrows move within.
|
|
3
|
+
* independently: Tab moves between them, arrows move within. Seats in
|
|
4
|
+
* different sections are never neighbours (§4.3.5).
|
|
4
5
|
*/
|
|
5
6
|
export function buildNavigationGraph(section, options = {}) {
|
|
6
|
-
const bands = section.layoutMode === 'freeform' ? freeformBands(section) : gridBands(section);
|
|
7
7
|
const included = options.includes;
|
|
8
|
-
const
|
|
9
|
-
.
|
|
10
|
-
.
|
|
8
|
+
const bands = section.rows
|
|
9
|
+
.flatMap(rowBands)
|
|
10
|
+
.map((band) => ({
|
|
11
|
+
seats: band.filter((s) => !included || included(s.id)),
|
|
12
|
+
ascending: runsRight(band) !== !!options.rtl,
|
|
13
|
+
}))
|
|
14
|
+
.filter((band) => band.seats.length > 0);
|
|
15
|
+
const downIsNext = runsDown(bands.map((b) => b.seats));
|
|
11
16
|
const neighbours = new Map();
|
|
12
17
|
const order = [];
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
bands.forEach(({ seats, ascending }, bandIndex) => {
|
|
19
|
+
const above = bands[downIsNext ? bandIndex - 1 : bandIndex + 1]?.seats;
|
|
20
|
+
const below = bands[downIsNext ? bandIndex + 1 : bandIndex - 1]?.seats;
|
|
21
|
+
seats.forEach((seat, i) => {
|
|
15
22
|
order.push(seat.id);
|
|
16
|
-
const prev =
|
|
17
|
-
const next =
|
|
23
|
+
const prev = seats[i - 1]?.id;
|
|
24
|
+
const next = seats[i + 1]?.id;
|
|
18
25
|
neighbours.set(seat.id, {
|
|
19
|
-
left:
|
|
20
|
-
right:
|
|
21
|
-
up: nearest(
|
|
22
|
-
down: nearest(
|
|
23
|
-
rowStart:
|
|
24
|
-
rowEnd:
|
|
26
|
+
left: ascending ? prev : next,
|
|
27
|
+
right: ascending ? next : prev,
|
|
28
|
+
up: nearest(above, seat),
|
|
29
|
+
down: nearest(below, seat),
|
|
30
|
+
rowStart: seats[0]?.id,
|
|
31
|
+
rowEnd: seats[seats.length - 1]?.id,
|
|
25
32
|
});
|
|
26
33
|
});
|
|
27
34
|
});
|
|
@@ -33,65 +40,73 @@ export function buildNavigationGraph(section, options = {}) {
|
|
|
33
40
|
};
|
|
34
41
|
}
|
|
35
42
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
43
|
+
* The bands one row contributes. A named row is one band, in the `col`-then-`x`
|
|
44
|
+
* order the render model already gave it. An empty row (§4.2.2) contributes
|
|
45
|
+
* nothing, so an arrow press never lands between two rows of seats.
|
|
38
46
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
47
|
+
* The seats with no `row` are not a row at all, so they are banded by `y`:
|
|
48
|
+
* sorted top to bottom and split wherever the vertical step is far larger than
|
|
49
|
+
* a typical one, which separates distinct arcs without assuming they are
|
|
50
|
+
* level. This is a heuristic; a document that wants exact reading order gives
|
|
51
|
+
* its seats `row` and `col`.
|
|
42
52
|
*/
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
.filter((row) => row.seats.length > 0)
|
|
46
|
-
.map((row) => row.seats.map((seat, i) => ({ id: seat.id, ordinal: seat.col ?? i })));
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Freeform sections: group by the seats' `row` labels when they have them,
|
|
50
|
-
* since §4.5 permits `row` as a pure label in freeform mode and an author who
|
|
51
|
-
* supplied one has told us how the seats group.
|
|
52
|
-
*
|
|
53
|
-
* With no row labels at all, seats are banded by `y`: sorted top to bottom and
|
|
54
|
-
* split wherever the vertical step exceeds the median step, which separates
|
|
55
|
-
* distinct arcs without assuming they are level. This is a heuristic; a
|
|
56
|
-
* document that wants exact reading order should carry `row` labels.
|
|
57
|
-
*/
|
|
58
|
-
function freeformBands(section) {
|
|
59
|
-
const toNav = (seats) => [...seats]
|
|
60
|
-
.sort((a, b) => (a.x ?? 0) - (b.x ?? 0))
|
|
61
|
-
.map((seat) => ({ id: seat.id, ordinal: seat.x ?? 0 }));
|
|
62
|
-
const hasRowLabels = section.seats.some((s) => s.row !== undefined && s.row !== '');
|
|
63
|
-
if (hasRowLabels) {
|
|
64
|
-
return section.rows.filter((row) => row.seats.length > 0).map((row) => toNav(row.seats));
|
|
65
|
-
}
|
|
66
|
-
const sorted = [...section.seats].sort((a, b) => (a.y ?? 0) - (b.y ?? 0));
|
|
67
|
-
if (sorted.length === 0) {
|
|
53
|
+
function rowBands(row) {
|
|
54
|
+
if (row.seats.length === 0) {
|
|
68
55
|
return [];
|
|
69
56
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
if (row.id !== '') {
|
|
58
|
+
return [[...row.seats]];
|
|
59
|
+
}
|
|
60
|
+
const sorted = [...row.seats].sort((a, b) => a.y - b.y);
|
|
61
|
+
// "Typical" is the 25th percentile rather than the median: the between-band
|
|
62
|
+
// jumps are themselves steps, and with few bands they drag the median up
|
|
63
|
+
// until no jump looks unusual any more. The floor keeps seats a fraction of
|
|
64
|
+
// a seat apart in one band.
|
|
65
|
+
const steps = sorted.slice(1).map((seat, i) => seat.y - sorted[i].y);
|
|
66
|
+
const threshold = Math.max(percentile(steps, 0.25) * 4, sorted[0].size / 4);
|
|
76
67
|
const bands = [[sorted[0]]];
|
|
77
68
|
for (let i = 1; i < sorted.length; i++) {
|
|
78
|
-
|
|
79
|
-
if (gap > threshold) {
|
|
69
|
+
if (sorted[i].y - sorted[i - 1].y > threshold) {
|
|
80
70
|
bands.push([]);
|
|
81
71
|
}
|
|
82
72
|
bands[bands.length - 1].push(sorted[i]);
|
|
83
73
|
}
|
|
84
|
-
return bands.map(
|
|
74
|
+
return bands.map((band) => band.sort((a, b) => a.x - b.x));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether a band's order runs left to right on the canvas, judged from its two
|
|
78
|
+
* ends. A band whose ends are level, such as a round table's, counts as left
|
|
79
|
+
* to right.
|
|
80
|
+
*/
|
|
81
|
+
function runsRight(band) {
|
|
82
|
+
const dx = band[band.length - 1].x - band[0].x;
|
|
83
|
+
return dx >= -tolerance(band[0]);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Whether the row order runs down the screen, judged from the first and last
|
|
87
|
+
* bands' mean `y`. Applied to the whole section, so up and down stay each
|
|
88
|
+
* other's inverse.
|
|
89
|
+
*/
|
|
90
|
+
function runsDown(bands) {
|
|
91
|
+
if (bands.length < 2) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
const meanY = (band) => band.reduce((sum, s) => sum + s.y, 0) / band.length;
|
|
95
|
+
return meanY(bands[bands.length - 1]) - meanY(bands[0]) >= -tolerance(bands[0][0]);
|
|
96
|
+
}
|
|
97
|
+
/** Below this, two positions count as level: a small fraction of a seat. */
|
|
98
|
+
function tolerance(seat) {
|
|
99
|
+
return seat.size * 1e-6;
|
|
85
100
|
}
|
|
86
|
-
/** The seat in `band`
|
|
101
|
+
/** The seat in `band` nearest `from` on the canvas — vertical movement. */
|
|
87
102
|
function nearest(band, from) {
|
|
88
103
|
if (!band || band.length === 0) {
|
|
89
104
|
return undefined;
|
|
90
105
|
}
|
|
91
106
|
let best = band[0];
|
|
92
|
-
let bestDistance = Math.
|
|
107
|
+
let bestDistance = Math.hypot(best.x - from.x, best.y - from.y);
|
|
93
108
|
for (const candidate of band.slice(1)) {
|
|
94
|
-
const distance = Math.
|
|
109
|
+
const distance = Math.hypot(candidate.x - from.x, candidate.y - from.y);
|
|
95
110
|
if (distance < bestDistance) {
|
|
96
111
|
best = candidate;
|
|
97
112
|
bestDistance = distance;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-order.js","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"navigation-order.js","sourceRoot":"","sources":["../../src/render/navigation-order.ts"],"names":[],"mappings":"AAyDA;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAsB,EACtB,UAA6B,EAAE;IAE/B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI;SACvB,OAAO,CAAC,QAAQ,CAAC;SACjB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACd,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtD,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG;KAC7C,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE3C,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC;QACvE,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC;QAEvE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACxB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAEpB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;YAC9B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE;gBACtB,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;gBAC7B,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;gBAC9B,EAAE,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;gBACxB,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;gBAC1B,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;gBACtB,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE;aACpC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK;QACL,UAAU;QACV,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACf,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,QAAQ,CAAC,GAAc;IAC9B,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAClB,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAExD,4EAA4E;IAC5E,yEAAyE;IACzE,4EAA4E;IAC5E,4BAA4B;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAE5E,MAAM,KAAK,GAAmB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,IAA2B;IAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,KAAyC;IACzD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,IAA2B,EAAE,EAAE,CAC5C,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACtD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,4EAA4E;AAC5E,SAAS,SAAS,CAAC,IAAgB;IACjC,OAAO,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AAC1B,CAAC;AAED,2EAA2E;AAC3E,SAAS,OAAO,CAAC,IAAuC,EAAE,IAAgB;IACxE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,IAAI,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,IAAI,QAAQ,GAAG,YAAY,EAAE,CAAC;YAC5B,IAAI,GAAG,SAAS,CAAC;YACjB,YAAY,GAAG,QAAQ,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,EAAE,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,MAAyB,EAAE,QAAgB;IAC7D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Accessibility, Element, Money, PriceTier, Seat, SeatType, Section } from '../kerusi/kerusi-map.model.js';
|
|
2
|
-
import { SectionLayoutMode } from '../kerusi/kerusi-layout-mode.js';
|
|
1
|
+
import { Accessibility, Bounds, Canvas, Element, Money, PriceTier, Seat, SeatType, Section } from '../kerusi/kerusi-map.model.js';
|
|
3
2
|
import { SeatStatus } from '../kerusi/kerusi-state.model.js';
|
|
4
3
|
/**
|
|
5
4
|
* The renderer's view of a Kerusi document pair: a `KerusiMap` with its
|
|
6
5
|
* `KerusiState` merged in, every reference resolved, every label localized, and
|
|
7
|
-
* every
|
|
6
|
+
* every size the document leaves to a default filled in. Positions stay in the
|
|
7
|
+
* map's canvas units (§4.5), exactly as the document gives them.
|
|
8
8
|
*
|
|
9
9
|
* This is deliberately not the source documents — a renderer should never have
|
|
10
10
|
* to re-resolve a price tier or re-read a locale map while drawing. It is also
|
|
@@ -26,14 +26,25 @@ export interface RenderSeat {
|
|
|
26
26
|
rowLabel?: string;
|
|
27
27
|
/** The row's position among the section's rows, for vertical navigation. */
|
|
28
28
|
rowIndex: number;
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* `Seat.col` — the adjacency ordinal within the row (§4.3.5). It says which
|
|
31
|
+
* seats are neighbours, never where a seat is drawn.
|
|
32
|
+
*/
|
|
30
33
|
col?: number;
|
|
31
|
-
/** `Seat.x
|
|
32
|
-
x
|
|
33
|
-
/** `Seat.y
|
|
34
|
-
y
|
|
35
|
-
/**
|
|
34
|
+
/** `Seat.x`: the centre, in canvas units. */
|
|
35
|
+
x: number;
|
|
36
|
+
/** `Seat.y`: the centre, in canvas units. */
|
|
37
|
+
y: number;
|
|
38
|
+
/** The side of the seat's square footprint, in canvas units: its section's seat size. */
|
|
39
|
+
size: number;
|
|
40
|
+
/** `Seat.rotation` in degrees, clockwise on screen about the centre. */
|
|
36
41
|
rotation?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Whether an aisle runs beside this seat: the next seat along its row skips
|
|
44
|
+
* a `col`, so the two are not neighbours (§4.3.5). Read from adjacency, never
|
|
45
|
+
* from the drawing.
|
|
46
|
+
*/
|
|
47
|
+
besideAisle: boolean;
|
|
37
48
|
/** The resolved legend entry, not merely its id (§4.7). */
|
|
38
49
|
type: SeatType;
|
|
39
50
|
/** `SeatType.label` localized, defaulting to the type id. */
|
|
@@ -63,13 +74,15 @@ export interface RenderElement {
|
|
|
63
74
|
/** `Element.kind`: "screen" | "stage" | "exit" | "lavatory" | "gap" | "label" | ... */
|
|
64
75
|
kind: string;
|
|
65
76
|
label?: string;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
width
|
|
72
|
-
|
|
77
|
+
/** `Element.x`: the centre, in canvas units. */
|
|
78
|
+
x: number;
|
|
79
|
+
/** `Element.y`: the centre, in canvas units. */
|
|
80
|
+
y: number;
|
|
81
|
+
/** Canvas units; the section's seat size when the document gives none (§4.4.1). */
|
|
82
|
+
width: number;
|
|
83
|
+
/** Canvas units; the section's seat size when the document gives none (§4.4.1). */
|
|
84
|
+
height: number;
|
|
85
|
+
/** Degrees, clockwise on screen about the centre. */
|
|
73
86
|
rotation?: number;
|
|
74
87
|
source: Element;
|
|
75
88
|
}
|
|
@@ -81,13 +94,12 @@ export interface RenderRow {
|
|
|
81
94
|
label?: string;
|
|
82
95
|
/** Position among the section's rows, after the §4.2.1 ordering. */
|
|
83
96
|
index: number;
|
|
84
|
-
/** The seats in this row, ordered by `col
|
|
97
|
+
/** The seats in this row, ordered by `col`, then by `x`. */
|
|
85
98
|
seats: readonly RenderSeat[];
|
|
86
99
|
/**
|
|
87
|
-
* An **empty row** (§4.2.2): declared by `Section.rows
|
|
88
|
-
* seat
|
|
89
|
-
*
|
|
90
|
-
* cinema screen and row A, or a cross-aisle mid-section.
|
|
100
|
+
* An **empty row** (§4.2.2): declared by `Section.rows` and referenced by no
|
|
101
|
+
* seat. It takes its place in the row order and reserves nothing: rows give
|
|
102
|
+
* order and adjacency, never space. Kept as information only.
|
|
91
103
|
*/
|
|
92
104
|
empty: boolean;
|
|
93
105
|
}
|
|
@@ -101,10 +113,16 @@ export interface RenderSection {
|
|
|
101
113
|
id: string;
|
|
102
114
|
/** `Section.label` localized; absent when the section declares none. */
|
|
103
115
|
label?: string;
|
|
104
|
-
/** The
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
|
|
116
|
+
/** The seat size that applies in this section (§4.5), resolved. */
|
|
117
|
+
seatSize: number;
|
|
118
|
+
/**
|
|
119
|
+
* The section's extent on the canvas: `Section.bounds` when declared, else
|
|
120
|
+
* the box around its seats' and elements' footprints. For zooming to a
|
|
121
|
+
* section and revealing a focused seat; never for placing anything.
|
|
122
|
+
*/
|
|
123
|
+
bounds: Bounds;
|
|
124
|
+
/** Whether `bounds` is the document's own `Section.bounds` rather than computed. */
|
|
125
|
+
boundsDeclared: boolean;
|
|
108
126
|
/** Rows in display order. Grouping only — seats do not live inside rows. */
|
|
109
127
|
rows: readonly RenderRow[];
|
|
110
128
|
/** Every seat in the section, flat, in row-then-position order. */
|
|
@@ -114,7 +132,7 @@ export interface RenderSection {
|
|
|
114
132
|
/**
|
|
115
133
|
* `Section.directions` localized (§4.10) — what an axis means in the physical
|
|
116
134
|
* world. Informational only: nothing in this library reads it to decide
|
|
117
|
-
*
|
|
135
|
+
* screen placement, exactly as nothing branches on `RenderMap.domain`.
|
|
118
136
|
*/
|
|
119
137
|
directions?: readonly RenderDirection[];
|
|
120
138
|
source: Section;
|
|
@@ -127,6 +145,10 @@ export interface RenderMap {
|
|
|
127
145
|
locale: string;
|
|
128
146
|
/** `KerusiMap.domain` — informational only; never used to reject or branch. */
|
|
129
147
|
domain?: string;
|
|
148
|
+
/** `KerusiMap.canvas`: the one drawing surface every section shares (§4.5). */
|
|
149
|
+
canvas: Canvas;
|
|
150
|
+
/** `KerusiMap.seatSize`: the map-wide seat size, before any section overrides it. */
|
|
151
|
+
seatSize: number;
|
|
130
152
|
/** Sections in `Section.index` order. */
|
|
131
153
|
sections: readonly RenderSection[];
|
|
132
154
|
/** The legend, with labels localized, for a key or legend component. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-model.d.ts","sourceRoot":"","sources":["../../src/render/render-model.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,OAAO,EACP,KAAK,EACL,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,OAAO,EACR,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"render-model.d.ts","sourceRoot":"","sources":["../../src/render/render-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,8 +31,8 @@ export declare function seatBodyPath(x: number, y: number, width: number, height
|
|
|
31
31
|
* The width of the frame around a selected seat's core — feed it straight to
|
|
32
32
|
* {@link seatBodyPath}'s `inset` to get the core.
|
|
33
33
|
*
|
|
34
|
-
* A seat is always square: `
|
|
35
|
-
* `size × size`,
|
|
34
|
+
* A seat is always square: `map-layout` places every one of them as
|
|
35
|
+
* `size × size`, every seat in a section the same size. That makes the inset
|
|
36
36
|
* shape an *exact* scaled copy of the body about its centre, not merely a
|
|
37
37
|
* similar-looking one, so any mark that clears the body also clears the core
|
|
38
38
|
* when it is drawn to the core's own box. That is what lets the frame width
|
|
@@ -62,7 +62,7 @@ export declare const seatRingStroke: typeof seatSelectedFrame;
|
|
|
62
62
|
* though, so pass the box the figure is meant to sit *on*: a selected seat
|
|
63
63
|
* draws it to the core's box, not the seat's, or the hips cross the frame.
|
|
64
64
|
*
|
|
65
|
-
* The ratios assume a square box, which is what `
|
|
65
|
+
* The ratios assume a square box, which is what `map-layout` always
|
|
66
66
|
* places. On a wide box the head, sized from the width, would outgrow the
|
|
67
67
|
* height.
|
|
68
68
|
*/
|
|
@@ -98,8 +98,8 @@ export function seatBodyPath(x, y, width, height, inset = 0) {
|
|
|
98
98
|
* The width of the frame around a selected seat's core — feed it straight to
|
|
99
99
|
* {@link seatBodyPath}'s `inset` to get the core.
|
|
100
100
|
*
|
|
101
|
-
* A seat is always square: `
|
|
102
|
-
* `size × size`,
|
|
101
|
+
* A seat is always square: `map-layout` places every one of them as
|
|
102
|
+
* `size × size`, every seat in a section the same size. That makes the inset
|
|
103
103
|
* shape an *exact* scaled copy of the body about its centre, not merely a
|
|
104
104
|
* similar-looking one, so any mark that clears the body also clears the core
|
|
105
105
|
* when it is drawn to the core's own box. That is what lets the frame width
|
|
@@ -131,7 +131,7 @@ export const seatRingStroke = seatSelectedFrame;
|
|
|
131
131
|
* though, so pass the box the figure is meant to sit *on*: a selected seat
|
|
132
132
|
* draws it to the core's box, not the seat's, or the hips cross the frame.
|
|
133
133
|
*
|
|
134
|
-
* The ratios assume a square box, which is what `
|
|
134
|
+
* The ratios assume a square box, which is what `map-layout` always
|
|
135
135
|
* places. On a wide box the head, sized from the width, would outgrow the
|
|
136
136
|
* height.
|
|
137
137
|
*/
|
package/dist/view/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Presentation policy that is still framework-free: the colour system a
|
|
3
3
|
* renderer resolves seat fills from, the ARIA strings it announces, the
|
|
4
|
-
* selection rules that decide whether a seat may be toggled,
|
|
5
|
-
*
|
|
4
|
+
* selection rules that decide whether a seat may be toggled, the zoom, pan and
|
|
5
|
+
* gesture arithmetic behind a zoomable map, and the hover rules and card
|
|
6
|
+
* placement behind seat events and seat cards.
|
|
6
7
|
*
|
|
7
8
|
* A binding turns these into markup; none of them know how.
|
|
8
9
|
*/
|
|
@@ -12,6 +13,9 @@ export * from './kerusi-seatmap-colors.js';
|
|
|
12
13
|
export * from './rtl.js';
|
|
13
14
|
export * from './seat-glyph.js';
|
|
14
15
|
export * from './seat-aria.js';
|
|
16
|
+
export * from './seat-card.js';
|
|
17
|
+
export * from './seat-card-placement.js';
|
|
18
|
+
export * from './seat-hover.js';
|
|
15
19
|
export * from './selection.js';
|
|
16
20
|
export * from './viewport.js';
|
|
17
21
|
export * from './viewport-gesture.js';
|
package/dist/view/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/view/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/view/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC"}
|
package/dist/view/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Presentation policy that is still framework-free: the colour system a
|
|
3
3
|
* renderer resolves seat fills from, the ARIA strings it announces, the
|
|
4
|
-
* selection rules that decide whether a seat may be toggled,
|
|
5
|
-
*
|
|
4
|
+
* selection rules that decide whether a seat may be toggled, the zoom, pan and
|
|
5
|
+
* gesture arithmetic behind a zoomable map, and the hover rules and card
|
|
6
|
+
* placement behind seat events and seat cards.
|
|
6
7
|
*
|
|
7
8
|
* A binding turns these into markup; none of them know how.
|
|
8
9
|
*/
|
|
@@ -12,6 +13,9 @@ export * from './kerusi-seatmap-colors.js';
|
|
|
12
13
|
export * from './rtl.js';
|
|
13
14
|
export * from './seat-glyph.js';
|
|
14
15
|
export * from './seat-aria.js';
|
|
16
|
+
export * from './seat-card.js';
|
|
17
|
+
export * from './seat-card-placement.js';
|
|
18
|
+
export * from './seat-hover.js';
|
|
15
19
|
export * from './selection.js';
|
|
16
20
|
export * from './viewport.js';
|
|
17
21
|
export * from './viewport-gesture.js';
|
package/dist/view/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/view/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/view/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC"}
|
package/dist/view/rtl.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Which locales are written right-to-left.
|
|
3
3
|
*
|
|
4
|
-
* A binding needs this to decide whether to mirror
|
|
4
|
+
* A binding needs this to decide whether to mirror the whole canvas and swap the
|
|
5
5
|
* left/right arrow keys, and it would be written identically in every one —
|
|
6
6
|
* so it belongs here rather than in any of them.
|
|
7
7
|
*/
|