@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.js
CHANGED
|
@@ -723,20 +723,6 @@ function applyHidden(doc, hidden) {
|
|
|
723
723
|
return { ...doc, objects };
|
|
724
724
|
}
|
|
725
725
|
|
|
726
|
-
// src/engine/SeatmapRenderer.ts
|
|
727
|
-
import { Konva } from "konva/lib/Core";
|
|
728
|
-
import { Stage } from "konva/lib/Stage";
|
|
729
|
-
import { Layer } from "konva/lib/Layer";
|
|
730
|
-
import { Group } from "konva/lib/Group";
|
|
731
|
-
import { Circle } from "konva/lib/shapes/Circle";
|
|
732
|
-
import { Rect } from "konva/lib/shapes/Rect";
|
|
733
|
-
import { Ellipse } from "konva/lib/shapes/Ellipse";
|
|
734
|
-
import { Line } from "konva/lib/shapes/Line";
|
|
735
|
-
import { Text } from "konva/lib/shapes/Text";
|
|
736
|
-
import { Path } from "konva/lib/shapes/Path";
|
|
737
|
-
import { Image as KImage } from "konva/lib/shapes/Image";
|
|
738
|
-
import { Shape } from "konva/lib/Shape";
|
|
739
|
-
|
|
740
726
|
// src/core/chartRenderRules.ts
|
|
741
727
|
var SEAT_LABEL_FONT_SIZE = 7;
|
|
742
728
|
var BOOTH_LABEL_FONT_SIZE = 10;
|
|
@@ -793,6 +779,470 @@ function stateAwareBookableLabelInk(fill, preferred) {
|
|
|
793
779
|
return darkContrast >= lightContrast ? DARK_BOOKABLE_LABEL_INK : LIGHT_BOOKABLE_LABEL_INK;
|
|
794
780
|
}
|
|
795
781
|
|
|
782
|
+
// src/core/renderedQuality.ts
|
|
783
|
+
var RENDERED_QUALITY_REPORT_VERSION = 3;
|
|
784
|
+
var MIN_TEXT_CONTRAST = 4.5;
|
|
785
|
+
var MIN_GRAPHICAL_CONTRAST = 3;
|
|
786
|
+
var MIN_POINTER_TARGET_PX = 24;
|
|
787
|
+
var MAX_SAMPLES_PER_FINDING = 20;
|
|
788
|
+
function visibleWithBox(items) {
|
|
789
|
+
return items.filter((item) => item.visible && Boolean(item.screenBox));
|
|
790
|
+
}
|
|
791
|
+
function intersects(left, right) {
|
|
792
|
+
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;
|
|
793
|
+
}
|
|
794
|
+
function minimum(values) {
|
|
795
|
+
return values.length ? Math.round(Math.min(...values) * 100) / 100 : null;
|
|
796
|
+
}
|
|
797
|
+
function finding(code, message, samples) {
|
|
798
|
+
if (!samples.length) return null;
|
|
799
|
+
return {
|
|
800
|
+
code,
|
|
801
|
+
message,
|
|
802
|
+
count: samples.length,
|
|
803
|
+
samples: samples.slice(0, MAX_SAMPLES_PER_FINDING)
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
function labelSample(label, measured, minimumValue) {
|
|
807
|
+
return {
|
|
808
|
+
primaryId: label.seatId,
|
|
809
|
+
primaryLabel: label.label,
|
|
810
|
+
...measured == null ? {} : { measured: Math.round(measured * 100) / 100 },
|
|
811
|
+
...minimumValue == null ? {} : { minimum: minimumValue }
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function hierarchySample(label, measured, minimumValue) {
|
|
815
|
+
return {
|
|
816
|
+
primaryId: label.id,
|
|
817
|
+
primaryLabel: label.label,
|
|
818
|
+
...measured == null ? {} : { measured: Math.round(measured * 100) / 100 },
|
|
819
|
+
...minimumValue == null ? {} : { minimum: minimumValue }
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
function freeTextSample(label, measured, minimumValue) {
|
|
823
|
+
return {
|
|
824
|
+
primaryId: label.objectId,
|
|
825
|
+
primaryLabel: label.text,
|
|
826
|
+
...measured == null ? {} : { measured: Math.round(measured * 100) / 100 },
|
|
827
|
+
...minimumValue == null ? {} : { minimum: minimumValue }
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
function collisionSample(primaryId, primaryLabel, secondaryId, secondaryLabel) {
|
|
831
|
+
return { primaryId, primaryLabel, secondaryId, secondaryLabel };
|
|
832
|
+
}
|
|
833
|
+
function inspectRenderedQualityEvidence(evidence, state = "overview", targetCategoryKey = null) {
|
|
834
|
+
const bookable = visibleWithBox(evidence.labels);
|
|
835
|
+
const hierarchy = visibleWithBox(evidence.hierarchyLabels);
|
|
836
|
+
const freeText = visibleWithBox(evidence.freeTextLabels);
|
|
837
|
+
const visibleGA = evidence.gaAreas.filter((area) => area.visible);
|
|
838
|
+
const bookableContrast = bookable.map((label) => renderedTextContrast(label.ink, label.fill) ?? 0);
|
|
839
|
+
const hierarchyContrast = hierarchy.map((label) => renderedTextContrast(label.ink, label.fill) ?? 0);
|
|
840
|
+
const freeTextContrast = freeText.map((label) => renderedTextContrast(label.ink, label.background) ?? 0);
|
|
841
|
+
const gaContrast = visibleGA.map((area) => renderedTextContrast(area.effectiveBackground, evidence.canvasBackground) ?? 0);
|
|
842
|
+
const targetLabels = targetCategoryKey ? evidence.labels.filter((label) => label.categoryKey === targetCategoryKey) : evidence.labels;
|
|
843
|
+
const targetGAAreas = targetCategoryKey ? evidence.gaAreas.filter((area) => area.categoryKey === targetCategoryKey) : evidence.gaAreas;
|
|
844
|
+
const selected = targetLabels.filter((label) => label.selected);
|
|
845
|
+
const held = targetLabels.filter((label) => label.status === "held");
|
|
846
|
+
const booked = targetLabels.filter((label) => label.status === "booked");
|
|
847
|
+
const activePointerTargets = targetLabels.filter((label) => label.pointerTarget.active);
|
|
848
|
+
const activeCategoryKeys = /* @__PURE__ */ new Set([
|
|
849
|
+
...evidence.labels.filter((label) => label.visible && (label.opacity > 0.25 || label.selected || label.status !== "free")).map((label) => label.categoryKey),
|
|
850
|
+
...evidence.gaAreas.filter((area) => area.visible && area.opacity > 0.1).map((area) => area.categoryKey)
|
|
851
|
+
]);
|
|
852
|
+
const selectedRingContrast = selected.length ? minimum(selected.flatMap((label) => [
|
|
853
|
+
renderedTextContrast(evidence.selectionRingColor, evidence.canvasBackground) ?? 0,
|
|
854
|
+
renderedTextContrast(evidence.selectionRingColor, label.fill) ?? 0
|
|
855
|
+
])) : null;
|
|
856
|
+
const findings = [];
|
|
857
|
+
const overviewCountSamples = (count, label) => Array.from({ length: count }, (_, index) => ({
|
|
858
|
+
primaryId: `overview:${index + 1}`,
|
|
859
|
+
primaryLabel: label
|
|
860
|
+
}));
|
|
861
|
+
if (state === "overview" && evidence.rung === "sections") {
|
|
862
|
+
findings.push(finding(
|
|
863
|
+
"overview-section-category-paint",
|
|
864
|
+
"Section overview shells must use the neutral hierarchy palette, not category paint",
|
|
865
|
+
overviewCountSamples(evidence.overviewStyle.categoryPaintedSectionShells, "category-painted section shell")
|
|
866
|
+
));
|
|
867
|
+
findings.push(finding(
|
|
868
|
+
"overview-category-detail-visible",
|
|
869
|
+
"Category-tinted section detail must wait until section focus or seat zoom",
|
|
870
|
+
overviewCountSamples(evidence.overviewStyle.visibleCategoryDetailOutlines, "category detail outline")
|
|
871
|
+
));
|
|
872
|
+
findings.push(finding(
|
|
873
|
+
"overview-row-hints-visible",
|
|
874
|
+
"Row and seat patterns must not clutter the section overview",
|
|
875
|
+
overviewCountSamples(evidence.overviewStyle.visibleSectionRowHints, "section row hint")
|
|
876
|
+
));
|
|
877
|
+
findings.push(finding(
|
|
878
|
+
"overview-availability-clutter",
|
|
879
|
+
"Live availability counts belong in focused detail, not on section overview shells",
|
|
880
|
+
overviewCountSamples(evidence.overviewStyle.visibleSectionAvailabilityLabels, "section availability label")
|
|
881
|
+
));
|
|
882
|
+
findings.push(finding(
|
|
883
|
+
"overview-ga-detail-visible",
|
|
884
|
+
"Section-contained standing paint belongs in focused detail, not on section overview shells",
|
|
885
|
+
overviewCountSamples(evidence.overviewStyle.visibleSectionGADetails, "section-contained GA detail")
|
|
886
|
+
));
|
|
887
|
+
}
|
|
888
|
+
findings.push(finding(
|
|
889
|
+
"bookable-label-undersized",
|
|
890
|
+
"Visible bookable labels must meet the rendered small-text size floor",
|
|
891
|
+
bookable.filter((label) => label.renderedFontPx < evidence.minimumVisibleLabelPx).map((label) => labelSample(label, label.renderedFontPx, evidence.minimumVisibleLabelPx))
|
|
892
|
+
));
|
|
893
|
+
if (state === "interaction") {
|
|
894
|
+
const labelledInventory = targetLabels.length > 0;
|
|
895
|
+
const gaInventory = targetGAAreas.length > 0;
|
|
896
|
+
const detailInventory = labelledInventory || gaInventory;
|
|
897
|
+
const visibleTargetGA = targetGAAreas.filter((area) => area.visible);
|
|
898
|
+
const sections = /* @__PURE__ */ new Set([
|
|
899
|
+
...targetLabels.flatMap((label) => label.sectionId ? [label.sectionId] : []),
|
|
900
|
+
...targetGAAreas.flatMap((area) => area.sectionId ? [area.sectionId] : [])
|
|
901
|
+
]);
|
|
902
|
+
const categories = /* @__PURE__ */ new Set([
|
|
903
|
+
...evidence.labels.map((label) => label.categoryKey),
|
|
904
|
+
...evidence.gaAreas.map((area) => area.categoryKey)
|
|
905
|
+
]);
|
|
906
|
+
const inViewport = (label) => label.screenCenter.x >= 0 && label.screenCenter.x <= evidence.viewport.width && label.screenCenter.y >= 0 && label.screenCenter.y <= evidence.viewport.height;
|
|
907
|
+
findings.push(finding(
|
|
908
|
+
"detail-rung-missing",
|
|
909
|
+
"Interaction evidence with bookable inventory must render the seat-detail rung",
|
|
910
|
+
detailInventory && evidence.rung !== "seats" ? [{ primaryId: "renderer", primaryLabel: evidence.rung }] : []
|
|
911
|
+
));
|
|
912
|
+
findings.push(finding(
|
|
913
|
+
"detail-inventory-not-visible",
|
|
914
|
+
"Interaction evidence must frame at least one real bookable unit",
|
|
915
|
+
detailInventory && !targetLabels.some(inViewport) && !visibleTargetGA.length ? [{ primaryId: "renderer", primaryLabel: "no target inventory in viewport" }] : []
|
|
916
|
+
));
|
|
917
|
+
findings.push(finding(
|
|
918
|
+
"pointer-target-inactive",
|
|
919
|
+
"Interaction evidence must expose a live production pointer target",
|
|
920
|
+
labelledInventory && !activePointerTargets.some(inViewport) || !labelledInventory && gaInventory && !visibleTargetGA.some((area) => area.interactive) ? [{ primaryId: "renderer", primaryLabel: "no active pointer target in viewport" }] : []
|
|
921
|
+
));
|
|
922
|
+
findings.push(finding(
|
|
923
|
+
"pointer-target-undersized",
|
|
924
|
+
"Every active production pointer target must reach at least 24 CSS pixels",
|
|
925
|
+
activePointerTargets.filter((label) => inViewport(label) && label.pointerTarget.effectiveMinimumPx < MIN_POINTER_TARGET_PX).map((label) => labelSample(label, label.pointerTarget.effectiveMinimumPx, MIN_POINTER_TARGET_PX))
|
|
926
|
+
));
|
|
927
|
+
findings.push(finding(
|
|
928
|
+
"selected-state-missing",
|
|
929
|
+
"Interaction evidence must paint a selected unit and its renderer-owned ring",
|
|
930
|
+
labelledInventory && (!selected.length || !selected.some((label) => evidence.selectionRingSeatIds.includes(label.seatId))) ? [{ primaryId: "renderer", primaryLabel: "selected state" }] : []
|
|
931
|
+
));
|
|
932
|
+
findings.push(finding(
|
|
933
|
+
"selected-state-contrast-low",
|
|
934
|
+
"The selected-state ring must maintain 3:1 graphical contrast with the canvas",
|
|
935
|
+
selected.length && (selectedRingContrast ?? 0) < MIN_GRAPHICAL_CONTRAST ? [{
|
|
936
|
+
primaryId: selected[0].seatId,
|
|
937
|
+
primaryLabel: selected[0].label,
|
|
938
|
+
measured: Math.round((selectedRingContrast ?? 0) * 100) / 100,
|
|
939
|
+
minimum: MIN_GRAPHICAL_CONTRAST
|
|
940
|
+
}] : []
|
|
941
|
+
));
|
|
942
|
+
findings.push(finding(
|
|
943
|
+
"held-state-missing",
|
|
944
|
+
"Interaction evidence must paint a held state when the floor has at least two status-managed units",
|
|
945
|
+
targetLabels.length >= 2 && !held.length ? [{ primaryId: "renderer", primaryLabel: "held state" }] : []
|
|
946
|
+
));
|
|
947
|
+
findings.push(finding(
|
|
948
|
+
"booked-state-missing",
|
|
949
|
+
"Interaction evidence must paint a taken state when the floor has at least three status-managed units",
|
|
950
|
+
targetLabels.length >= 3 && !booked.length ? [{ primaryId: "renderer", primaryLabel: "booked state" }] : []
|
|
951
|
+
));
|
|
952
|
+
const heldSignatures = new Set(held.map((label) => `${label.fill.toLowerCase()}:${label.opacity}`));
|
|
953
|
+
const bookedSignatures = new Set(booked.map((label) => `${label.fill.toLowerCase()}:${label.opacity}`));
|
|
954
|
+
findings.push(finding(
|
|
955
|
+
"status-state-indistinct",
|
|
956
|
+
"Held and taken evidence must resolve to distinct renderer paint",
|
|
957
|
+
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 }] : []
|
|
958
|
+
));
|
|
959
|
+
findings.push(finding(
|
|
960
|
+
"section-focus-missing",
|
|
961
|
+
"Interaction evidence must exercise section focus and its backdrop when section membership exists",
|
|
962
|
+
sections.size && (!evidence.focusedSectionId || !evidence.focusBackdropVisible) ? [{ primaryId: "renderer", primaryLabel: "section focus" }] : []
|
|
963
|
+
));
|
|
964
|
+
findings.push(finding(
|
|
965
|
+
"category-filter-missing",
|
|
966
|
+
"Interaction evidence must exercise a category filter when multiple categories exist",
|
|
967
|
+
categories.size >= 2 && (!evidence.categoryFilterKeys || !evidence.categoryFilterKeys.length) ? [{ primaryId: "renderer", primaryLabel: "category filter" }] : []
|
|
968
|
+
));
|
|
969
|
+
const excludedFree = evidence.labels.filter((label) => label.status === "free" && !label.selected && evidence.categoryFilterKeys != null && !evidence.categoryFilterKeys.includes(label.categoryKey));
|
|
970
|
+
const excludedGA = evidence.gaAreas.filter((area) => evidence.categoryFilterKeys != null && !evidence.categoryFilterKeys.includes(area.categoryKey));
|
|
971
|
+
findings.push(finding(
|
|
972
|
+
"category-filter-ineffective",
|
|
973
|
+
"The active category filter must visibly dim excluded free inventory",
|
|
974
|
+
(excludedFree.length || excludedGA.length) && !excludedFree.some((label) => label.opacity <= 0.25) && !excludedGA.some((area) => area.opacity <= 0.1) ? [
|
|
975
|
+
...excludedFree.map((label) => labelSample(label, label.opacity)),
|
|
976
|
+
...excludedGA.map((area) => ({ primaryId: area.areaId, primaryLabel: area.label, measured: area.opacity }))
|
|
977
|
+
] : []
|
|
978
|
+
));
|
|
979
|
+
findings.push(finding(
|
|
980
|
+
"target-category-not-visible",
|
|
981
|
+
"A category-specific interaction scene must visibly paint its exact target category",
|
|
982
|
+
targetCategoryKey && !activeCategoryKeys.has(targetCategoryKey) ? [{ primaryId: targetCategoryKey, primaryLabel: targetCategoryKey }] : []
|
|
983
|
+
));
|
|
984
|
+
findings.push(finding(
|
|
985
|
+
"target-category-filter-mismatch",
|
|
986
|
+
"A category-specific interaction scene must bind its filter to only the exact target category",
|
|
987
|
+
targetCategoryKey && (evidence.categoryFilterKeys?.length !== 1 || evidence.categoryFilterKeys[0] !== targetCategoryKey) ? [{ primaryId: targetCategoryKey, primaryLabel: targetCategoryKey }] : []
|
|
988
|
+
));
|
|
989
|
+
}
|
|
990
|
+
findings.push(finding(
|
|
991
|
+
"bookable-label-contrast-low",
|
|
992
|
+
"Visible bookable labels must meet 4.5:1 contrast against their actual paint",
|
|
993
|
+
bookable.flatMap((label) => {
|
|
994
|
+
const ratio = renderedTextContrast(label.ink, label.fill) ?? 0;
|
|
995
|
+
return ratio < MIN_TEXT_CONTRAST ? [labelSample(label, ratio, MIN_TEXT_CONTRAST)] : [];
|
|
996
|
+
})
|
|
997
|
+
));
|
|
998
|
+
const bookableCollisions = [];
|
|
999
|
+
for (let index = 0; index < bookable.length; index += 1) {
|
|
1000
|
+
for (let other = 0; other < index; other += 1) {
|
|
1001
|
+
if (intersects(bookable[index].screenBox, bookable[other].screenBox)) {
|
|
1002
|
+
bookableCollisions.push(collisionSample(
|
|
1003
|
+
bookable[other].seatId,
|
|
1004
|
+
bookable[other].label,
|
|
1005
|
+
bookable[index].seatId,
|
|
1006
|
+
bookable[index].label
|
|
1007
|
+
));
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
findings.push(finding(
|
|
1012
|
+
"bookable-label-collision",
|
|
1013
|
+
"Visible bookable labels must not overlap",
|
|
1014
|
+
bookableCollisions
|
|
1015
|
+
));
|
|
1016
|
+
findings.push(finding(
|
|
1017
|
+
"hierarchy-label-undersized",
|
|
1018
|
+
"Visible section and zone labels must meet the rendered small-text size floor",
|
|
1019
|
+
hierarchy.filter((label) => label.renderedFontPx < MIN_VISIBLE_BOOKABLE_LABEL_PX).map((label) => hierarchySample(label, label.renderedFontPx, MIN_VISIBLE_BOOKABLE_LABEL_PX))
|
|
1020
|
+
));
|
|
1021
|
+
findings.push(finding(
|
|
1022
|
+
"hierarchy-label-contrast-low",
|
|
1023
|
+
"Visible section and zone labels must meet 4.5:1 contrast against their backing paint",
|
|
1024
|
+
hierarchy.flatMap((label) => {
|
|
1025
|
+
const ratio = renderedTextContrast(label.ink, label.fill) ?? 0;
|
|
1026
|
+
return ratio < MIN_TEXT_CONTRAST ? [hierarchySample(label, ratio, MIN_TEXT_CONTRAST)] : [];
|
|
1027
|
+
})
|
|
1028
|
+
));
|
|
1029
|
+
findings.push(finding(
|
|
1030
|
+
"hierarchy-label-outside-section",
|
|
1031
|
+
"Visible section labels must remain inside the filled section surface and outside holes",
|
|
1032
|
+
hierarchy.filter((label) => label.kind === "section" && label.fitsContainer === false).map((label) => hierarchySample(label))
|
|
1033
|
+
));
|
|
1034
|
+
const hierarchyCollisions = [];
|
|
1035
|
+
for (let index = 0; index < hierarchy.length; index += 1) {
|
|
1036
|
+
for (let other = 0; other < index; other += 1) {
|
|
1037
|
+
if (intersects(hierarchy[index].screenBox, hierarchy[other].screenBox)) {
|
|
1038
|
+
hierarchyCollisions.push(collisionSample(
|
|
1039
|
+
hierarchy[other].id,
|
|
1040
|
+
hierarchy[other].label,
|
|
1041
|
+
hierarchy[index].id,
|
|
1042
|
+
hierarchy[index].label
|
|
1043
|
+
));
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
findings.push(finding(
|
|
1048
|
+
"hierarchy-label-collision",
|
|
1049
|
+
"Visible hierarchy labels must not overlap each other",
|
|
1050
|
+
hierarchyCollisions
|
|
1051
|
+
));
|
|
1052
|
+
const hierarchyBookableCollisions = [];
|
|
1053
|
+
for (const upper of hierarchy) {
|
|
1054
|
+
for (const unit of bookable) {
|
|
1055
|
+
if (intersects(upper.screenBox, unit.screenBox)) {
|
|
1056
|
+
hierarchyBookableCollisions.push(collisionSample(upper.id, upper.label, unit.seatId, unit.label));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
findings.push(finding(
|
|
1061
|
+
"hierarchy-bookable-collision",
|
|
1062
|
+
"Visible hierarchy labels must not overlap bookable labels",
|
|
1063
|
+
hierarchyBookableCollisions
|
|
1064
|
+
));
|
|
1065
|
+
findings.push(finding(
|
|
1066
|
+
"free-text-undersized",
|
|
1067
|
+
"Visible chart text must meet the rendered small-text size floor",
|
|
1068
|
+
freeText.filter((label) => label.renderedFontPx < evidence.minimumVisibleLabelPx).map((label) => freeTextSample(label, label.renderedFontPx, evidence.minimumVisibleLabelPx))
|
|
1069
|
+
));
|
|
1070
|
+
findings.push(finding(
|
|
1071
|
+
"free-text-contrast-low",
|
|
1072
|
+
"Visible chart text must meet 4.5:1 contrast against its measured background",
|
|
1073
|
+
freeText.flatMap((label) => {
|
|
1074
|
+
const ratio = renderedTextContrast(label.ink, label.background) ?? 0;
|
|
1075
|
+
return ratio < MIN_TEXT_CONTRAST ? [freeTextSample(label, ratio, MIN_TEXT_CONTRAST)] : [];
|
|
1076
|
+
})
|
|
1077
|
+
));
|
|
1078
|
+
const freeTextCollisions = [];
|
|
1079
|
+
for (let index = 0; index < freeText.length; index += 1) {
|
|
1080
|
+
for (let other = 0; other < index; other += 1) {
|
|
1081
|
+
if (intersects(freeText[index].screenBox, freeText[other].screenBox)) {
|
|
1082
|
+
freeTextCollisions.push(collisionSample(
|
|
1083
|
+
freeText[other].objectId,
|
|
1084
|
+
freeText[other].text,
|
|
1085
|
+
freeText[index].objectId,
|
|
1086
|
+
freeText[index].text
|
|
1087
|
+
));
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
findings.push(finding(
|
|
1092
|
+
"free-text-collision",
|
|
1093
|
+
"Visible chart text labels must not overlap each other",
|
|
1094
|
+
freeTextCollisions
|
|
1095
|
+
));
|
|
1096
|
+
const freeTextBookableCollisions = [];
|
|
1097
|
+
for (const text of freeText) {
|
|
1098
|
+
for (const unit of bookable) {
|
|
1099
|
+
if (intersects(text.screenBox, unit.screenBox)) {
|
|
1100
|
+
freeTextBookableCollisions.push(collisionSample(text.objectId, text.text, unit.seatId, unit.label));
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
findings.push(finding(
|
|
1105
|
+
"free-text-bookable-collision",
|
|
1106
|
+
"Visible chart text must not overlap bookable labels",
|
|
1107
|
+
freeTextBookableCollisions
|
|
1108
|
+
));
|
|
1109
|
+
const freeTextHierarchyCollisions = [];
|
|
1110
|
+
for (const text of freeText) {
|
|
1111
|
+
for (const upper of hierarchy) {
|
|
1112
|
+
if (intersects(text.screenBox, upper.screenBox)) {
|
|
1113
|
+
freeTextHierarchyCollisions.push(collisionSample(text.objectId, text.text, upper.id, upper.label));
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
findings.push(finding(
|
|
1118
|
+
"free-text-hierarchy-collision",
|
|
1119
|
+
"Visible chart text must not overlap hierarchy labels",
|
|
1120
|
+
freeTextHierarchyCollisions
|
|
1121
|
+
));
|
|
1122
|
+
findings.push(finding(
|
|
1123
|
+
"ga-contrast-low",
|
|
1124
|
+
"Visible GA surfaces must maintain 3:1 graphical contrast with the canvas",
|
|
1125
|
+
visibleGA.flatMap((area) => {
|
|
1126
|
+
const ratio = renderedTextContrast(area.effectiveBackground, evidence.canvasBackground) ?? 0;
|
|
1127
|
+
return ratio < MIN_GRAPHICAL_CONTRAST ? [{
|
|
1128
|
+
primaryId: area.areaId,
|
|
1129
|
+
primaryLabel: area.label,
|
|
1130
|
+
measured: Math.round(ratio * 100) / 100,
|
|
1131
|
+
minimum: MIN_GRAPHICAL_CONTRAST
|
|
1132
|
+
}] : [];
|
|
1133
|
+
})
|
|
1134
|
+
));
|
|
1135
|
+
const materialFindings = findings.filter((item) => Boolean(item));
|
|
1136
|
+
return {
|
|
1137
|
+
version: RENDERED_QUALITY_REPORT_VERSION,
|
|
1138
|
+
passed: materialFindings.length === 0,
|
|
1139
|
+
state,
|
|
1140
|
+
targetCategoryKey,
|
|
1141
|
+
resolvedRules: [
|
|
1142
|
+
"rendered-overview-label-size",
|
|
1143
|
+
"rendered-overview-label-contrast",
|
|
1144
|
+
"rendered-overview-label-collision",
|
|
1145
|
+
"rendered-overview-hierarchy-containment",
|
|
1146
|
+
"rendered-overview-section-first-style",
|
|
1147
|
+
"rendered-overview-ga-contrast",
|
|
1148
|
+
...state === "interaction" ? [
|
|
1149
|
+
"rendered-detail-inventory",
|
|
1150
|
+
"rendered-pointer-target",
|
|
1151
|
+
"rendered-selected-held-taken-states",
|
|
1152
|
+
"rendered-section-focus",
|
|
1153
|
+
"rendered-category-filter"
|
|
1154
|
+
] : []
|
|
1155
|
+
],
|
|
1156
|
+
viewport: evidence.viewport,
|
|
1157
|
+
canvasBackground: evidence.canvasBackground,
|
|
1158
|
+
effectiveScale: evidence.effectiveScale,
|
|
1159
|
+
rung: evidence.rung,
|
|
1160
|
+
inventory: {
|
|
1161
|
+
totalBookableUnits: evidence.totalBookableUnits,
|
|
1162
|
+
totalLabelledBookableUnits: evidence.totalLabelledBookableUnits,
|
|
1163
|
+
visibleBookableLabels: bookable.length,
|
|
1164
|
+
hiddenBookableLabels: evidence.hiddenLabels,
|
|
1165
|
+
visibleHierarchyLabels: hierarchy.length,
|
|
1166
|
+
visibleFreeTextLabels: freeText.length,
|
|
1167
|
+
visibleGAAreas: visibleGA.length
|
|
1168
|
+
},
|
|
1169
|
+
overviewStyle: evidence.overviewStyle,
|
|
1170
|
+
composition: {
|
|
1171
|
+
hierarchy: evidence.hierarchyLabels.filter((label) => label.role === "name").map((label) => ({
|
|
1172
|
+
id: label.id,
|
|
1173
|
+
kind: label.kind,
|
|
1174
|
+
label: label.label,
|
|
1175
|
+
visible: label.visible
|
|
1176
|
+
})).sort((left, right) => left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)),
|
|
1177
|
+
labelledObjects: evidence.freeTextLabels.map((label) => ({
|
|
1178
|
+
objectId: label.objectId,
|
|
1179
|
+
kind: label.kind,
|
|
1180
|
+
text: label.text,
|
|
1181
|
+
visible: label.visible
|
|
1182
|
+
})).sort((left, right) => left.objectId.localeCompare(right.objectId) || left.kind.localeCompare(right.kind)),
|
|
1183
|
+
gaAreas: evidence.gaAreas.map((area) => ({
|
|
1184
|
+
areaId: area.areaId,
|
|
1185
|
+
label: area.label,
|
|
1186
|
+
categoryKey: area.categoryKey,
|
|
1187
|
+
...area.sectionId ? { sectionId: area.sectionId } : {},
|
|
1188
|
+
visible: area.visible
|
|
1189
|
+
})).sort((left, right) => left.areaId.localeCompare(right.areaId)),
|
|
1190
|
+
bookableSectionIds: [...new Set(evidence.labels.flatMap((label) => label.sectionId ? [label.sectionId] : []))].sort(),
|
|
1191
|
+
categoryKeys: [.../* @__PURE__ */ new Set([
|
|
1192
|
+
...evidence.labels.map((label) => label.categoryKey),
|
|
1193
|
+
...evidence.gaAreas.map((area) => area.categoryKey)
|
|
1194
|
+
])].sort(),
|
|
1195
|
+
activeCategoryKeys: [...activeCategoryKeys].sort()
|
|
1196
|
+
},
|
|
1197
|
+
metrics: {
|
|
1198
|
+
minimumRenderedBookableLabelPx: minimum(bookable.map((label) => label.renderedFontPx)),
|
|
1199
|
+
minimumBookableLabelContrast: minimum(bookableContrast),
|
|
1200
|
+
minimumRenderedHierarchyLabelPx: minimum(hierarchy.map((label) => label.renderedFontPx)),
|
|
1201
|
+
minimumHierarchyLabelContrast: minimum(hierarchyContrast),
|
|
1202
|
+
minimumRenderedFreeTextPx: minimum(freeText.map((label) => label.renderedFontPx)),
|
|
1203
|
+
minimumFreeTextContrast: minimum(freeTextContrast),
|
|
1204
|
+
minimumGAContrast: minimum(gaContrast),
|
|
1205
|
+
minimumEffectivePointerTargetPx: minimum(activePointerTargets.map((label) => label.pointerTarget.effectiveMinimumPx)),
|
|
1206
|
+
selectedRingContrast: selectedRingContrast == null ? null : Math.round(selectedRingContrast * 100) / 100
|
|
1207
|
+
},
|
|
1208
|
+
interaction: {
|
|
1209
|
+
applicable: {
|
|
1210
|
+
detail: targetLabels.length > 0 || targetGAAreas.length > 0,
|
|
1211
|
+
pointer: targetLabels.length > 0 || targetGAAreas.length > 0,
|
|
1212
|
+
held: targetLabels.length >= 2,
|
|
1213
|
+
booked: targetLabels.length >= 3,
|
|
1214
|
+
sectionFocus: targetLabels.some((label) => Boolean(label.sectionId)) || targetGAAreas.some((area) => Boolean(area.sectionId)),
|
|
1215
|
+
categoryFilter: (/* @__PURE__ */ new Set([
|
|
1216
|
+
...evidence.labels.map((label) => label.categoryKey),
|
|
1217
|
+
...evidence.gaAreas.map((area) => area.categoryKey)
|
|
1218
|
+
])).size >= 2
|
|
1219
|
+
},
|
|
1220
|
+
selectedUnits: selected.length,
|
|
1221
|
+
heldUnits: held.length,
|
|
1222
|
+
bookedUnits: booked.length,
|
|
1223
|
+
activePointerTargets: activePointerTargets.length,
|
|
1224
|
+
focusedSectionId: evidence.focusedSectionId,
|
|
1225
|
+
focusBackdropVisible: evidence.focusBackdropVisible,
|
|
1226
|
+
categoryFilterKeys: evidence.categoryFilterKeys
|
|
1227
|
+
},
|
|
1228
|
+
findings: materialFindings
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// src/engine/SeatmapRenderer.ts
|
|
1233
|
+
import { Konva } from "konva/lib/Core";
|
|
1234
|
+
import { Stage } from "konva/lib/Stage";
|
|
1235
|
+
import { Layer } from "konva/lib/Layer";
|
|
1236
|
+
import { Group } from "konva/lib/Group";
|
|
1237
|
+
import { Circle } from "konva/lib/shapes/Circle";
|
|
1238
|
+
import { Rect } from "konva/lib/shapes/Rect";
|
|
1239
|
+
import { Ellipse } from "konva/lib/shapes/Ellipse";
|
|
1240
|
+
import { Line } from "konva/lib/shapes/Line";
|
|
1241
|
+
import { Text } from "konva/lib/shapes/Text";
|
|
1242
|
+
import { Path } from "konva/lib/shapes/Path";
|
|
1243
|
+
import { Image as KImage } from "konva/lib/shapes/Image";
|
|
1244
|
+
import { Shape } from "konva/lib/Shape";
|
|
1245
|
+
|
|
796
1246
|
// src/lib/money.ts
|
|
797
1247
|
var DEFAULT_CURRENCY = "USD";
|
|
798
1248
|
var displayLocale;
|
|
@@ -855,6 +1305,7 @@ var en = {
|
|
|
855
1305
|
"picker.seatsLeftInSection.one": "{count} seat left",
|
|
856
1306
|
"picker.seatsLeftInSection.other": "{count} seats left",
|
|
857
1307
|
"picker.overview": "Overview",
|
|
1308
|
+
"picker.entrance": "Entrance",
|
|
858
1309
|
"picker.tapSeatHint": "Tap any seat to check its view",
|
|
859
1310
|
"picker.ticketTierFor": "Ticket tier for {label}",
|
|
860
1311
|
"picker.viewFromSeat": "View from seat {label}",
|
|
@@ -4826,13 +5277,15 @@ var PickerController = class {
|
|
|
4826
5277
|
const sourceLabel = source && "label" in source && typeof source.label === "string" ? source.label : void 0;
|
|
4827
5278
|
const sourceDisplayLabel = source && "displayLabel" in source && typeof source.displayLabel === "string" && source.displayLabel ? source.displayLabel : sourceLabel;
|
|
4828
5279
|
const rowLabel = s.kind === "booth" ? void 0 : sourceDisplayLabel;
|
|
5280
|
+
const rowType = source && "displayType" in source && typeof source.displayType === "string" && source.displayType.trim() ? source.displayType.trim() : void 0;
|
|
4829
5281
|
const visibleSeatLabel = s.displayLabel ?? s.label;
|
|
4830
5282
|
const labelParts = visibleSeatLabel.split("-");
|
|
4831
5283
|
const seatNumber = sourceDisplayLabel && visibleSeatLabel.startsWith(`${sourceDisplayLabel}-`) ? visibleSeatLabel.slice(sourceDisplayLabel.length + 1) : s.kind === "booth" ? visibleSeatLabel : labelParts[labelParts.length - 1] ?? visibleSeatLabel;
|
|
4832
5284
|
this.seatContext.set(s.id, {
|
|
4833
5285
|
sectionLabel: sectionLabels.get(membership.objectToSection.get(s.rowId) ?? ""),
|
|
4834
5286
|
rowLabel,
|
|
4835
|
-
seatNumber
|
|
5287
|
+
seatNumber,
|
|
5288
|
+
rowType
|
|
4836
5289
|
});
|
|
4837
5290
|
}
|
|
4838
5291
|
const currency = res.event.currency ?? this.opts.currency;
|
|
@@ -5467,6 +5920,7 @@ var PickerController = class {
|
|
|
5467
5920
|
id,
|
|
5468
5921
|
label: sec.label,
|
|
5469
5922
|
zoneLabel: zone?.label ?? "",
|
|
5923
|
+
...sec.entrance && sec.entrance.trim() ? { entrance: sec.entrance.trim() } : {},
|
|
5470
5924
|
color,
|
|
5471
5925
|
seatsLeft,
|
|
5472
5926
|
priceMin: prices.length ? prices[0] : 0,
|
|
@@ -6000,9 +6454,9 @@ function generateSeatThumb(seat, focalPoint, _neighborSeats) {
|
|
|
6000
6454
|
|
|
6001
6455
|
// src/i18n/bundles.ts
|
|
6002
6456
|
var LOADERS = {
|
|
6003
|
-
es: () => import("./es-
|
|
6004
|
-
de: () => import("./de-
|
|
6005
|
-
fr: () => import("./fr-
|
|
6457
|
+
es: () => import("./es-SKAODLTR.js").then((m) => ({ default: m.es })),
|
|
6458
|
+
de: () => import("./de-73TIXYJH.js").then((m) => ({ default: m.de })),
|
|
6459
|
+
fr: () => import("./fr-J4637T6V.js").then((m) => ({ default: m.fr }))
|
|
6006
6460
|
};
|
|
6007
6461
|
var loaded = /* @__PURE__ */ new Set(["en"]);
|
|
6008
6462
|
async function loadLocale(code) {
|
|
@@ -6031,6 +6485,7 @@ export {
|
|
|
6031
6485
|
MAX_EVENT_INVENTORY,
|
|
6032
6486
|
MAX_GA_CAPACITY,
|
|
6033
6487
|
PickerController,
|
|
6488
|
+
RENDERED_QUALITY_REPORT_VERSION,
|
|
6034
6489
|
SUPPORTED_LOCALES,
|
|
6035
6490
|
SeatmapRenderer,
|
|
6036
6491
|
UNGROUPED_ID,
|
|
@@ -6058,6 +6513,7 @@ export {
|
|
|
6058
6513
|
generateSeatThumb,
|
|
6059
6514
|
getLocale,
|
|
6060
6515
|
hiddenObjectIds,
|
|
6516
|
+
inspectRenderedQualityEvidence,
|
|
6061
6517
|
isGaUnitLabel,
|
|
6062
6518
|
isSectionHidden,
|
|
6063
6519
|
layerOf,
|