@seatlayer/core 0.24.0 → 0.25.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/dist/{de-EIG65UFU.js → de-73TIXYJH.js} +2 -1
- package/dist/{de-EIG65UFU.js.map → de-73TIXYJH.js.map} +1 -1
- package/dist/{es-47HFAWS6.js → es-SKAODLTR.js} +2 -1
- package/dist/{es-47HFAWS6.js.map → es-SKAODLTR.js.map} +1 -1
- package/dist/{fr-7IXNNFBS.js → fr-J4637T6V.js} +2 -1
- package/dist/{fr-7IXNNFBS.js.map → fr-J4637T6V.js.map} +1 -1
- package/dist/index.cjs +476 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +136 -1
- package/dist/index.d.ts +136 -1
- package/dist/index.js +474 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65,6 +65,7 @@ var init_es = __esm({
|
|
|
65
65
|
"picker.seatsLeftInSection.one": "{count} asiento disponible",
|
|
66
66
|
"picker.seatsLeftInSection.other": "{count} asientos disponibles",
|
|
67
67
|
"picker.overview": "Descripci\xF3n general",
|
|
68
|
+
"picker.entrance": "Entrada",
|
|
68
69
|
"picker.tapSeatHint": "Toca cualquier asiento para ver su vista",
|
|
69
70
|
"picker.ticketTierFor": "Categor\xEDa de entrada para {label}",
|
|
70
71
|
"picker.viewFromSeat": "Vista desde el asiento {label}",
|
|
@@ -123,6 +124,7 @@ var init_de = __esm({
|
|
|
123
124
|
"picker.seatsLeftInSection.one": "{count} Platz verf\xFCgbar",
|
|
124
125
|
"picker.seatsLeftInSection.other": "{count} Pl\xE4tze verf\xFCgbar",
|
|
125
126
|
"picker.overview": "\xDCbersicht",
|
|
127
|
+
"picker.entrance": "Eingang",
|
|
126
128
|
"picker.tapSeatHint": "Tippen Sie auf einen Platz, um die Ansicht zu pr\xFCfen",
|
|
127
129
|
"picker.ticketTierFor": "Ticketklasse f\xFCr {label}",
|
|
128
130
|
"picker.viewFromSeat": "Ansicht von Platz {label}",
|
|
@@ -181,6 +183,7 @@ var init_fr = __esm({
|
|
|
181
183
|
"picker.seatsLeftInSection.one": "{count} si\xE8ge restant",
|
|
182
184
|
"picker.seatsLeftInSection.other": "{count} si\xE8ges restants",
|
|
183
185
|
"picker.overview": "Vue d'ensemble",
|
|
186
|
+
"picker.entrance": "Entr\xE9e",
|
|
184
187
|
"picker.tapSeatHint": "Appuyez sur un si\xE8ge pour v\xE9rifier sa vue",
|
|
185
188
|
"picker.ticketTierFor": "Cat\xE9gorie de billet pour {label}",
|
|
186
189
|
"picker.viewFromSeat": "Vue depuis le si\xE8ge {label}",
|
|
@@ -211,6 +214,7 @@ __export(index_exports, {
|
|
|
211
214
|
MAX_EVENT_INVENTORY: () => MAX_EVENT_INVENTORY,
|
|
212
215
|
MAX_GA_CAPACITY: () => MAX_GA_CAPACITY,
|
|
213
216
|
PickerController: () => PickerController,
|
|
217
|
+
RENDERED_QUALITY_REPORT_VERSION: () => RENDERED_QUALITY_REPORT_VERSION,
|
|
214
218
|
SUPPORTED_LOCALES: () => SUPPORTED_LOCALES,
|
|
215
219
|
SeatmapRenderer: () => SeatmapRenderer,
|
|
216
220
|
UNGROUPED_ID: () => UNGROUPED_ID,
|
|
@@ -238,6 +242,7 @@ __export(index_exports, {
|
|
|
238
242
|
generateSeatThumb: () => generateSeatThumb,
|
|
239
243
|
getLocale: () => getLocale,
|
|
240
244
|
hiddenObjectIds: () => hiddenObjectIds,
|
|
245
|
+
inspectRenderedQualityEvidence: () => inspectRenderedQualityEvidence,
|
|
241
246
|
isGaUnitLabel: () => isGaUnitLabel,
|
|
242
247
|
isSectionHidden: () => isSectionHidden,
|
|
243
248
|
layerOf: () => layerOf,
|
|
@@ -983,20 +988,6 @@ function applyHidden(doc, hidden) {
|
|
|
983
988
|
return { ...doc, objects };
|
|
984
989
|
}
|
|
985
990
|
|
|
986
|
-
// src/engine/SeatmapRenderer.ts
|
|
987
|
-
var import_Core = require("konva/lib/Core");
|
|
988
|
-
var import_Stage = require("konva/lib/Stage");
|
|
989
|
-
var import_Layer = require("konva/lib/Layer");
|
|
990
|
-
var import_Group = require("konva/lib/Group");
|
|
991
|
-
var import_Circle = require("konva/lib/shapes/Circle");
|
|
992
|
-
var import_Rect = require("konva/lib/shapes/Rect");
|
|
993
|
-
var import_Ellipse = require("konva/lib/shapes/Ellipse");
|
|
994
|
-
var import_Line = require("konva/lib/shapes/Line");
|
|
995
|
-
var import_Text = require("konva/lib/shapes/Text");
|
|
996
|
-
var import_Path = require("konva/lib/shapes/Path");
|
|
997
|
-
var import_Image = require("konva/lib/shapes/Image");
|
|
998
|
-
var import_Shape = require("konva/lib/Shape");
|
|
999
|
-
|
|
1000
991
|
// src/core/chartRenderRules.ts
|
|
1001
992
|
var SEAT_LABEL_FONT_SIZE = 7;
|
|
1002
993
|
var BOOTH_LABEL_FONT_SIZE = 10;
|
|
@@ -1053,6 +1044,470 @@ function stateAwareBookableLabelInk(fill, preferred) {
|
|
|
1053
1044
|
return darkContrast >= lightContrast ? DARK_BOOKABLE_LABEL_INK : LIGHT_BOOKABLE_LABEL_INK;
|
|
1054
1045
|
}
|
|
1055
1046
|
|
|
1047
|
+
// src/core/renderedQuality.ts
|
|
1048
|
+
var RENDERED_QUALITY_REPORT_VERSION = 3;
|
|
1049
|
+
var MIN_TEXT_CONTRAST = 4.5;
|
|
1050
|
+
var MIN_GRAPHICAL_CONTRAST = 3;
|
|
1051
|
+
var MIN_POINTER_TARGET_PX = 24;
|
|
1052
|
+
var MAX_SAMPLES_PER_FINDING = 20;
|
|
1053
|
+
function visibleWithBox(items) {
|
|
1054
|
+
return items.filter((item) => item.visible && Boolean(item.screenBox));
|
|
1055
|
+
}
|
|
1056
|
+
function intersects(left, right) {
|
|
1057
|
+
return left.x < right.x + right.width && left.x + left.width > right.x && left.y < right.y + right.height && left.y + left.height > right.y;
|
|
1058
|
+
}
|
|
1059
|
+
function minimum(values) {
|
|
1060
|
+
return values.length ? Math.round(Math.min(...values) * 100) / 100 : null;
|
|
1061
|
+
}
|
|
1062
|
+
function finding(code, message, samples) {
|
|
1063
|
+
if (!samples.length) return null;
|
|
1064
|
+
return {
|
|
1065
|
+
code,
|
|
1066
|
+
message,
|
|
1067
|
+
count: samples.length,
|
|
1068
|
+
samples: samples.slice(0, MAX_SAMPLES_PER_FINDING)
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
function labelSample(label, measured, minimumValue) {
|
|
1072
|
+
return {
|
|
1073
|
+
primaryId: label.seatId,
|
|
1074
|
+
primaryLabel: label.label,
|
|
1075
|
+
...measured == null ? {} : { measured: Math.round(measured * 100) / 100 },
|
|
1076
|
+
...minimumValue == null ? {} : { minimum: minimumValue }
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function hierarchySample(label, measured, minimumValue) {
|
|
1080
|
+
return {
|
|
1081
|
+
primaryId: label.id,
|
|
1082
|
+
primaryLabel: label.label,
|
|
1083
|
+
...measured == null ? {} : { measured: Math.round(measured * 100) / 100 },
|
|
1084
|
+
...minimumValue == null ? {} : { minimum: minimumValue }
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
function freeTextSample(label, measured, minimumValue) {
|
|
1088
|
+
return {
|
|
1089
|
+
primaryId: label.objectId,
|
|
1090
|
+
primaryLabel: label.text,
|
|
1091
|
+
...measured == null ? {} : { measured: Math.round(measured * 100) / 100 },
|
|
1092
|
+
...minimumValue == null ? {} : { minimum: minimumValue }
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
function collisionSample(primaryId, primaryLabel, secondaryId, secondaryLabel) {
|
|
1096
|
+
return { primaryId, primaryLabel, secondaryId, secondaryLabel };
|
|
1097
|
+
}
|
|
1098
|
+
function inspectRenderedQualityEvidence(evidence, state = "overview", targetCategoryKey = null) {
|
|
1099
|
+
const bookable = visibleWithBox(evidence.labels);
|
|
1100
|
+
const hierarchy = visibleWithBox(evidence.hierarchyLabels);
|
|
1101
|
+
const freeText = visibleWithBox(evidence.freeTextLabels);
|
|
1102
|
+
const visibleGA = evidence.gaAreas.filter((area) => area.visible);
|
|
1103
|
+
const bookableContrast = bookable.map((label) => renderedTextContrast(label.ink, label.fill) ?? 0);
|
|
1104
|
+
const hierarchyContrast = hierarchy.map((label) => renderedTextContrast(label.ink, label.fill) ?? 0);
|
|
1105
|
+
const freeTextContrast = freeText.map((label) => renderedTextContrast(label.ink, label.background) ?? 0);
|
|
1106
|
+
const gaContrast = visibleGA.map((area) => renderedTextContrast(area.effectiveBackground, evidence.canvasBackground) ?? 0);
|
|
1107
|
+
const targetLabels = targetCategoryKey ? evidence.labels.filter((label) => label.categoryKey === targetCategoryKey) : evidence.labels;
|
|
1108
|
+
const targetGAAreas = targetCategoryKey ? evidence.gaAreas.filter((area) => area.categoryKey === targetCategoryKey) : evidence.gaAreas;
|
|
1109
|
+
const selected = targetLabels.filter((label) => label.selected);
|
|
1110
|
+
const held = targetLabels.filter((label) => label.status === "held");
|
|
1111
|
+
const booked = targetLabels.filter((label) => label.status === "booked");
|
|
1112
|
+
const activePointerTargets = targetLabels.filter((label) => label.pointerTarget.active);
|
|
1113
|
+
const activeCategoryKeys = /* @__PURE__ */ new Set([
|
|
1114
|
+
...evidence.labels.filter((label) => label.visible && (label.opacity > 0.25 || label.selected || label.status !== "free")).map((label) => label.categoryKey),
|
|
1115
|
+
...evidence.gaAreas.filter((area) => area.visible && area.opacity > 0.1).map((area) => area.categoryKey)
|
|
1116
|
+
]);
|
|
1117
|
+
const selectedRingContrast = selected.length ? minimum(selected.flatMap((label) => [
|
|
1118
|
+
renderedTextContrast(evidence.selectionRingColor, evidence.canvasBackground) ?? 0,
|
|
1119
|
+
renderedTextContrast(evidence.selectionRingColor, label.fill) ?? 0
|
|
1120
|
+
])) : null;
|
|
1121
|
+
const findings = [];
|
|
1122
|
+
const overviewCountSamples = (count, label) => Array.from({ length: count }, (_, index) => ({
|
|
1123
|
+
primaryId: `overview:${index + 1}`,
|
|
1124
|
+
primaryLabel: label
|
|
1125
|
+
}));
|
|
1126
|
+
if (state === "overview" && evidence.rung === "sections") {
|
|
1127
|
+
findings.push(finding(
|
|
1128
|
+
"overview-section-category-paint",
|
|
1129
|
+
"Section overview shells must use the neutral hierarchy palette, not category paint",
|
|
1130
|
+
overviewCountSamples(evidence.overviewStyle.categoryPaintedSectionShells, "category-painted section shell")
|
|
1131
|
+
));
|
|
1132
|
+
findings.push(finding(
|
|
1133
|
+
"overview-category-detail-visible",
|
|
1134
|
+
"Category-tinted section detail must wait until section focus or seat zoom",
|
|
1135
|
+
overviewCountSamples(evidence.overviewStyle.visibleCategoryDetailOutlines, "category detail outline")
|
|
1136
|
+
));
|
|
1137
|
+
findings.push(finding(
|
|
1138
|
+
"overview-row-hints-visible",
|
|
1139
|
+
"Row and seat patterns must not clutter the section overview",
|
|
1140
|
+
overviewCountSamples(evidence.overviewStyle.visibleSectionRowHints, "section row hint")
|
|
1141
|
+
));
|
|
1142
|
+
findings.push(finding(
|
|
1143
|
+
"overview-availability-clutter",
|
|
1144
|
+
"Live availability counts belong in focused detail, not on section overview shells",
|
|
1145
|
+
overviewCountSamples(evidence.overviewStyle.visibleSectionAvailabilityLabels, "section availability label")
|
|
1146
|
+
));
|
|
1147
|
+
findings.push(finding(
|
|
1148
|
+
"overview-ga-detail-visible",
|
|
1149
|
+
"Section-contained standing paint belongs in focused detail, not on section overview shells",
|
|
1150
|
+
overviewCountSamples(evidence.overviewStyle.visibleSectionGADetails, "section-contained GA detail")
|
|
1151
|
+
));
|
|
1152
|
+
}
|
|
1153
|
+
findings.push(finding(
|
|
1154
|
+
"bookable-label-undersized",
|
|
1155
|
+
"Visible bookable labels must meet the rendered small-text size floor",
|
|
1156
|
+
bookable.filter((label) => label.renderedFontPx < evidence.minimumVisibleLabelPx).map((label) => labelSample(label, label.renderedFontPx, evidence.minimumVisibleLabelPx))
|
|
1157
|
+
));
|
|
1158
|
+
if (state === "interaction") {
|
|
1159
|
+
const labelledInventory = targetLabels.length > 0;
|
|
1160
|
+
const gaInventory = targetGAAreas.length > 0;
|
|
1161
|
+
const detailInventory = labelledInventory || gaInventory;
|
|
1162
|
+
const visibleTargetGA = targetGAAreas.filter((area) => area.visible);
|
|
1163
|
+
const sections = /* @__PURE__ */ new Set([
|
|
1164
|
+
...targetLabels.flatMap((label) => label.sectionId ? [label.sectionId] : []),
|
|
1165
|
+
...targetGAAreas.flatMap((area) => area.sectionId ? [area.sectionId] : [])
|
|
1166
|
+
]);
|
|
1167
|
+
const categories = /* @__PURE__ */ new Set([
|
|
1168
|
+
...evidence.labels.map((label) => label.categoryKey),
|
|
1169
|
+
...evidence.gaAreas.map((area) => area.categoryKey)
|
|
1170
|
+
]);
|
|
1171
|
+
const inViewport = (label) => label.screenCenter.x >= 0 && label.screenCenter.x <= evidence.viewport.width && label.screenCenter.y >= 0 && label.screenCenter.y <= evidence.viewport.height;
|
|
1172
|
+
findings.push(finding(
|
|
1173
|
+
"detail-rung-missing",
|
|
1174
|
+
"Interaction evidence with bookable inventory must render the seat-detail rung",
|
|
1175
|
+
detailInventory && evidence.rung !== "seats" ? [{ primaryId: "renderer", primaryLabel: evidence.rung }] : []
|
|
1176
|
+
));
|
|
1177
|
+
findings.push(finding(
|
|
1178
|
+
"detail-inventory-not-visible",
|
|
1179
|
+
"Interaction evidence must frame at least one real bookable unit",
|
|
1180
|
+
detailInventory && !targetLabels.some(inViewport) && !visibleTargetGA.length ? [{ primaryId: "renderer", primaryLabel: "no target inventory in viewport" }] : []
|
|
1181
|
+
));
|
|
1182
|
+
findings.push(finding(
|
|
1183
|
+
"pointer-target-inactive",
|
|
1184
|
+
"Interaction evidence must expose a live production pointer target",
|
|
1185
|
+
labelledInventory && !activePointerTargets.some(inViewport) || !labelledInventory && gaInventory && !visibleTargetGA.some((area) => area.interactive) ? [{ primaryId: "renderer", primaryLabel: "no active pointer target in viewport" }] : []
|
|
1186
|
+
));
|
|
1187
|
+
findings.push(finding(
|
|
1188
|
+
"pointer-target-undersized",
|
|
1189
|
+
"Every active production pointer target must reach at least 24 CSS pixels",
|
|
1190
|
+
activePointerTargets.filter((label) => inViewport(label) && label.pointerTarget.effectiveMinimumPx < MIN_POINTER_TARGET_PX).map((label) => labelSample(label, label.pointerTarget.effectiveMinimumPx, MIN_POINTER_TARGET_PX))
|
|
1191
|
+
));
|
|
1192
|
+
findings.push(finding(
|
|
1193
|
+
"selected-state-missing",
|
|
1194
|
+
"Interaction evidence must paint a selected unit and its renderer-owned ring",
|
|
1195
|
+
labelledInventory && (!selected.length || !selected.some((label) => evidence.selectionRingSeatIds.includes(label.seatId))) ? [{ primaryId: "renderer", primaryLabel: "selected state" }] : []
|
|
1196
|
+
));
|
|
1197
|
+
findings.push(finding(
|
|
1198
|
+
"selected-state-contrast-low",
|
|
1199
|
+
"The selected-state ring must maintain 3:1 graphical contrast with the canvas",
|
|
1200
|
+
selected.length && (selectedRingContrast ?? 0) < MIN_GRAPHICAL_CONTRAST ? [{
|
|
1201
|
+
primaryId: selected[0].seatId,
|
|
1202
|
+
primaryLabel: selected[0].label,
|
|
1203
|
+
measured: Math.round((selectedRingContrast ?? 0) * 100) / 100,
|
|
1204
|
+
minimum: MIN_GRAPHICAL_CONTRAST
|
|
1205
|
+
}] : []
|
|
1206
|
+
));
|
|
1207
|
+
findings.push(finding(
|
|
1208
|
+
"held-state-missing",
|
|
1209
|
+
"Interaction evidence must paint a held state when the floor has at least two status-managed units",
|
|
1210
|
+
targetLabels.length >= 2 && !held.length ? [{ primaryId: "renderer", primaryLabel: "held state" }] : []
|
|
1211
|
+
));
|
|
1212
|
+
findings.push(finding(
|
|
1213
|
+
"booked-state-missing",
|
|
1214
|
+
"Interaction evidence must paint a taken state when the floor has at least three status-managed units",
|
|
1215
|
+
targetLabels.length >= 3 && !booked.length ? [{ primaryId: "renderer", primaryLabel: "booked state" }] : []
|
|
1216
|
+
));
|
|
1217
|
+
const heldSignatures = new Set(held.map((label) => `${label.fill.toLowerCase()}:${label.opacity}`));
|
|
1218
|
+
const bookedSignatures = new Set(booked.map((label) => `${label.fill.toLowerCase()}:${label.opacity}`));
|
|
1219
|
+
findings.push(finding(
|
|
1220
|
+
"status-state-indistinct",
|
|
1221
|
+
"Held and taken evidence must resolve to distinct renderer paint",
|
|
1222
|
+
held.length && booked.length && [...heldSignatures].some((signature) => bookedSignatures.has(signature)) ? [{ primaryId: held[0].seatId, primaryLabel: held[0].label, secondaryId: booked[0].seatId, secondaryLabel: booked[0].label }] : []
|
|
1223
|
+
));
|
|
1224
|
+
findings.push(finding(
|
|
1225
|
+
"section-focus-missing",
|
|
1226
|
+
"Interaction evidence must exercise section focus and its backdrop when section membership exists",
|
|
1227
|
+
sections.size && (!evidence.focusedSectionId || !evidence.focusBackdropVisible) ? [{ primaryId: "renderer", primaryLabel: "section focus" }] : []
|
|
1228
|
+
));
|
|
1229
|
+
findings.push(finding(
|
|
1230
|
+
"category-filter-missing",
|
|
1231
|
+
"Interaction evidence must exercise a category filter when multiple categories exist",
|
|
1232
|
+
categories.size >= 2 && (!evidence.categoryFilterKeys || !evidence.categoryFilterKeys.length) ? [{ primaryId: "renderer", primaryLabel: "category filter" }] : []
|
|
1233
|
+
));
|
|
1234
|
+
const excludedFree = evidence.labels.filter((label) => label.status === "free" && !label.selected && evidence.categoryFilterKeys != null && !evidence.categoryFilterKeys.includes(label.categoryKey));
|
|
1235
|
+
const excludedGA = evidence.gaAreas.filter((area) => evidence.categoryFilterKeys != null && !evidence.categoryFilterKeys.includes(area.categoryKey));
|
|
1236
|
+
findings.push(finding(
|
|
1237
|
+
"category-filter-ineffective",
|
|
1238
|
+
"The active category filter must visibly dim excluded free inventory",
|
|
1239
|
+
(excludedFree.length || excludedGA.length) && !excludedFree.some((label) => label.opacity <= 0.25) && !excludedGA.some((area) => area.opacity <= 0.1) ? [
|
|
1240
|
+
...excludedFree.map((label) => labelSample(label, label.opacity)),
|
|
1241
|
+
...excludedGA.map((area) => ({ primaryId: area.areaId, primaryLabel: area.label, measured: area.opacity }))
|
|
1242
|
+
] : []
|
|
1243
|
+
));
|
|
1244
|
+
findings.push(finding(
|
|
1245
|
+
"target-category-not-visible",
|
|
1246
|
+
"A category-specific interaction scene must visibly paint its exact target category",
|
|
1247
|
+
targetCategoryKey && !activeCategoryKeys.has(targetCategoryKey) ? [{ primaryId: targetCategoryKey, primaryLabel: targetCategoryKey }] : []
|
|
1248
|
+
));
|
|
1249
|
+
findings.push(finding(
|
|
1250
|
+
"target-category-filter-mismatch",
|
|
1251
|
+
"A category-specific interaction scene must bind its filter to only the exact target category",
|
|
1252
|
+
targetCategoryKey && (evidence.categoryFilterKeys?.length !== 1 || evidence.categoryFilterKeys[0] !== targetCategoryKey) ? [{ primaryId: targetCategoryKey, primaryLabel: targetCategoryKey }] : []
|
|
1253
|
+
));
|
|
1254
|
+
}
|
|
1255
|
+
findings.push(finding(
|
|
1256
|
+
"bookable-label-contrast-low",
|
|
1257
|
+
"Visible bookable labels must meet 4.5:1 contrast against their actual paint",
|
|
1258
|
+
bookable.flatMap((label) => {
|
|
1259
|
+
const ratio = renderedTextContrast(label.ink, label.fill) ?? 0;
|
|
1260
|
+
return ratio < MIN_TEXT_CONTRAST ? [labelSample(label, ratio, MIN_TEXT_CONTRAST)] : [];
|
|
1261
|
+
})
|
|
1262
|
+
));
|
|
1263
|
+
const bookableCollisions = [];
|
|
1264
|
+
for (let index = 0; index < bookable.length; index += 1) {
|
|
1265
|
+
for (let other = 0; other < index; other += 1) {
|
|
1266
|
+
if (intersects(bookable[index].screenBox, bookable[other].screenBox)) {
|
|
1267
|
+
bookableCollisions.push(collisionSample(
|
|
1268
|
+
bookable[other].seatId,
|
|
1269
|
+
bookable[other].label,
|
|
1270
|
+
bookable[index].seatId,
|
|
1271
|
+
bookable[index].label
|
|
1272
|
+
));
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
findings.push(finding(
|
|
1277
|
+
"bookable-label-collision",
|
|
1278
|
+
"Visible bookable labels must not overlap",
|
|
1279
|
+
bookableCollisions
|
|
1280
|
+
));
|
|
1281
|
+
findings.push(finding(
|
|
1282
|
+
"hierarchy-label-undersized",
|
|
1283
|
+
"Visible section and zone labels must meet the rendered small-text size floor",
|
|
1284
|
+
hierarchy.filter((label) => label.renderedFontPx < MIN_VISIBLE_BOOKABLE_LABEL_PX).map((label) => hierarchySample(label, label.renderedFontPx, MIN_VISIBLE_BOOKABLE_LABEL_PX))
|
|
1285
|
+
));
|
|
1286
|
+
findings.push(finding(
|
|
1287
|
+
"hierarchy-label-contrast-low",
|
|
1288
|
+
"Visible section and zone labels must meet 4.5:1 contrast against their backing paint",
|
|
1289
|
+
hierarchy.flatMap((label) => {
|
|
1290
|
+
const ratio = renderedTextContrast(label.ink, label.fill) ?? 0;
|
|
1291
|
+
return ratio < MIN_TEXT_CONTRAST ? [hierarchySample(label, ratio, MIN_TEXT_CONTRAST)] : [];
|
|
1292
|
+
})
|
|
1293
|
+
));
|
|
1294
|
+
findings.push(finding(
|
|
1295
|
+
"hierarchy-label-outside-section",
|
|
1296
|
+
"Visible section labels must remain inside the filled section surface and outside holes",
|
|
1297
|
+
hierarchy.filter((label) => label.kind === "section" && label.fitsContainer === false).map((label) => hierarchySample(label))
|
|
1298
|
+
));
|
|
1299
|
+
const hierarchyCollisions = [];
|
|
1300
|
+
for (let index = 0; index < hierarchy.length; index += 1) {
|
|
1301
|
+
for (let other = 0; other < index; other += 1) {
|
|
1302
|
+
if (intersects(hierarchy[index].screenBox, hierarchy[other].screenBox)) {
|
|
1303
|
+
hierarchyCollisions.push(collisionSample(
|
|
1304
|
+
hierarchy[other].id,
|
|
1305
|
+
hierarchy[other].label,
|
|
1306
|
+
hierarchy[index].id,
|
|
1307
|
+
hierarchy[index].label
|
|
1308
|
+
));
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
findings.push(finding(
|
|
1313
|
+
"hierarchy-label-collision",
|
|
1314
|
+
"Visible hierarchy labels must not overlap each other",
|
|
1315
|
+
hierarchyCollisions
|
|
1316
|
+
));
|
|
1317
|
+
const hierarchyBookableCollisions = [];
|
|
1318
|
+
for (const upper of hierarchy) {
|
|
1319
|
+
for (const unit of bookable) {
|
|
1320
|
+
if (intersects(upper.screenBox, unit.screenBox)) {
|
|
1321
|
+
hierarchyBookableCollisions.push(collisionSample(upper.id, upper.label, unit.seatId, unit.label));
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
findings.push(finding(
|
|
1326
|
+
"hierarchy-bookable-collision",
|
|
1327
|
+
"Visible hierarchy labels must not overlap bookable labels",
|
|
1328
|
+
hierarchyBookableCollisions
|
|
1329
|
+
));
|
|
1330
|
+
findings.push(finding(
|
|
1331
|
+
"free-text-undersized",
|
|
1332
|
+
"Visible chart text must meet the rendered small-text size floor",
|
|
1333
|
+
freeText.filter((label) => label.renderedFontPx < evidence.minimumVisibleLabelPx).map((label) => freeTextSample(label, label.renderedFontPx, evidence.minimumVisibleLabelPx))
|
|
1334
|
+
));
|
|
1335
|
+
findings.push(finding(
|
|
1336
|
+
"free-text-contrast-low",
|
|
1337
|
+
"Visible chart text must meet 4.5:1 contrast against its measured background",
|
|
1338
|
+
freeText.flatMap((label) => {
|
|
1339
|
+
const ratio = renderedTextContrast(label.ink, label.background) ?? 0;
|
|
1340
|
+
return ratio < MIN_TEXT_CONTRAST ? [freeTextSample(label, ratio, MIN_TEXT_CONTRAST)] : [];
|
|
1341
|
+
})
|
|
1342
|
+
));
|
|
1343
|
+
const freeTextCollisions = [];
|
|
1344
|
+
for (let index = 0; index < freeText.length; index += 1) {
|
|
1345
|
+
for (let other = 0; other < index; other += 1) {
|
|
1346
|
+
if (intersects(freeText[index].screenBox, freeText[other].screenBox)) {
|
|
1347
|
+
freeTextCollisions.push(collisionSample(
|
|
1348
|
+
freeText[other].objectId,
|
|
1349
|
+
freeText[other].text,
|
|
1350
|
+
freeText[index].objectId,
|
|
1351
|
+
freeText[index].text
|
|
1352
|
+
));
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
findings.push(finding(
|
|
1357
|
+
"free-text-collision",
|
|
1358
|
+
"Visible chart text labels must not overlap each other",
|
|
1359
|
+
freeTextCollisions
|
|
1360
|
+
));
|
|
1361
|
+
const freeTextBookableCollisions = [];
|
|
1362
|
+
for (const text of freeText) {
|
|
1363
|
+
for (const unit of bookable) {
|
|
1364
|
+
if (intersects(text.screenBox, unit.screenBox)) {
|
|
1365
|
+
freeTextBookableCollisions.push(collisionSample(text.objectId, text.text, unit.seatId, unit.label));
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
findings.push(finding(
|
|
1370
|
+
"free-text-bookable-collision",
|
|
1371
|
+
"Visible chart text must not overlap bookable labels",
|
|
1372
|
+
freeTextBookableCollisions
|
|
1373
|
+
));
|
|
1374
|
+
const freeTextHierarchyCollisions = [];
|
|
1375
|
+
for (const text of freeText) {
|
|
1376
|
+
for (const upper of hierarchy) {
|
|
1377
|
+
if (intersects(text.screenBox, upper.screenBox)) {
|
|
1378
|
+
freeTextHierarchyCollisions.push(collisionSample(text.objectId, text.text, upper.id, upper.label));
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
findings.push(finding(
|
|
1383
|
+
"free-text-hierarchy-collision",
|
|
1384
|
+
"Visible chart text must not overlap hierarchy labels",
|
|
1385
|
+
freeTextHierarchyCollisions
|
|
1386
|
+
));
|
|
1387
|
+
findings.push(finding(
|
|
1388
|
+
"ga-contrast-low",
|
|
1389
|
+
"Visible GA surfaces must maintain 3:1 graphical contrast with the canvas",
|
|
1390
|
+
visibleGA.flatMap((area) => {
|
|
1391
|
+
const ratio = renderedTextContrast(area.effectiveBackground, evidence.canvasBackground) ?? 0;
|
|
1392
|
+
return ratio < MIN_GRAPHICAL_CONTRAST ? [{
|
|
1393
|
+
primaryId: area.areaId,
|
|
1394
|
+
primaryLabel: area.label,
|
|
1395
|
+
measured: Math.round(ratio * 100) / 100,
|
|
1396
|
+
minimum: MIN_GRAPHICAL_CONTRAST
|
|
1397
|
+
}] : [];
|
|
1398
|
+
})
|
|
1399
|
+
));
|
|
1400
|
+
const materialFindings = findings.filter((item) => Boolean(item));
|
|
1401
|
+
return {
|
|
1402
|
+
version: RENDERED_QUALITY_REPORT_VERSION,
|
|
1403
|
+
passed: materialFindings.length === 0,
|
|
1404
|
+
state,
|
|
1405
|
+
targetCategoryKey,
|
|
1406
|
+
resolvedRules: [
|
|
1407
|
+
"rendered-overview-label-size",
|
|
1408
|
+
"rendered-overview-label-contrast",
|
|
1409
|
+
"rendered-overview-label-collision",
|
|
1410
|
+
"rendered-overview-hierarchy-containment",
|
|
1411
|
+
"rendered-overview-section-first-style",
|
|
1412
|
+
"rendered-overview-ga-contrast",
|
|
1413
|
+
...state === "interaction" ? [
|
|
1414
|
+
"rendered-detail-inventory",
|
|
1415
|
+
"rendered-pointer-target",
|
|
1416
|
+
"rendered-selected-held-taken-states",
|
|
1417
|
+
"rendered-section-focus",
|
|
1418
|
+
"rendered-category-filter"
|
|
1419
|
+
] : []
|
|
1420
|
+
],
|
|
1421
|
+
viewport: evidence.viewport,
|
|
1422
|
+
canvasBackground: evidence.canvasBackground,
|
|
1423
|
+
effectiveScale: evidence.effectiveScale,
|
|
1424
|
+
rung: evidence.rung,
|
|
1425
|
+
inventory: {
|
|
1426
|
+
totalBookableUnits: evidence.totalBookableUnits,
|
|
1427
|
+
totalLabelledBookableUnits: evidence.totalLabelledBookableUnits,
|
|
1428
|
+
visibleBookableLabels: bookable.length,
|
|
1429
|
+
hiddenBookableLabels: evidence.hiddenLabels,
|
|
1430
|
+
visibleHierarchyLabels: hierarchy.length,
|
|
1431
|
+
visibleFreeTextLabels: freeText.length,
|
|
1432
|
+
visibleGAAreas: visibleGA.length
|
|
1433
|
+
},
|
|
1434
|
+
overviewStyle: evidence.overviewStyle,
|
|
1435
|
+
composition: {
|
|
1436
|
+
hierarchy: evidence.hierarchyLabels.filter((label) => label.role === "name").map((label) => ({
|
|
1437
|
+
id: label.id,
|
|
1438
|
+
kind: label.kind,
|
|
1439
|
+
label: label.label,
|
|
1440
|
+
visible: label.visible
|
|
1441
|
+
})).sort((left, right) => left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)),
|
|
1442
|
+
labelledObjects: evidence.freeTextLabels.map((label) => ({
|
|
1443
|
+
objectId: label.objectId,
|
|
1444
|
+
kind: label.kind,
|
|
1445
|
+
text: label.text,
|
|
1446
|
+
visible: label.visible
|
|
1447
|
+
})).sort((left, right) => left.objectId.localeCompare(right.objectId) || left.kind.localeCompare(right.kind)),
|
|
1448
|
+
gaAreas: evidence.gaAreas.map((area) => ({
|
|
1449
|
+
areaId: area.areaId,
|
|
1450
|
+
label: area.label,
|
|
1451
|
+
categoryKey: area.categoryKey,
|
|
1452
|
+
...area.sectionId ? { sectionId: area.sectionId } : {},
|
|
1453
|
+
visible: area.visible
|
|
1454
|
+
})).sort((left, right) => left.areaId.localeCompare(right.areaId)),
|
|
1455
|
+
bookableSectionIds: [...new Set(evidence.labels.flatMap((label) => label.sectionId ? [label.sectionId] : []))].sort(),
|
|
1456
|
+
categoryKeys: [.../* @__PURE__ */ new Set([
|
|
1457
|
+
...evidence.labels.map((label) => label.categoryKey),
|
|
1458
|
+
...evidence.gaAreas.map((area) => area.categoryKey)
|
|
1459
|
+
])].sort(),
|
|
1460
|
+
activeCategoryKeys: [...activeCategoryKeys].sort()
|
|
1461
|
+
},
|
|
1462
|
+
metrics: {
|
|
1463
|
+
minimumRenderedBookableLabelPx: minimum(bookable.map((label) => label.renderedFontPx)),
|
|
1464
|
+
minimumBookableLabelContrast: minimum(bookableContrast),
|
|
1465
|
+
minimumRenderedHierarchyLabelPx: minimum(hierarchy.map((label) => label.renderedFontPx)),
|
|
1466
|
+
minimumHierarchyLabelContrast: minimum(hierarchyContrast),
|
|
1467
|
+
minimumRenderedFreeTextPx: minimum(freeText.map((label) => label.renderedFontPx)),
|
|
1468
|
+
minimumFreeTextContrast: minimum(freeTextContrast),
|
|
1469
|
+
minimumGAContrast: minimum(gaContrast),
|
|
1470
|
+
minimumEffectivePointerTargetPx: minimum(activePointerTargets.map((label) => label.pointerTarget.effectiveMinimumPx)),
|
|
1471
|
+
selectedRingContrast: selectedRingContrast == null ? null : Math.round(selectedRingContrast * 100) / 100
|
|
1472
|
+
},
|
|
1473
|
+
interaction: {
|
|
1474
|
+
applicable: {
|
|
1475
|
+
detail: targetLabels.length > 0 || targetGAAreas.length > 0,
|
|
1476
|
+
pointer: targetLabels.length > 0 || targetGAAreas.length > 0,
|
|
1477
|
+
held: targetLabels.length >= 2,
|
|
1478
|
+
booked: targetLabels.length >= 3,
|
|
1479
|
+
sectionFocus: targetLabels.some((label) => Boolean(label.sectionId)) || targetGAAreas.some((area) => Boolean(area.sectionId)),
|
|
1480
|
+
categoryFilter: (/* @__PURE__ */ new Set([
|
|
1481
|
+
...evidence.labels.map((label) => label.categoryKey),
|
|
1482
|
+
...evidence.gaAreas.map((area) => area.categoryKey)
|
|
1483
|
+
])).size >= 2
|
|
1484
|
+
},
|
|
1485
|
+
selectedUnits: selected.length,
|
|
1486
|
+
heldUnits: held.length,
|
|
1487
|
+
bookedUnits: booked.length,
|
|
1488
|
+
activePointerTargets: activePointerTargets.length,
|
|
1489
|
+
focusedSectionId: evidence.focusedSectionId,
|
|
1490
|
+
focusBackdropVisible: evidence.focusBackdropVisible,
|
|
1491
|
+
categoryFilterKeys: evidence.categoryFilterKeys
|
|
1492
|
+
},
|
|
1493
|
+
findings: materialFindings
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// src/engine/SeatmapRenderer.ts
|
|
1498
|
+
var import_Core = require("konva/lib/Core");
|
|
1499
|
+
var import_Stage = require("konva/lib/Stage");
|
|
1500
|
+
var import_Layer = require("konva/lib/Layer");
|
|
1501
|
+
var import_Group = require("konva/lib/Group");
|
|
1502
|
+
var import_Circle = require("konva/lib/shapes/Circle");
|
|
1503
|
+
var import_Rect = require("konva/lib/shapes/Rect");
|
|
1504
|
+
var import_Ellipse = require("konva/lib/shapes/Ellipse");
|
|
1505
|
+
var import_Line = require("konva/lib/shapes/Line");
|
|
1506
|
+
var import_Text = require("konva/lib/shapes/Text");
|
|
1507
|
+
var import_Path = require("konva/lib/shapes/Path");
|
|
1508
|
+
var import_Image = require("konva/lib/shapes/Image");
|
|
1509
|
+
var import_Shape = require("konva/lib/Shape");
|
|
1510
|
+
|
|
1056
1511
|
// src/lib/money.ts
|
|
1057
1512
|
var DEFAULT_CURRENCY = "USD";
|
|
1058
1513
|
var displayLocale;
|
|
@@ -1115,6 +1570,7 @@ var en = {
|
|
|
1115
1570
|
"picker.seatsLeftInSection.one": "{count} seat left",
|
|
1116
1571
|
"picker.seatsLeftInSection.other": "{count} seats left",
|
|
1117
1572
|
"picker.overview": "Overview",
|
|
1573
|
+
"picker.entrance": "Entrance",
|
|
1118
1574
|
"picker.tapSeatHint": "Tap any seat to check its view",
|
|
1119
1575
|
"picker.ticketTierFor": "Ticket tier for {label}",
|
|
1120
1576
|
"picker.viewFromSeat": "View from seat {label}",
|
|
@@ -5086,13 +5542,15 @@ var PickerController = class {
|
|
|
5086
5542
|
const sourceLabel = source && "label" in source && typeof source.label === "string" ? source.label : void 0;
|
|
5087
5543
|
const sourceDisplayLabel = source && "displayLabel" in source && typeof source.displayLabel === "string" && source.displayLabel ? source.displayLabel : sourceLabel;
|
|
5088
5544
|
const rowLabel = s.kind === "booth" ? void 0 : sourceDisplayLabel;
|
|
5545
|
+
const rowType = source && "displayType" in source && typeof source.displayType === "string" && source.displayType.trim() ? source.displayType.trim() : void 0;
|
|
5089
5546
|
const visibleSeatLabel = s.displayLabel ?? s.label;
|
|
5090
5547
|
const labelParts = visibleSeatLabel.split("-");
|
|
5091
5548
|
const seatNumber = sourceDisplayLabel && visibleSeatLabel.startsWith(`${sourceDisplayLabel}-`) ? visibleSeatLabel.slice(sourceDisplayLabel.length + 1) : s.kind === "booth" ? visibleSeatLabel : labelParts[labelParts.length - 1] ?? visibleSeatLabel;
|
|
5092
5549
|
this.seatContext.set(s.id, {
|
|
5093
5550
|
sectionLabel: sectionLabels.get(membership.objectToSection.get(s.rowId) ?? ""),
|
|
5094
5551
|
rowLabel,
|
|
5095
|
-
seatNumber
|
|
5552
|
+
seatNumber,
|
|
5553
|
+
rowType
|
|
5096
5554
|
});
|
|
5097
5555
|
}
|
|
5098
5556
|
const currency = res.event.currency ?? this.opts.currency;
|
|
@@ -5727,6 +6185,7 @@ var PickerController = class {
|
|
|
5727
6185
|
id,
|
|
5728
6186
|
label: sec.label,
|
|
5729
6187
|
zoneLabel: zone?.label ?? "",
|
|
6188
|
+
...sec.entrance && sec.entrance.trim() ? { entrance: sec.entrance.trim() } : {},
|
|
5730
6189
|
color,
|
|
5731
6190
|
seatsLeft,
|
|
5732
6191
|
priceMin: prices.length ? prices[0] : 0,
|
|
@@ -6292,6 +6751,7 @@ async function loadLocale(code) {
|
|
|
6292
6751
|
MAX_EVENT_INVENTORY,
|
|
6293
6752
|
MAX_GA_CAPACITY,
|
|
6294
6753
|
PickerController,
|
|
6754
|
+
RENDERED_QUALITY_REPORT_VERSION,
|
|
6295
6755
|
SUPPORTED_LOCALES,
|
|
6296
6756
|
SeatmapRenderer,
|
|
6297
6757
|
UNGROUPED_ID,
|
|
@@ -6319,6 +6779,7 @@ async function loadLocale(code) {
|
|
|
6319
6779
|
generateSeatThumb,
|
|
6320
6780
|
getLocale,
|
|
6321
6781
|
hiddenObjectIds,
|
|
6782
|
+
inspectRenderedQualityEvidence,
|
|
6322
6783
|
isGaUnitLabel,
|
|
6323
6784
|
isSectionHidden,
|
|
6324
6785
|
layerOf,
|