@vectoriox/iox-builder 4.8.0 → 4.9.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.
|
@@ -16,7 +16,7 @@ import { SelectModule } from 'primeng/select';
|
|
|
16
16
|
import { TooltipModule } from 'primeng/tooltip';
|
|
17
17
|
import { DialogModule } from 'primeng/dialog';
|
|
18
18
|
import * as i17 from '@vectoriox/iox-ui';
|
|
19
|
-
import { DEFAULT_WIDTH_BY_TYPE, Breakpoint, BREAKPOINTS, countOverrides, setOverride, breakpointDef, breakpointForWidth, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, DeclarationContext, planResponsiveRules, resolveStyleProps, buildTargetedStateSelector, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, safeSetInput, composeVirtualTraits, stateNamesIn, collectContextSlotKeys, withContextSlots, nodeUsesContext, resolvePath, applyBindingPipes, IS_PREVIEW, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, IoxPageModule } from '@vectoriox/iox-ui';
|
|
19
|
+
import { DEFAULT_WIDTH_BY_TYPE, Breakpoint, BREAKPOINTS, countOverrides, setOverride, breakpointDef, breakpointForWidth, ContextStore, ComponentInstanceRegistryService, isActionEnabled, isContextAction, contextActionEffect, resolveContextValue, DeclarationContext, planResponsiveRules, resolveStyleProps, buildTargetedStateSelector, compileDeclarations, wrapInBreakpointQuery, BreakpointQueryKind, safeSetInput, composeVirtualTraits, stateNamesIn, resolveInputs, setInputOverride, collectContextSlotKeys, withContextSlots, nodeUsesContext, resolvePath, applyBindingPipes, IS_PREVIEW, planDataSource, dataSourcePlanToRequest, normalizeCollectionResult, resolveRepeaterItems, getByPath, computeRelative, IoxPageModule } from '@vectoriox/iox-ui';
|
|
20
20
|
export { ComponentInstanceRegistryService, VIRTUAL_TRAIT_KEYS, composeVirtualTraits } from '@vectoriox/iox-ui';
|
|
21
21
|
import Lenis from 'lenis';
|
|
22
22
|
import { Subject, BehaviorSubject, of, forkJoin } from 'rxjs';
|
|
@@ -1202,6 +1202,10 @@ var AuditCode;
|
|
|
1202
1202
|
AuditCode["TapTarget"] = "tap-target";
|
|
1203
1203
|
AuditCode["TinyText"] = "tiny-text";
|
|
1204
1204
|
AuditCode["UnboundedImage"] = "unbounded-image";
|
|
1205
|
+
/** A slider showing more slides than the width can carry — each one unreadably narrow. */
|
|
1206
|
+
AuditCode["SlidesOverflow"] = "slides-overflow";
|
|
1207
|
+
/** An out-of-flow node whose box lands outside the frame, so it is unreachable at this width. */
|
|
1208
|
+
AuditCode["OffscreenAbsolute"] = "offscreen-absolute";
|
|
1205
1209
|
})(AuditCode || (AuditCode = {}));
|
|
1206
1210
|
var AuditSeverity;
|
|
1207
1211
|
(function (AuditSeverity) {
|
|
@@ -1215,6 +1219,10 @@ const MIN_TAP_PX = 44;
|
|
|
1215
1219
|
const MIN_TEXT_PX = 12;
|
|
1216
1220
|
/** Types a visitor taps. */
|
|
1217
1221
|
const TAPPABLE = new Set(['Button', 'ButtonBlock', 'Link', 'LinkedContainer']);
|
|
1222
|
+
/** Narrower than this, a slide shows too little of its content to be worth showing at all. */
|
|
1223
|
+
const MIN_SLIDE_PX = 200;
|
|
1224
|
+
/** Out-of-flow positioning that can put a node outside the frame. */
|
|
1225
|
+
const OUT_OF_FLOW = new Set(['absolute', 'fixed']);
|
|
1218
1226
|
const nameOf = (node) => node.label || node.type;
|
|
1219
1227
|
const pxOf = (value) => {
|
|
1220
1228
|
const match = /^(-?\d+(?:\.\d+)?)px$/i.exec(String(value ?? ''));
|
|
@@ -1277,6 +1285,37 @@ function auditNodes(nodes, viewportWidth) {
|
|
|
1277
1285
|
fix: { height: 'auto', aspectRatio: '16 / 9' },
|
|
1278
1286
|
});
|
|
1279
1287
|
}
|
|
1288
|
+
// 6. A slider showing more slides than this width can carry. NOT a CSS problem: slide count
|
|
1289
|
+
// is a component trait, so the fix is a per-breakpoint TRAIT override (P3-G1), not a style.
|
|
1290
|
+
if (node.type === 'SliderContainer' && node.width > 0) {
|
|
1291
|
+
const perView = Number(node.inputs?.['slidesPerView']);
|
|
1292
|
+
if (Number.isFinite(perView) && perView > 1) {
|
|
1293
|
+
const slideWidth = node.width / perView;
|
|
1294
|
+
if (slideWidth < MIN_SLIDE_PX) {
|
|
1295
|
+
const fits = Math.max(1, Math.floor(node.width / MIN_SLIDE_PX));
|
|
1296
|
+
findings.push({
|
|
1297
|
+
nodeId: node.id, nodeName: name, code: AuditCode.SlidesOverflow, severity: AuditSeverity.Warning,
|
|
1298
|
+
message: `${name} shows ${perView} slides across ${Math.round(node.width)}px — ${Math.round(slideWidth)}px each. Show ${fits} at this width.`,
|
|
1299
|
+
inputFix: { slidesPerView: fits },
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
// 7. An out-of-flow node that has left the frame. Measured, not inferred: a negative offset
|
|
1305
|
+
// is legitimate design until it actually puts the content beyond the edge.
|
|
1306
|
+
if (OUT_OF_FLOW.has(String(styles['position'] ?? '')) && node.left !== undefined && node.width > 0) {
|
|
1307
|
+
const right = node.left + node.width;
|
|
1308
|
+
const offLeft = right <= 0.5;
|
|
1309
|
+
const offRight = node.left >= viewportWidth - 0.5;
|
|
1310
|
+
if (offLeft || offRight) {
|
|
1311
|
+
findings.push({
|
|
1312
|
+
nodeId: node.id, nodeName: name, code: AuditCode.OffscreenAbsolute, severity: AuditSeverity.Error,
|
|
1313
|
+
message: `${name} is positioned ${offLeft ? 'left of' : 'beyond'} the ${viewportWidth}px viewport — it cannot be seen or tapped at this width.`,
|
|
1314
|
+
// No blind auto-fix: the author's intent (a decorative overflow vs a mistake)
|
|
1315
|
+
// is not knowable from geometry. Selecting the node is the useful action.
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1280
1319
|
}
|
|
1281
1320
|
const rank = { [AuditSeverity.Error]: 0, [AuditSeverity.Warning]: 1, [AuditSeverity.Info]: 2 };
|
|
1282
1321
|
return findings
|
|
@@ -5607,6 +5646,9 @@ class BuilderComponent {
|
|
|
5607
5646
|
this.auditRunning = true;
|
|
5608
5647
|
const scale = this.viewportService.getScale() || 1;
|
|
5609
5648
|
const width = this.viewportService.getState().width;
|
|
5649
|
+
// Positions are reported relative to the canvas FRAME, not the editor window, so an
|
|
5650
|
+
// "off-screen" finding means off the simulated viewport rather than off the user's monitor.
|
|
5651
|
+
const frame = this.previewCanvas?.nativeElement?.getBoundingClientRect() ?? { left: 0, top: 0 };
|
|
5610
5652
|
const nodes = [];
|
|
5611
5653
|
const visit = (list) => {
|
|
5612
5654
|
for (const node of list) {
|
|
@@ -5618,8 +5660,13 @@ class BuilderComponent {
|
|
|
5618
5660
|
type: node.type,
|
|
5619
5661
|
label: node.label,
|
|
5620
5662
|
styleProps: resolveStyleProps(this.baseStyleMapOf(node), node.responsive, this.activeBreakpoint),
|
|
5663
|
+
// Traits resolved at this width too — some failures (a 4-up slider on a
|
|
5664
|
+
// phone) are invisible to a style-only audit. See P3-G1/G4.
|
|
5665
|
+
inputs: resolveInputs(this.baseInputMapOf(node), node.responsive, this.activeBreakpoint),
|
|
5621
5666
|
width: rect.width / scale,
|
|
5622
5667
|
height: rect.height / scale,
|
|
5668
|
+
left: (rect.left - frame.left) / scale,
|
|
5669
|
+
top: (rect.top - frame.top) / scale,
|
|
5623
5670
|
});
|
|
5624
5671
|
}
|
|
5625
5672
|
if (node.children?.length)
|
|
@@ -5632,10 +5679,38 @@ class BuilderComponent {
|
|
|
5632
5679
|
this.auditRunning = false;
|
|
5633
5680
|
this.cdr.detectChanges();
|
|
5634
5681
|
}
|
|
5682
|
+
/** The node's OWN trait values — the Base layer the responsive `inputs` cascade sits on top of. */
|
|
5683
|
+
baseInputMapOf(node) {
|
|
5684
|
+
const out = {};
|
|
5685
|
+
for (const trait of (node.traits ?? [])) {
|
|
5686
|
+
if (trait?.name !== undefined && trait.default !== undefined)
|
|
5687
|
+
out[trait.name] = trait.default;
|
|
5688
|
+
}
|
|
5689
|
+
return out;
|
|
5690
|
+
}
|
|
5635
5691
|
/** Apply one finding's fix into the current breakpoint's layer (or the base styles at Base). */
|
|
5636
5692
|
applyAuditFix(finding) {
|
|
5637
5693
|
const node = this.findNodeById(finding.nodeId, this.layout) ?? this.findNodeById(finding.nodeId, this.globalElements);
|
|
5638
|
-
if (!node
|
|
5694
|
+
if (!node)
|
|
5695
|
+
return;
|
|
5696
|
+
// A TRAIT fix (slide count) is not CSS — it lands in the breakpoint's `inputs` layer, or on
|
|
5697
|
+
// the node itself at Base, mirroring exactly how a style fix is applied below.
|
|
5698
|
+
if (finding.inputFix) {
|
|
5699
|
+
for (const [name, value] of Object.entries(finding.inputFix)) {
|
|
5700
|
+
if (this.activeBreakpoint === Breakpoint.Base) {
|
|
5701
|
+
const trait = (node.traits ?? []).find((t) => t.name === name);
|
|
5702
|
+
if (trait)
|
|
5703
|
+
trait.default = value;
|
|
5704
|
+
}
|
|
5705
|
+
else {
|
|
5706
|
+
node.responsive = setInputOverride(node.responsive, this.activeBreakpoint, name, value);
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
this.panelEventService.emit(PanelEventTypes.LAYOUT_CHANGED, this.layout);
|
|
5710
|
+
this.runAudit();
|
|
5711
|
+
return;
|
|
5712
|
+
}
|
|
5713
|
+
if (!finding.fix)
|
|
5639
5714
|
return;
|
|
5640
5715
|
if (this.activeBreakpoint === Breakpoint.Base) {
|
|
5641
5716
|
for (const [name, value] of Object.entries(finding.fix)) {
|
|
@@ -5664,7 +5739,7 @@ class BuilderComponent {
|
|
|
5664
5739
|
}
|
|
5665
5740
|
/** Apply every fixable finding, then re-measure. */
|
|
5666
5741
|
applyAllAuditFixes() {
|
|
5667
|
-
for (const finding of this.auditFindings.filter(f => !!f.fix))
|
|
5742
|
+
for (const finding of this.auditFindings.filter(f => !!f.fix || !!f.inputFix))
|
|
5668
5743
|
this.applyAuditFix(finding);
|
|
5669
5744
|
}
|
|
5670
5745
|
/** Select the node a finding is about. */
|