@ng-prism/core 22.3.0 → 22.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/dist/app/canvas/canvas-bg.styles.d.ts +7 -12
- package/dist/app/canvas/canvas-bg.styles.d.ts.map +1 -1
- package/dist/app/canvas/canvas-bg.styles.js +35 -37
- package/dist/app/canvas/measure-geometry.d.ts +140 -0
- package/dist/app/canvas/measure-geometry.d.ts.map +1 -0
- package/dist/app/canvas/measure-geometry.js +217 -0
- package/dist/app/canvas/measure-keyboard.d.ts +20 -0
- package/dist/app/canvas/measure-keyboard.d.ts.map +1 -0
- package/dist/app/canvas/measure-keyboard.js +34 -0
- package/dist/app/canvas/measure-quad.d.ts +114 -0
- package/dist/app/canvas/measure-quad.d.ts.map +1 -0
- package/dist/app/canvas/measure-quad.js +173 -0
- package/dist/app/canvas/measure-snap.d.ts +35 -0
- package/dist/app/canvas/measure-snap.d.ts.map +1 -0
- package/dist/app/canvas/measure-snap.js +63 -0
- package/dist/app/canvas/prism-canvas-measure.component.d.ts +142 -0
- package/dist/app/canvas/prism-canvas-measure.component.d.ts.map +1 -0
- package/dist/app/canvas/prism-canvas-measure.component.js +554 -0
- package/dist/app/canvas/prism-canvas-pin-row.component.d.ts +23 -0
- package/dist/app/canvas/prism-canvas-pin-row.component.d.ts.map +1 -0
- package/dist/app/canvas/prism-canvas-pin-row.component.js +64 -0
- package/dist/app/canvas/prism-canvas-toolbar.component.d.ts +22 -39
- package/dist/app/canvas/prism-canvas-toolbar.component.d.ts.map +1 -1
- package/dist/app/canvas/prism-canvas-toolbar.component.js +61 -69
- package/dist/app/canvas/prism-template-popover.component.js +1 -1
- package/dist/app/canvas/viewport-snap.d.ts +1 -1
- package/dist/app/canvas/viewport-snap.js +1 -1
- package/dist/app/component-head/head-metrics.d.ts +2 -2
- package/dist/app/component-head/head-metrics.js +1 -1
- package/dist/app/component-head/prism-component-head.component.d.ts +2 -2
- package/dist/app/component-head/prism-component-head.component.d.ts.map +1 -1
- package/dist/app/component-head/prism-component-head.component.js +11 -18
- package/dist/app/component-head/prism-head-gauge.component.js +1 -1
- package/dist/app/component-head/prism-head-info.component.d.ts +1 -1
- package/dist/app/component-head/prism-head-info.component.js +2 -2
- package/dist/app/component-head/prism-stat.component.d.ts +1 -1
- package/dist/app/component-head/prism-stat.component.js +2 -2
- package/dist/app/directives/prism-resizer.directive.d.ts +1 -1
- package/dist/app/directives/prism-resizer.directive.js +2 -2
- package/dist/app/directives/resize-value.d.ts +2 -2
- package/dist/app/directives/resize-value.js +2 -2
- package/dist/app/discovery-manifest.d.ts +2 -2
- package/dist/app/discovery-manifest.d.ts.map +1 -1
- package/dist/app/discovery-manifest.js +11 -15
- package/dist/app/header/prism-header.component.js +1 -1
- package/dist/app/icons/icon-registry.d.ts +2 -2
- package/dist/app/icons/icon-registry.d.ts.map +1 -1
- package/dist/app/icons/icon-registry.js +3 -2
- package/dist/app/icons/prism-icon.component.d.ts +2 -2
- package/dist/app/icons/prism-icon.component.js +4 -4
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +14 -21
- package/dist/app/overview/overview-visibility.d.ts +1 -1
- package/dist/app/overview/overview-visibility.js +1 -1
- package/dist/app/overview/prism-overview-cell.component.d.ts +2 -2
- package/dist/app/overview/prism-overview-cell.component.d.ts.map +1 -1
- package/dist/app/overview/prism-overview-cell.component.js +9 -10
- package/dist/app/overview/prism-overview.component.d.ts +2 -2
- package/dist/app/overview/prism-overview.component.js +2 -2
- package/dist/app/page-renderer/prism-page-renderer.component.js +1 -1
- package/dist/app/panels/a11y/a11y-audit.service.js +2 -2
- package/dist/app/panels/a11y/a11y-summary.d.ts +3 -4
- package/dist/app/panels/a11y/a11y-summary.d.ts.map +1 -1
- package/dist/app/panels/a11y/a11y-summary.js +3 -4
- package/dist/app/panels/a11y/a11y-thresholds.d.ts +1 -1
- package/dist/app/panels/a11y/a11y-thresholds.d.ts.map +1 -1
- package/dist/app/panels/a11y/a11y-thresholds.js +1 -1
- package/dist/app/panels/a11y/a11y.types.d.ts +1 -1
- package/dist/app/panels/a11y/a11y.types.d.ts.map +1 -1
- package/dist/app/panels/builtin-navigation-decorations.d.ts +1 -1
- package/dist/app/panels/builtin-navigation-decorations.js +1 -1
- package/dist/app/panels/controls/non-editable-input-types.js +1 -1
- package/dist/app/panels/panel-host/panel-badge.d.ts +1 -1
- package/dist/app/provide-prism.js +1 -1
- package/dist/app/renderer/known-inputs.d.ts +1 -1
- package/dist/app/renderer/known-inputs.js +1 -1
- package/dist/app/renderer/overlay-resolver.d.ts +17 -3
- package/dist/app/renderer/overlay-resolver.d.ts.map +1 -1
- package/dist/app/renderer/overlay-resolver.js +9 -4
- package/dist/app/renderer/prism-renderer.component.d.ts +3 -3
- package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
- package/dist/app/renderer/prism-renderer.component.js +25 -16
- package/dist/app/renderer/projectable-content.js +3 -3
- package/dist/app/services/prism-canvas.service.d.ts +12 -1
- package/dist/app/services/prism-canvas.service.d.ts.map +1 -1
- package/dist/app/services/prism-canvas.service.js +23 -5
- package/dist/app/services/prism-capture.service.d.ts +24 -50
- package/dist/app/services/prism-capture.service.d.ts.map +1 -1
- package/dist/app/services/prism-capture.service.js +60 -140
- package/dist/app/services/prism-measure.service.d.ts +44 -0
- package/dist/app/services/prism-measure.service.d.ts.map +1 -0
- package/dist/app/services/prism-measure.service.js +91 -0
- package/dist/app/services/prism-panel.service.d.ts +2 -2
- package/dist/app/services/prism-panel.service.js +2 -2
- package/dist/app/services/prism-persistence.service.js +1 -1
- package/dist/app/services/prism-renderer.service.js +1 -1
- package/dist/app/services/prism-url-state.service.js +1 -1
- package/dist/app/services/prism-variant-bg.service.d.ts +10 -24
- package/dist/app/services/prism-variant-bg.service.d.ts.map +1 -1
- package/dist/app/services/prism-variant-bg.service.js +10 -24
- package/dist/app/shared/prism-metric-badge.component.d.ts +9 -15
- package/dist/app/shared/prism-metric-badge.component.d.ts.map +1 -1
- package/dist/app/shared/prism-metric-badge.component.js +10 -16
- package/dist/app/shell/prism-shell.component.d.ts.map +1 -1
- package/dist/app/shell/prism-shell.component.js +12 -9
- package/dist/app/sidebar/navigation-decorations.d.ts +4 -4
- package/dist/app/sidebar/navigation-decorations.js +2 -2
- package/dist/app/sidebar/prism-sidebar.component.d.ts +2 -2
- package/dist/app/sidebar/prism-sidebar.component.js +3 -3
- package/dist/app/theme/prism-default-theme.js +3 -3
- package/dist/app/variant-ribbon/spectrum.d.ts +1 -1
- package/dist/app/variant-ribbon/spectrum.js +1 -1
- package/dist/app/view-tab-bar/next-view-id.d.ts +1 -1
- package/dist/app/view-tab-bar/next-view-id.js +1 -1
- package/dist/builder/a11y/a11y-report-reader.d.ts +1 -1
- package/dist/builder/a11y/a11y-report-reader.js +1 -1
- package/dist/builder/manifest/runtime-manifest.generator.js +6 -2
- package/dist/builder/plugin-runner/plugin-runner.js +1 -1
- package/dist/builder/scanner/ast-utils.d.ts +46 -2
- package/dist/builder/scanner/ast-utils.d.ts.map +1 -1
- package/dist/builder/scanner/ast-utils.js +337 -31
- package/dist/builder/scanner/class-hierarchy.d.ts +33 -0
- package/dist/builder/scanner/class-hierarchy.d.ts.map +1 -0
- package/dist/builder/scanner/class-hierarchy.js +87 -0
- package/dist/builder/scanner/component.scanner.d.ts +7 -1
- package/dist/builder/scanner/component.scanner.d.ts.map +1 -1
- package/dist/builder/scanner/component.scanner.js +106 -58
- package/dist/builder/scanner/input.extractor.d.ts +7 -4
- package/dist/builder/scanner/input.extractor.d.ts.map +1 -1
- package/dist/builder/scanner/input.extractor.js +153 -55
- package/dist/builder/scanner/manifest.generator.js +6 -2
- package/dist/builder/scanner/scanner.d.ts +3 -1
- package/dist/builder/scanner/scanner.d.ts.map +1 -1
- package/dist/builder/scanner/scanner.js +6 -4
- package/dist/builder/scanner/showcase-diagnostics.d.ts +25 -0
- package/dist/builder/scanner/showcase-diagnostics.d.ts.map +1 -0
- package/dist/builder/scanner/showcase-diagnostics.js +96 -0
- package/dist/builder/shared/prism-pipeline.d.ts +1 -1
- package/dist/builder/shared/prism-pipeline.d.ts.map +1 -1
- package/dist/builder/shared/prism-pipeline.js +12 -14
- package/dist/builder/watcher/prism-watcher.js +2 -2
- package/dist/decorator/showcase.types.d.ts +11 -9
- package/dist/decorator/showcase.types.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/plugin/define-config.js +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +5 -9
- package/dist/plugin/plugin.types.d.ts +30 -13
- package/dist/plugin/plugin.types.d.ts.map +1 -1
- package/dist/schematics/migrations/v22-0-cache-dir-rename/index.js +2 -2
- package/dist/schematics/ng-add/index.js +4 -4
- package/dist/shared/canvas-bg.type.d.ts +5 -6
- package/dist/shared/canvas-bg.type.d.ts.map +1 -1
- package/dist/shared/canvas-bg.type.js +4 -5
- package/dist/shared/measure.type.d.ts +52 -0
- package/dist/shared/measure.type.d.ts.map +1 -0
- package/dist/shared/measure.type.js +21 -0
- package/dist/shared/variant-bg.d.ts +20 -31
- package/dist/shared/variant-bg.d.ts.map +1 -1
- package/dist/shared/variant-bg.js +20 -31
- package/dist/shared/viewport.type.d.ts +15 -19
- package/dist/shared/viewport.type.d.ts.map +1 -1
- package/dist/shared/viewport.type.js +15 -19
- package/dist/transformer/showcase-strip.transformer.js +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { afterNextRender, ChangeDetectionStrategy, Component, computed, DestroyRef, ElementRef, inject, signal } from '@angular/core';
|
|
2
|
+
import { MEASURE_SNAP_TOLERANCE } from '../../shared/measure.type.js';
|
|
3
|
+
import { PrismCanvasService } from '../services/prism-canvas.service.js';
|
|
4
|
+
import { PrismMeasureService } from '../services/prism-measure.service.js';
|
|
5
|
+
import { PrismRendererService } from '../services/prism-renderer.service.js';
|
|
6
|
+
import { constrainDirection, formatMeasure, labelPlacement, measureDistance, quantizeFree, tickEndpoints, toHostSpace, toLocal, toScreen } from './measure-geometry.js';
|
|
7
|
+
import { nudge } from './measure-keyboard.js';
|
|
8
|
+
import { outlineOf, quadCentre, quadLines, quadSpans, quadSummary } from './measure-quad.js';
|
|
9
|
+
import { nearestSnap, readElementBox, snapTargetsFor } from './measure-snap.js';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
function PrismCanvasMeasureComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
12
|
+
i0.ɵɵnamespaceSVG();
|
|
13
|
+
i0.ɵɵdomElement(0, "rect", 7);
|
|
14
|
+
} if (rf & 2) {
|
|
15
|
+
const r_r1 = ctx;
|
|
16
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
17
|
+
i0.ɵɵclassProp("m-outline--preview", ctx_r1.hover().preview);
|
|
18
|
+
i0.ɵɵattribute("x", r_r1.x)("y", r_r1.y)("width", r_r1.width)("height", r_r1.height);
|
|
19
|
+
} }
|
|
20
|
+
function PrismCanvasMeasureComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
21
|
+
i0.ɵɵnamespaceSVG();
|
|
22
|
+
i0.ɵɵdomElement(0, "rect", 2);
|
|
23
|
+
} if (rf & 2) {
|
|
24
|
+
const r_r3 = ctx;
|
|
25
|
+
i0.ɵɵattribute("x", r_r3.x)("y", r_r3.y)("width", r_r3.width)("height", r_r3.height);
|
|
26
|
+
} }
|
|
27
|
+
function PrismCanvasMeasureComponent_For_4_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
28
|
+
i0.ɵɵnamespaceSVG();
|
|
29
|
+
i0.ɵɵdomElement(0, "line", 8);
|
|
30
|
+
} if (rf & 2) {
|
|
31
|
+
const e_r4 = ctx.$implicit;
|
|
32
|
+
i0.ɵɵattribute("x1", e_r4[0].x)("y1", e_r4[0].y)("x2", e_r4[1].x)("y2", e_r4[1].y);
|
|
33
|
+
} }
|
|
34
|
+
function PrismCanvasMeasureComponent_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
35
|
+
i0.ɵɵnamespaceSVG();
|
|
36
|
+
i0.ɵɵdomElementStart(0, "g");
|
|
37
|
+
i0.ɵɵdomElement(1, "line")(2, "line")(3, "line");
|
|
38
|
+
i0.ɵɵrepeaterCreate(4, PrismCanvasMeasureComponent_For_4_For_5_Template, 1, 4, ":svg:line", 8, i0.ɵɵrepeaterTrackByIndex);
|
|
39
|
+
i0.ɵɵdomElementEnd();
|
|
40
|
+
} if (rf & 2) {
|
|
41
|
+
const l_r5 = ctx.$implicit;
|
|
42
|
+
i0.ɵɵclassProp("is-pinned", l_r5.pinned);
|
|
43
|
+
i0.ɵɵadvance();
|
|
44
|
+
i0.ɵɵattribute("x1", l_r5.a.x)("y1", l_r5.a.y)("x2", l_r5.b.x)("y2", l_r5.b.y);
|
|
45
|
+
i0.ɵɵadvance();
|
|
46
|
+
i0.ɵɵattribute("x1", l_r5.tickA[0].x)("y1", l_r5.tickA[0].y)("x2", l_r5.tickA[1].x)("y2", l_r5.tickA[1].y);
|
|
47
|
+
i0.ɵɵadvance();
|
|
48
|
+
i0.ɵɵattribute("x1", l_r5.tickB[0].x)("y1", l_r5.tickB[0].y)("x2", l_r5.tickB[1].x)("y2", l_r5.tickB[1].y);
|
|
49
|
+
i0.ɵɵadvance();
|
|
50
|
+
i0.ɵɵrepeater(l_r5.echoes);
|
|
51
|
+
} }
|
|
52
|
+
function PrismCanvasMeasureComponent_For_6_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
53
|
+
i0.ɵɵnamespaceSVG();
|
|
54
|
+
i0.ɵɵdomElement(0, "line", 8);
|
|
55
|
+
} if (rf & 2) {
|
|
56
|
+
const e_r6 = ctx.$implicit;
|
|
57
|
+
i0.ɵɵattribute("x1", e_r6[0].x)("y1", e_r6[0].y)("x2", e_r6[1].x)("y2", e_r6[1].y);
|
|
58
|
+
} }
|
|
59
|
+
function PrismCanvasMeasureComponent_For_6_Template(rf, ctx) { if (rf & 1) {
|
|
60
|
+
i0.ɵɵnamespaceSVG();
|
|
61
|
+
i0.ɵɵdomElementStart(0, "g");
|
|
62
|
+
i0.ɵɵdomElement(1, "line")(2, "line")(3, "line");
|
|
63
|
+
i0.ɵɵrepeaterCreate(4, PrismCanvasMeasureComponent_For_6_For_5_Template, 1, 4, ":svg:line", 8, i0.ɵɵrepeaterTrackByIndex);
|
|
64
|
+
i0.ɵɵdomElementEnd();
|
|
65
|
+
} if (rf & 2) {
|
|
66
|
+
const l_r7 = ctx.$implicit;
|
|
67
|
+
i0.ɵɵclassProp("is-pinned", l_r7.pinned);
|
|
68
|
+
i0.ɵɵadvance();
|
|
69
|
+
i0.ɵɵattribute("x1", l_r7.a.x)("y1", l_r7.a.y)("x2", l_r7.b.x)("y2", l_r7.b.y);
|
|
70
|
+
i0.ɵɵadvance();
|
|
71
|
+
i0.ɵɵattribute("x1", l_r7.tickA[0].x)("y1", l_r7.tickA[0].y)("x2", l_r7.tickA[1].x)("y2", l_r7.tickA[1].y);
|
|
72
|
+
i0.ɵɵadvance();
|
|
73
|
+
i0.ɵɵattribute("x1", l_r7.tickB[0].x)("y1", l_r7.tickB[0].y)("x2", l_r7.tickB[1].x)("y2", l_r7.tickB[1].y);
|
|
74
|
+
i0.ɵɵadvance();
|
|
75
|
+
i0.ɵɵrepeater(l_r7.echoes);
|
|
76
|
+
} }
|
|
77
|
+
function PrismCanvasMeasureComponent_For_9_Template(rf, ctx) { if (rf & 1) {
|
|
78
|
+
i0.ɵɵdomElementStart(0, "span", 9);
|
|
79
|
+
i0.ɵɵtext(1);
|
|
80
|
+
i0.ɵɵdomElementEnd();
|
|
81
|
+
} if (rf & 2) {
|
|
82
|
+
const l_r8 = ctx.$implicit;
|
|
83
|
+
i0.ɵɵstyleProp("left", l_r8.label.x, "px")("top", l_r8.label.y, "px");
|
|
84
|
+
i0.ɵɵclassProp("is-pinned", l_r8.pinned);
|
|
85
|
+
i0.ɵɵadvance();
|
|
86
|
+
i0.ɵɵtextInterpolate1(" ", l_r8.text, " ");
|
|
87
|
+
} }
|
|
88
|
+
function PrismCanvasMeasureComponent_For_11_Template(rf, ctx) { if (rf & 1) {
|
|
89
|
+
i0.ɵɵdomElementStart(0, "span", 9);
|
|
90
|
+
i0.ɵɵtext(1);
|
|
91
|
+
i0.ɵɵdomElementEnd();
|
|
92
|
+
} if (rf & 2) {
|
|
93
|
+
const l_r9 = ctx.$implicit;
|
|
94
|
+
i0.ɵɵstyleProp("left", l_r9.label.x, "px")("top", l_r9.label.y, "px");
|
|
95
|
+
i0.ɵɵclassProp("is-pinned", l_r9.pinned);
|
|
96
|
+
i0.ɵɵadvance();
|
|
97
|
+
i0.ɵɵtextInterpolate1(" ", l_r9.text, " ");
|
|
98
|
+
} }
|
|
99
|
+
function PrismCanvasMeasureComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
100
|
+
i0.ɵɵtext(0);
|
|
101
|
+
} if (rf & 2) {
|
|
102
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
103
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.hover().summary, " ");
|
|
104
|
+
} }
|
|
105
|
+
function PrismCanvasMeasureComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
106
|
+
i0.ɵɵtext(0);
|
|
107
|
+
} if (rf & 2) {
|
|
108
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
109
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.lines()[ctx_r1.lines().length - 1].text, " ");
|
|
110
|
+
} }
|
|
111
|
+
/**
|
|
112
|
+
* The topmost element under a screen point that belongs to the specimen.
|
|
113
|
+
*
|
|
114
|
+
* `elementsFromPoint`, the plural one, and the loop are what make this work
|
|
115
|
+
* at all. The overlay host sits above `.demo-wrap` (see the `z-index` in the
|
|
116
|
+
* stylesheet and why it has to be there) and is the one layer that takes
|
|
117
|
+
* pointer events across the whole stage — so the singular `elementFromPoint`
|
|
118
|
+
* answers the overlay itself for every point over the specimen, `contains`
|
|
119
|
+
* then rejects it, and snapping and alt-click anchoring both stop working
|
|
120
|
+
* entirely. Walking the hit list past the overlay's own subtree is what
|
|
121
|
+
* gives the element actually underneath it.
|
|
122
|
+
*
|
|
123
|
+
* The `contains` check stays, and is still not optional: the toolrail and
|
|
124
|
+
* the viewport grips sit *above* the overlay, so they appear in the hit list
|
|
125
|
+
* ahead of anything in the specimen. Only the first hit the rendered root
|
|
126
|
+
* owns is the one the tool may latch onto.
|
|
127
|
+
*/
|
|
128
|
+
export function elementUnderPoint(x, y, root) {
|
|
129
|
+
if (!root)
|
|
130
|
+
return null;
|
|
131
|
+
for (const hit of document.elementsFromPoint(x, y)) {
|
|
132
|
+
if (root.contains(hit))
|
|
133
|
+
return hit;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* A snap's edge as a line segment, crosswise to the edge's axis.
|
|
139
|
+
*
|
|
140
|
+
* The length is deliberately fixed rather than the full edge length: an echo
|
|
141
|
+
* that retraces the whole element edge reads as a second measurement instead
|
|
142
|
+
* of a footnote to the first.
|
|
143
|
+
*/
|
|
144
|
+
const ECHO_HALF_LENGTH = 14;
|
|
145
|
+
export function echoFor(point, screen) {
|
|
146
|
+
if (!point.snap)
|
|
147
|
+
return null;
|
|
148
|
+
const horizontal = point.snap.side === 'top' || point.snap.side === 'bottom';
|
|
149
|
+
return horizontal
|
|
150
|
+
? [
|
|
151
|
+
{ x: screen.x - ECHO_HALF_LENGTH, y: screen.y },
|
|
152
|
+
{ x: screen.x + ECHO_HALF_LENGTH, y: screen.y }
|
|
153
|
+
]
|
|
154
|
+
: [
|
|
155
|
+
{ x: screen.x, y: screen.y - ECHO_HALF_LENGTH },
|
|
156
|
+
{ x: screen.x, y: screen.y + ECHO_HALF_LENGTH }
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Wires the two triggers that tell `lines` its drawn geometry might be
|
|
161
|
+
* stale, and returns how to undo them.
|
|
162
|
+
*
|
|
163
|
+
* A `ResizeObserver` on `stage` and `wrap` covers window resize, panel
|
|
164
|
+
* resize, and the specimen itself changing size. Resize alone is not enough
|
|
165
|
+
* because scrolling moves the specimen without resizing anything — a
|
|
166
|
+
* `ResizeObserver` does not fire on scroll — so `stage` also gets its own
|
|
167
|
+
* `scroll` listener. Dropping either one leaves the lines drawn at a screen
|
|
168
|
+
* position the specimen has since left, which is the worst impression a
|
|
169
|
+
* measuring tool can make.
|
|
170
|
+
*
|
|
171
|
+
* Takes the `ResizeObserver` constructor as a parameter instead of reading
|
|
172
|
+
* the global directly so the wiring stays testable without a real one:
|
|
173
|
+
* jsdom does not implement `ResizeObserver` at all, the same gap
|
|
174
|
+
* `elementsFromPoint` has above.
|
|
175
|
+
*/
|
|
176
|
+
export function watchGeometry(stage, wrap, onChange, ResizeObserverCtor = ResizeObserver) {
|
|
177
|
+
const ro = new ResizeObserverCtor(onChange);
|
|
178
|
+
ro.observe(stage);
|
|
179
|
+
if (wrap)
|
|
180
|
+
ro.observe(wrap);
|
|
181
|
+
stage.addEventListener('scroll', onChange);
|
|
182
|
+
return () => {
|
|
183
|
+
ro.disconnect();
|
|
184
|
+
stage.removeEventListener('scroll', onChange);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const EMPTY_HOVER = { lines: [], summary: '', anchor: null, target: null, preview: false };
|
|
188
|
+
/**
|
|
189
|
+
* Keyboard support here only operates a draft that already exists: nudge,
|
|
190
|
+
* commit, cancel. There is deliberately no keyboard path to *start* one —
|
|
191
|
+
* Spec §10 calls for Tab to step through the specimen's snap targets as the
|
|
192
|
+
* accessible substitute for a pointer-down, and that is not built. Tab is
|
|
193
|
+
* left strictly alone by `onKey` (see its doc, and `nudge`'s) precisely so
|
|
194
|
+
* it stays free for that mechanism once it exists, rather than being
|
|
195
|
+
* repurposed here as a stand-in. The `aria-label` below only promises what
|
|
196
|
+
* this component actually does today; it does not mention Tab, because
|
|
197
|
+
* nothing happens when a user presses it. What the real interaction should
|
|
198
|
+
* be is still undecided — a tool covering the whole canvas cannot simply
|
|
199
|
+
* grab Tab for its own stepping without first deciding how a keyboard user
|
|
200
|
+
* gets back out, and that is a product decision, not one this task makes.
|
|
201
|
+
*/
|
|
202
|
+
export class PrismCanvasMeasureComponent {
|
|
203
|
+
el = inject((ElementRef));
|
|
204
|
+
destroyRef = inject(DestroyRef);
|
|
205
|
+
canvas = inject(PrismCanvasService);
|
|
206
|
+
renderer = inject(PrismRendererService);
|
|
207
|
+
measure = inject(PrismMeasureService);
|
|
208
|
+
/**
|
|
209
|
+
* Bumped whenever geometry `lines` depends on might have moved without
|
|
210
|
+
* touching zoom, pins or the draft — see {@link watchGeometry} below.
|
|
211
|
+
*
|
|
212
|
+
* `getBoundingClientRect()` inside `origin()`, `hostRect()`,
|
|
213
|
+
* `specimenCentre()` and `boxOf()` is not itself a reactive signal:
|
|
214
|
+
* without an explicit nudge like this one, nothing would tell `lines` to
|
|
215
|
+
* re-run after a scroll or a resize, and the drawn lines would stay put
|
|
216
|
+
* while the specimen moves out from under them. It covers the host's own
|
|
217
|
+
* rect too, so reading that per evaluation costs no new trigger.
|
|
218
|
+
*/
|
|
219
|
+
geometryTick = signal(0, /* @ts-ignore */
|
|
220
|
+
...(ngDevMode ? [{ debugName: "geometryTick" }] : /* istanbul ignore next */ []));
|
|
221
|
+
/** Origin and zoom the projection runs from, in viewport coordinates. */
|
|
222
|
+
origin() {
|
|
223
|
+
const wrap = this.el.nativeElement.parentElement?.querySelector('.demo-wrap');
|
|
224
|
+
const r = wrap?.getBoundingClientRect();
|
|
225
|
+
return { x: r?.left ?? 0, y: r?.top ?? 0 };
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The host's own rect — what every drawn coordinate is measured against.
|
|
229
|
+
*
|
|
230
|
+
* Read once per `lines()` / `hover()` evaluation and handed to
|
|
231
|
+
* {@link toHostSpace}, which carries the full explanation of why the
|
|
232
|
+
* subtraction exists at all.
|
|
233
|
+
*/
|
|
234
|
+
hostRect() {
|
|
235
|
+
return this.el.nativeElement.getBoundingClientRect();
|
|
236
|
+
}
|
|
237
|
+
lines = computed(() => {
|
|
238
|
+
const zoom = this.canvas.zoom();
|
|
239
|
+
// Read for the dependency, not the value — the same way
|
|
240
|
+
// prism-canvas-rulers.component.ts forces a dependency on
|
|
241
|
+
// themeService.theme() without using it. This is what makes a
|
|
242
|
+
// scroll or a resize re-run this computed.
|
|
243
|
+
this.geometryTick();
|
|
244
|
+
const host = this.hostRect();
|
|
245
|
+
const origin = this.origin();
|
|
246
|
+
const centre = this.specimenCentre(host);
|
|
247
|
+
const all = [...this.measure.pins().map((m) => ({ m, pinned: true })), ...(this.measure.draft() ? [{ m: this.measure.draft(), pinned: false }] : [])];
|
|
248
|
+
return all.map(({ m, pinned }) => {
|
|
249
|
+
// toScreen answers in viewport coordinates, because that is the
|
|
250
|
+
// space `origin` lives in and the space `pointAt` reads the
|
|
251
|
+
// pointer in. toHostSpace is what turns that into the SVG's own
|
|
252
|
+
// user coordinates; everything downstream — ticks, label, echoes
|
|
253
|
+
// — is derived from the projected points and so follows.
|
|
254
|
+
const a = toHostSpace(toScreen(m.a, origin, zoom), host);
|
|
255
|
+
const b = toHostSpace(toScreen(m.b, origin, zoom), host);
|
|
256
|
+
return {
|
|
257
|
+
a,
|
|
258
|
+
b,
|
|
259
|
+
tickA: tickEndpoints(a, b, 'a'),
|
|
260
|
+
tickB: tickEndpoints(a, b, 'b'),
|
|
261
|
+
label: labelPlacement(a, b, centre),
|
|
262
|
+
text: `${formatMeasure(measureDistance(m.a, m.b))} px`,
|
|
263
|
+
pinned,
|
|
264
|
+
echoes: [echoFor(m.a, a), echoFor(m.b, b)].filter((e) => e !== null)
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
}, /* @ts-ignore */
|
|
268
|
+
...(ngDevMode ? [{ debugName: "lines" }] : /* istanbul ignore next */ []));
|
|
269
|
+
/**
|
|
270
|
+
* Everything the alt-hover mode draws, in one evaluation.
|
|
271
|
+
*
|
|
272
|
+
* One computed rather than three because all of it comes out of the same
|
|
273
|
+
* two `getBoundingClientRect()` reads, and splitting it would read the
|
|
274
|
+
* anchor's box once per consumer on every change detection pass.
|
|
275
|
+
*/
|
|
276
|
+
hover = computed(() => {
|
|
277
|
+
const anchorEl = this.measure.hoverAnchor();
|
|
278
|
+
const targetEl = this.hoverTarget();
|
|
279
|
+
// Same dependency-only read that `lines` above makes on
|
|
280
|
+
// `geometryTick`. `boxOf` reads `getBoundingClientRect()`, which is
|
|
281
|
+
// not itself reactive: holding the pointer still over an unchanged
|
|
282
|
+
// target does not re-set `hoverTarget` to a new value, so without
|
|
283
|
+
// this a scroll or a resize during that hold would leave the readout
|
|
284
|
+
// stale.
|
|
285
|
+
this.geometryTick();
|
|
286
|
+
// No anchor yet: outline whatever a click would take, so the choice is
|
|
287
|
+
// visible before it is made rather than after. Marked as a prediction
|
|
288
|
+
// so it draws fainter — at equal weight the click would change nothing
|
|
289
|
+
// on screen, the outline simply ceasing to follow the pointer, which is
|
|
290
|
+
// the same missing confirmation the anchor outline exists to provide.
|
|
291
|
+
if (!anchorEl) {
|
|
292
|
+
if (!targetEl)
|
|
293
|
+
return EMPTY_HOVER;
|
|
294
|
+
return { ...EMPTY_HOVER, anchor: outlineOf(this.boxOf(targetEl, this.hostRect())), preview: true };
|
|
295
|
+
}
|
|
296
|
+
const zoom = this.canvas.zoom();
|
|
297
|
+
const host = this.hostRect();
|
|
298
|
+
const a = this.boxOf(anchorEl, host);
|
|
299
|
+
// Spec §4.5: the anchor is outlined dashed from the moment it is
|
|
300
|
+
// set, before anything is hovered. Without it an Alt-click produces
|
|
301
|
+
// no visible change whatsoever — and hovering the anchor back to
|
|
302
|
+
// check cannot confirm it either, because that case deliberately
|
|
303
|
+
// draws no spans. The outline is the only confirmation the anchor
|
|
304
|
+
// registered, and it is also what tells the two ends of a single
|
|
305
|
+
// one-sided reading apart.
|
|
306
|
+
const anchor = outlineOf(a);
|
|
307
|
+
if (!targetEl || anchorEl === targetEl)
|
|
308
|
+
return { ...EMPTY_HOVER, anchor };
|
|
309
|
+
const t = this.boxOf(targetEl, host);
|
|
310
|
+
// `quadSpans` already drops a side with nothing to say, so an anchor
|
|
311
|
+
// and target that merely overlap correctly renders nothing at all —
|
|
312
|
+
// see its doc on `measure-quad.ts`. The DOM-bound half (boxOf) stops
|
|
313
|
+
// here; quadSpans/quadCentre/quadLines/quadSummary are all pure and tested
|
|
314
|
+
// directly in measure-quad.spec.ts.
|
|
315
|
+
const spans = quadSpans(a, t);
|
|
316
|
+
return {
|
|
317
|
+
lines: quadLines(spans, quadCentre(a, t), zoom),
|
|
318
|
+
summary: quadSummary(spans, zoom),
|
|
319
|
+
anchor,
|
|
320
|
+
// Solid against the anchor's dashed outline, per Spec §4.5.
|
|
321
|
+
target: outlineOf(t),
|
|
322
|
+
// The anchor is real by this point, not a prediction.
|
|
323
|
+
preview: false
|
|
324
|
+
};
|
|
325
|
+
}, /* @ts-ignore */
|
|
326
|
+
...(ngDevMode ? [{ debugName: "hover" }] : /* istanbul ignore next */ []));
|
|
327
|
+
/** The element under the pointer while alt-hovering, `null` otherwise. */
|
|
328
|
+
hoverTarget = signal(null, /* @ts-ignore */
|
|
329
|
+
...(ngDevMode ? [{ debugName: "hoverTarget" }] : /* istanbul ignore next */ []));
|
|
330
|
+
/**
|
|
331
|
+
* An element's current rectangle as a {@link Box}, already in host
|
|
332
|
+
* space.
|
|
333
|
+
*
|
|
334
|
+
* Projected here rather than at the four call sites downstream: a box
|
|
335
|
+
* that reached `quadSpans` in viewport coordinates would produce correct
|
|
336
|
+
* *distances* — a constant offset cancels in a subtraction — and draw
|
|
337
|
+
* them in the wrong place, which is the half that is actually visible.
|
|
338
|
+
*/
|
|
339
|
+
boxOf(element, host) {
|
|
340
|
+
const r = element.getBoundingClientRect();
|
|
341
|
+
const topLeft = toHostSpace({ x: r.left, y: r.top }, host);
|
|
342
|
+
const bottomRight = toHostSpace({ x: r.right, y: r.bottom }, host);
|
|
343
|
+
return { left: topLeft.x, top: topLeft.y, right: bottomRight.x, bottom: bottomRight.y };
|
|
344
|
+
}
|
|
345
|
+
specimenCentre(host) {
|
|
346
|
+
const r = this.renderer.renderedElement()?.getBoundingClientRect();
|
|
347
|
+
return r ? toHostSpace({ x: r.left + r.width / 2, y: r.top + r.height / 2 }, host) : { x: 0, y: 0 };
|
|
348
|
+
}
|
|
349
|
+
/** A pointer point, snapped to the nearest edge, in local coordinates. */
|
|
350
|
+
pointAt(clientX, clientY) {
|
|
351
|
+
const zoom = this.canvas.zoom();
|
|
352
|
+
const origin = this.origin();
|
|
353
|
+
const hit = elementUnderPoint(clientX, clientY, this.renderer.renderedElement());
|
|
354
|
+
const screen = { x: clientX, y: clientY };
|
|
355
|
+
// Every return below is quantized, and the asymmetry inside
|
|
356
|
+
// `quantizeFree` is the reason it is applied here rather than at the
|
|
357
|
+
// call site: only one axis of a snapped point comes from the document,
|
|
358
|
+
// and this is the only place that knows which.
|
|
359
|
+
if (!hit)
|
|
360
|
+
return quantizeFree({ ...toLocal(screen, origin, zoom), snap: null });
|
|
361
|
+
const target = nearestSnap(screen, snapTargetsFor(readElementBox(hit)), MEASURE_SNAP_TOLERANCE);
|
|
362
|
+
if (!target)
|
|
363
|
+
return quantizeFree({ ...toLocal(screen, origin, zoom), snap: null });
|
|
364
|
+
const snapped = target.axis === 'x' ? { x: target.at, y: clientY } : { x: clientX, y: target.at };
|
|
365
|
+
return quantizeFree({ ...toLocal(snapped, origin, zoom), snap: { kind: target.kind, side: target.side, from: hit } });
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Keyboard operation of the running draft.
|
|
369
|
+
*
|
|
370
|
+
* `Escape` and `Enter` act regardless of what `nudge` would say about the
|
|
371
|
+
* key; everything else only matters while a draft is open.
|
|
372
|
+
* `preventDefault` is called only on the branches that actually consumed
|
|
373
|
+
* the key — never for `Tab`, which `nudge` returns `null` for and this
|
|
374
|
+
* method then leaves untouched. Swallowing it here would break keyboard
|
|
375
|
+
* navigation of the whole shell for the sake of a tool that, as shipped,
|
|
376
|
+
* has nothing of its own to put Tab to — see `nudge`'s doc.
|
|
377
|
+
*/
|
|
378
|
+
onKey(event) {
|
|
379
|
+
if (event.key === 'Escape') {
|
|
380
|
+
this.measure.cancelDraft();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (event.key === 'Enter') {
|
|
384
|
+
this.measure.commitDraft();
|
|
385
|
+
event.preventDefault();
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const draft = this.measure.draft();
|
|
389
|
+
if (!draft)
|
|
390
|
+
return;
|
|
391
|
+
const moved = nudge(draft.b, event.key, event.shiftKey);
|
|
392
|
+
if (!moved)
|
|
393
|
+
return;
|
|
394
|
+
this.measure.updateDraft({ ...moved, snap: null });
|
|
395
|
+
event.preventDefault();
|
|
396
|
+
}
|
|
397
|
+
constructor() {
|
|
398
|
+
const host = this.el.nativeElement;
|
|
399
|
+
const down = (e) => {
|
|
400
|
+
// Primary button only, and the same failure
|
|
401
|
+
// `prism-resizer.directive.ts` guards against in its own
|
|
402
|
+
// `onMouseDown`: a right-click arms the drag, the context menu
|
|
403
|
+
// then takes the pointer so the matching `pointerup` never
|
|
404
|
+
// arrives, and the draft stays open afterwards with no button
|
|
405
|
+
// held. From there `move`'s `if (!this.measure.draft()) return;`
|
|
406
|
+
// protects nothing any more and the measurement simply follows
|
|
407
|
+
// the cursor. `preventDefault` does not suppress `contextmenu`,
|
|
408
|
+
// so the guard has to be the button check.
|
|
409
|
+
if (e.button !== 0)
|
|
410
|
+
return;
|
|
411
|
+
// Held Alt: fix the element under the pointer as the hover
|
|
412
|
+
// anchor instead of starting a drag measurement.
|
|
413
|
+
if (e.altKey) {
|
|
414
|
+
this.measure.setHoverAnchor(elementUnderPoint(e.clientX, e.clientY, this.renderer.renderedElement()));
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// Stops the drag from selecting the specimen's text and painting
|
|
418
|
+
// a selection highlight across the very thing being measured.
|
|
419
|
+
// `user-select: none` on the host does not cover it: the
|
|
420
|
+
// selection starts on the specimen underneath, not on the
|
|
421
|
+
// overlay.
|
|
422
|
+
e.preventDefault();
|
|
423
|
+
// …which also suppresses the compatibility `mousedown`, and with
|
|
424
|
+
// it the focus the host would otherwise have received from the
|
|
425
|
+
// click. Without this line the keyboard handling below becomes
|
|
426
|
+
// unreachable for anyone who starts with the pointer, which is
|
|
427
|
+
// everyone — a measurement cannot be started any other way.
|
|
428
|
+
host.focus({ preventScroll: true });
|
|
429
|
+
this.measure.beginDraft(this.pointAt(e.clientX, e.clientY));
|
|
430
|
+
host.setPointerCapture(e.pointerId);
|
|
431
|
+
};
|
|
432
|
+
const move = (e) => {
|
|
433
|
+
// Held Alt: track the element under the pointer as the hover
|
|
434
|
+
// target instead of updating a drag measurement in progress.
|
|
435
|
+
if (e.altKey) {
|
|
436
|
+
this.hoverTarget.set(elementUnderPoint(e.clientX, e.clientY, this.renderer.renderedElement()));
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
this.hoverTarget.set(null);
|
|
440
|
+
const draft = this.measure.draft();
|
|
441
|
+
if (!draft)
|
|
442
|
+
return;
|
|
443
|
+
const point = this.pointAt(e.clientX, e.clientY);
|
|
444
|
+
// Shift pulls the measurement onto the nearest of the eight
|
|
445
|
+
// 45-degree directions, the same service it performs in Photoshop,
|
|
446
|
+
// Illustrator and Figma. It runs on the already-snapped point
|
|
447
|
+
// rather than instead of it, so a latched edge still contributes
|
|
448
|
+
// on whichever axis the constraint preserves — see
|
|
449
|
+
// `constrainDirection`. Releasing Shift frees the drag again
|
|
450
|
+
// immediately, mid-drag included, because nothing about the
|
|
451
|
+
// constraint is stored.
|
|
452
|
+
this.measure.updateDraft(e.shiftKey ? constrainDirection(draft.a, point) : point);
|
|
453
|
+
};
|
|
454
|
+
const up = () => {
|
|
455
|
+
const draft = this.measure.draft();
|
|
456
|
+
if (!draft)
|
|
457
|
+
return;
|
|
458
|
+
// A pointer-down that never moved is a click, not a drag.
|
|
459
|
+
// `beginDraft` seeds `b` with `a`, so the two stay exactly equal
|
|
460
|
+
// until the first `pointermove` — and committing that pins a
|
|
461
|
+
// `0 px` chip the user then has to dismiss by hand. A finished
|
|
462
|
+
// drag being a pin is the design; a bare click is not a drag.
|
|
463
|
+
if (draft.a.x === draft.b.x && draft.a.y === draft.b.y) {
|
|
464
|
+
this.measure.cancelDraft();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
this.measure.commitDraft();
|
|
468
|
+
};
|
|
469
|
+
// A cancelled pointer — the browser taking it for a scroll or a
|
|
470
|
+
// gesture, the device going away — otherwise leaves the draft open
|
|
471
|
+
// for ever, the same end state the missing button guard above
|
|
472
|
+
// produced by another route.
|
|
473
|
+
const cancel = () => this.measure.cancelDraft();
|
|
474
|
+
host.addEventListener('pointerdown', down);
|
|
475
|
+
host.addEventListener('pointermove', move);
|
|
476
|
+
host.addEventListener('pointerup', up);
|
|
477
|
+
host.addEventListener('pointercancel', cancel);
|
|
478
|
+
this.destroyRef.onDestroy(() => {
|
|
479
|
+
host.removeEventListener('pointerdown', down);
|
|
480
|
+
host.removeEventListener('pointermove', move);
|
|
481
|
+
host.removeEventListener('pointerup', up);
|
|
482
|
+
host.removeEventListener('pointercancel', cancel);
|
|
483
|
+
});
|
|
484
|
+
// Deferred the same way box-model-overlay.component.ts defers its
|
|
485
|
+
// own read of `parentElement`: the host is not reliably attached to
|
|
486
|
+
// its final place in the stage yet while the constructor is running.
|
|
487
|
+
afterNextRender(() => {
|
|
488
|
+
const stage = host.parentElement;
|
|
489
|
+
if (!stage)
|
|
490
|
+
return;
|
|
491
|
+
const wrap = stage.querySelector('.demo-wrap');
|
|
492
|
+
this.destroyRef.onDestroy(watchGeometry(stage, wrap, () => this.geometryTick.update((v) => v + 1)));
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
static ɵfac = function PrismCanvasMeasureComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismCanvasMeasureComponent)(); };
|
|
496
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismCanvasMeasureComponent, selectors: [["prism-canvas-measure"]], hostAttrs: ["tabindex", "0", "role", "group", "aria-label", "Measure tool. Arrow keys nudge the active point, Shift for a larger step, Enter commits, Escape clears."], hostBindings: function PrismCanvasMeasureComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
497
|
+
i0.ɵɵlistener("keydown", function PrismCanvasMeasureComponent_keydown_HostBindingHandler($event) { return ctx.onKey($event); });
|
|
498
|
+
} }, decls: 15, vars: 3, consts: [["aria-hidden", "true", 1, "m-svg"], [1, "m-outline", "m-outline--anchor", 3, "m-outline--preview"], [1, "m-outline", "m-outline--target"], [3, "is-pinned"], [1, "m-labels"], [1, "m-label", 3, "is-pinned", "left", "top"], ["role", "status", "aria-live", "polite", 1, "m-live"], [1, "m-outline", "m-outline--anchor"], [1, "m-echo"], [1, "m-label"]], template: function PrismCanvasMeasureComponent_Template(rf, ctx) { if (rf & 1) {
|
|
499
|
+
i0.ɵɵnamespaceSVG();
|
|
500
|
+
i0.ɵɵdomElementStart(0, "svg", 0);
|
|
501
|
+
i0.ɵɵconditionalCreate(1, PrismCanvasMeasureComponent_Conditional_1_Template, 1, 6, ":svg:rect", 1);
|
|
502
|
+
i0.ɵɵconditionalCreate(2, PrismCanvasMeasureComponent_Conditional_2_Template, 1, 4, ":svg:rect", 2);
|
|
503
|
+
i0.ɵɵrepeaterCreate(3, PrismCanvasMeasureComponent_For_4_Template, 6, 14, ":svg:g", 3, i0.ɵɵrepeaterTrackByIndex);
|
|
504
|
+
i0.ɵɵrepeaterCreate(5, PrismCanvasMeasureComponent_For_6_Template, 6, 14, ":svg:g", 3, i0.ɵɵrepeaterTrackByIndex);
|
|
505
|
+
i0.ɵɵdomElementEnd();
|
|
506
|
+
i0.ɵɵnamespaceHTML();
|
|
507
|
+
i0.ɵɵdomElementStart(7, "div", 4);
|
|
508
|
+
i0.ɵɵrepeaterCreate(8, PrismCanvasMeasureComponent_For_9_Template, 2, 7, "span", 5, i0.ɵɵrepeaterTrackByIndex);
|
|
509
|
+
i0.ɵɵrepeaterCreate(10, PrismCanvasMeasureComponent_For_11_Template, 2, 7, "span", 5, i0.ɵɵrepeaterTrackByIndex);
|
|
510
|
+
i0.ɵɵdomElementEnd();
|
|
511
|
+
i0.ɵɵdomElementStart(12, "span", 6);
|
|
512
|
+
i0.ɵɵconditionalCreate(13, PrismCanvasMeasureComponent_Conditional_13_Template, 1, 1)(14, PrismCanvasMeasureComponent_Conditional_14_Template, 1, 1);
|
|
513
|
+
i0.ɵɵdomElementEnd();
|
|
514
|
+
} if (rf & 2) {
|
|
515
|
+
let tmp_0_0;
|
|
516
|
+
let tmp_1_0;
|
|
517
|
+
i0.ɵɵadvance();
|
|
518
|
+
i0.ɵɵconditional((tmp_0_0 = ctx.hover().anchor) ? 1 : -1, tmp_0_0);
|
|
519
|
+
i0.ɵɵadvance();
|
|
520
|
+
i0.ɵɵconditional((tmp_1_0 = ctx.hover().target) ? 2 : -1, tmp_1_0);
|
|
521
|
+
i0.ɵɵadvance();
|
|
522
|
+
i0.ɵɵrepeater(ctx.lines());
|
|
523
|
+
i0.ɵɵadvance(2);
|
|
524
|
+
i0.ɵɵrepeater(ctx.hover().lines);
|
|
525
|
+
i0.ɵɵadvance(3);
|
|
526
|
+
i0.ɵɵrepeater(ctx.lines());
|
|
527
|
+
i0.ɵɵadvance(2);
|
|
528
|
+
i0.ɵɵrepeater(ctx.hover().lines);
|
|
529
|
+
i0.ɵɵadvance(3);
|
|
530
|
+
i0.ɵɵconditional(ctx.hover().summary ? 13 : ctx.lines().length > 0 ? 14 : -1);
|
|
531
|
+
} }, styles: ["\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n[_nghost-%COMP%] {\n position: absolute;\n inset: 0;\n z-index: 1;\n cursor: crosshair;\n\n \n\n\n\n user-select: none;\n}\n\n.m-svg[_ngcontent-%COMP%], \n.m-labels[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n\n\n\n\n\n\n\n\n\n.m-svg[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n}\n\n.m-svg[_ngcontent-%COMP%] line[_ngcontent-%COMP%] {\n stroke: var(--%NS%prism-measure);\n stroke-width: 1;\n stroke-linecap: round;\n}\n\n.m-svg[_ngcontent-%COMP%] .is-pinned[_ngcontent-%COMP%] line[_ngcontent-%COMP%] {\n stroke-opacity: 0.55;\n}\n\n\n\n.m-svg[_ngcontent-%COMP%] .m-echo[_ngcontent-%COMP%] {\n stroke-opacity: 0.34;\n stroke-dasharray: 2 3;\n}\n\n.m-svg[_ngcontent-%COMP%] .is-pinned[_ngcontent-%COMP%] .m-echo[_ngcontent-%COMP%] {\n stroke-opacity: 0.2;\n}\n\n\n\n\n\n\n.m-svg[_ngcontent-%COMP%] .m-outline[_ngcontent-%COMP%] {\n fill: none;\n stroke: var(--%NS%prism-measure);\n stroke-opacity: 0.7;\n stroke-width: 1;\n}\n\n.m-svg[_ngcontent-%COMP%] .m-outline--anchor[_ngcontent-%COMP%] {\n stroke-dasharray: 4 3;\n}\n\n\n\n\n\n\n.m-svg[_ngcontent-%COMP%] .m-outline--preview[_ngcontent-%COMP%] {\n stroke-opacity: 0.35;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.m-label[_ngcontent-%COMP%] {\n position: absolute;\n padding: var(--%NS%sp-1) var(--%NS%sp-2);\n font-family: var(--%NS%font-mono);\n font-size: var(--%NS%fs-xs);\n font-variant-numeric: tabular-nums;\n line-height: 1;\n color: var(--%NS%prism-measure);\n white-space: nowrap;\n background: var(--%NS%prism-measure-plate, var(--%NS%prism-stage));\n transform: translate(-50%, -50%);\n}\n\n.m-label.is-pinned[_ngcontent-%COMP%] {\n opacity: 0.62;\n}\n\n\n\n.m-live[_ngcontent-%COMP%] {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n white-space: nowrap;\n clip-path: inset(50%);\n}\n\n[_nghost-%COMP%]:focus-visible {\n outline: 2px solid var(--%NS%prism-measure);\n outline-offset: -2px;\n}"] });
|
|
532
|
+
}
|
|
533
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismCanvasMeasureComponent, [{
|
|
534
|
+
type: Component,
|
|
535
|
+
args: [{ selector: 'prism-canvas-measure', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
536
|
+
tabindex: '0',
|
|
537
|
+
// `group`, not `application`. `application` is only justified for a
|
|
538
|
+
// widget with complete custom keyboard handling, because it pulls
|
|
539
|
+
// assistive technology out of browse mode and forwards every
|
|
540
|
+
// keystroke to the widget. The keyboard handling here is explicitly
|
|
541
|
+
// incomplete — see the class doc above: a measurement cannot be
|
|
542
|
+
// started without a pointer — so `application` would trade a screen
|
|
543
|
+
// reader user's browse mode for a key set that can do nothing.
|
|
544
|
+
// `group` is labelled and announced on focus without suppressing
|
|
545
|
+
// browse mode. `application` becomes the right answer the day Spec
|
|
546
|
+
// §10's Tab-stepping through the specimen's snap targets exists,
|
|
547
|
+
// and not before: revisit this line then, together with the
|
|
548
|
+
// `aria-label` below.
|
|
549
|
+
role: 'group',
|
|
550
|
+
'aria-label': 'Measure tool. Arrow keys nudge the active point, Shift for a larger step, Enter commits, Escape clears.',
|
|
551
|
+
'(keydown)': 'onKey($event)'
|
|
552
|
+
}, template: "<svg\n class=\"m-svg\"\n aria-hidden=\"true\">\n <!-- Spec \u00A74.5. Drawn before the spans so the four measurement lines stay\n the foreground mark and the two outlines read as the context they\n describe. -->\n @if (hover().anchor; as r) {\n <rect\n class=\"m-outline m-outline--anchor\"\n [class.m-outline--preview]=\"hover().preview\"\n [attr.x]=\"r.x\"\n [attr.y]=\"r.y\"\n [attr.width]=\"r.width\"\n [attr.height]=\"r.height\" />\n }\n @if (hover().target; as r) {\n <rect\n class=\"m-outline m-outline--target\"\n [attr.x]=\"r.x\"\n [attr.y]=\"r.y\"\n [attr.width]=\"r.width\"\n [attr.height]=\"r.height\" />\n }\n @for (l of lines(); track $index) {\n <g [class.is-pinned]=\"l.pinned\">\n <line\n [attr.x1]=\"l.a.x\"\n [attr.y1]=\"l.a.y\"\n [attr.x2]=\"l.b.x\"\n [attr.y2]=\"l.b.y\" />\n <line\n [attr.x1]=\"l.tickA[0].x\"\n [attr.y1]=\"l.tickA[0].y\"\n [attr.x2]=\"l.tickA[1].x\"\n [attr.y2]=\"l.tickA[1].y\" />\n <line\n [attr.x1]=\"l.tickB[0].x\"\n [attr.y1]=\"l.tickB[0].y\"\n [attr.x2]=\"l.tickB[1].x\"\n [attr.y2]=\"l.tickB[1].y\" />\n @for (e of l.echoes; track $index) {\n <line\n class=\"m-echo\"\n [attr.x1]=\"e[0].x\"\n [attr.y1]=\"e[0].y\"\n [attr.x2]=\"e[1].x\"\n [attr.y2]=\"e[1].y\" />\n }\n </g>\n }\n @for (l of hover().lines; track $index) {\n <g [class.is-pinned]=\"l.pinned\">\n <line\n [attr.x1]=\"l.a.x\"\n [attr.y1]=\"l.a.y\"\n [attr.x2]=\"l.b.x\"\n [attr.y2]=\"l.b.y\" />\n <line\n [attr.x1]=\"l.tickA[0].x\"\n [attr.y1]=\"l.tickA[0].y\"\n [attr.x2]=\"l.tickA[1].x\"\n [attr.y2]=\"l.tickA[1].y\" />\n <line\n [attr.x1]=\"l.tickB[0].x\"\n [attr.y1]=\"l.tickB[0].y\"\n [attr.x2]=\"l.tickB[1].x\"\n [attr.y2]=\"l.tickB[1].y\" />\n @for (e of l.echoes; track $index) {\n <line\n class=\"m-echo\"\n [attr.x1]=\"e[0].x\"\n [attr.y1]=\"e[0].y\"\n [attr.x2]=\"e[1].x\"\n [attr.y2]=\"e[1].y\" />\n }\n </g>\n }\n</svg>\n\n<div class=\"m-labels\">\n @for (l of lines(); track $index) {\n <span\n class=\"m-label\"\n [class.is-pinned]=\"l.pinned\"\n [style.left.px]=\"l.label.x\"\n [style.top.px]=\"l.label.y\">\n {{ l.text }}\n </span>\n }\n @for (l of hover().lines; track $index) {\n <span\n class=\"m-label\"\n [class.is-pinned]=\"l.pinned\"\n [style.left.px]=\"l.label.x\"\n [style.top.px]=\"l.label.y\">\n {{ l.text }}\n </span>\n }\n</div>\n\n<span\n class=\"m-live\"\n role=\"status\"\n aria-live=\"polite\">\n @if (hover().summary) {\n {{ hover().summary }}\n } @else if (lines().length > 0) {\n {{ lines()[lines().length - 1].text }}\n }\n</span>\n", styles: ["/* The host catches the pointer \u2014 it is the one layer allowed to. Everything\n it draws is exempted from that, or the hit test would land on its own\n lines instead of the specimen. Walking past the host itself is\n elementUnderPoint's job; see its doc.\n\n z-index 1, and both halves of that are load-bearing. It has to be set at\n all because .demo-wrap is position: relative with a transform \u2014 a\n stacking context of its own \u2014 and comes *after* this element in\n prism-renderer.component.html: at z-index auto the two are painted in\n tree order and .demo-wrap wins, hiding every line over the specimen and,\n worse, swallowing the pointerdown that alt-click anchoring needs to land\n on an element. And it has to be the *lowest* of the stage's overlays,\n below the background pill at 2, the rulers, grips and dimension line at\n 4, and the toolrail at 5, because this is the only one of them that\n takes pointer events across the whole stage. Raised past the pill it\n would cover its Reset button \u2014 the failure .vp-grip already documents \u2014\n and raised past the rail it would cover the very button that switches\n the tool off again. */\n:host {\n position: absolute;\n inset: 0;\n z-index: 1;\n cursor: crosshair;\n\n /* A measuring drag across a text-bearing specimen would otherwise paint\n a selection highlight over the thing being measured. `pointerdown`\n calls preventDefault() for the selection that starts underneath; this\n covers a selection that starts on the overlay itself. */\n user-select: none;\n}\n\n.m-svg,\n.m-labels {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n/* An `<svg>` is a replaced element, and for an absolutely positioned replaced\n element CSS takes the used width and height from the intrinsic size \u2014 300x150\n when the element declares none \u2014 instead of resolving them from `inset`. The\n rule above therefore sized this box 300x150 in the stage's top-left corner,\n while `.m-labels`, an ordinary div, stretched to the host as those same\n declarations promised. Since an `<svg>` clips to its own viewport, every line,\n tick, echo and outline beyond that corner was drawn and thrown away, and the\n tool printed numbers with nothing to attach them to. The identical trap is why\n `prism-canvas-rulers.component.ts` sizes its `<canvas>` elements by hand. */\n.m-svg {\n width: 100%;\n height: 100%;\n}\n\n.m-svg line {\n stroke: var(--prism-measure);\n stroke-width: 1;\n stroke-linecap: round;\n}\n\n.m-svg .is-pinned line {\n stroke-opacity: 0.55;\n}\n\n/* The snap echo is a footnote to the measurement, not a second measurement \u2014\n hence dotted and noticeably weaker than the line itself. */\n.m-svg .m-echo {\n stroke-opacity: 0.34;\n stroke-dasharray: 2 3;\n}\n\n.m-svg .is-pinned .m-echo {\n stroke-opacity: 0.2;\n}\n\n/* Spec \u00A74.5: anchor dashed, target solid. Both sit at reduced opacity for\n the same reason the snap echo does \u2014 they say *which two boxes* the four\n numbers are about, and must not compete with the numbers themselves. The\n anchor outline is also the only feedback an Alt-click produces before a\n second element is hovered. */\n.m-svg .m-outline {\n fill: none;\n stroke: var(--prism-measure);\n stroke-opacity: 0.7;\n stroke-width: 1;\n}\n\n.m-svg .m-outline--anchor {\n stroke-dasharray: 4 3;\n}\n\n/* The same dashed shape the anchor takes, at half the weight, while it is\n still only a prediction of what an Alt-click would take. Drawn at equal\n weight the click would produce no visible change \u2014 the outline is already\n there and merely stops following the pointer \u2014 which is the confirmation\n the anchor outline exists to give. */\n.m-svg .m-outline--preview {\n stroke-opacity: 0.35;\n}\n\n/* The number sits outside .demo-wrap and therefore does not scale with\n --zoom. It also never rotates for a diagonal measurement: a tilted number\n is slower to read, and readability is the whole point here.\n\n The plate follows the *ground*, not --prism-stage, and that is the same\n argument canvas-bg.styles.ts makes for --prism-measure itself. On\n bg: 'light' and bg: 'dark' the ground is absolute while --prism-stage\n stays with the theme, so a plate painted in --prism-stage puts the\n re-pointed measurement colour on the other theme's surface: 2.10:1 for a\n light ground under the dark theme, 2.13:1 the other way round. Both are\n documented @Showcase({ bg }) values, so \"always visible\" would have been\n true-in-the-common-case again \u2014 exactly the problem the token override\n exists to prevent. --prism-measure-plate is re-pointed alongside\n --prism-measure in that same file and falls back to --prism-stage\n everywhere else, where the stage *is* the ground. */\n.m-label {\n position: absolute;\n padding: var(--sp-1) var(--sp-2);\n font-family: var(--font-mono);\n font-size: var(--fs-xs);\n font-variant-numeric: tabular-nums;\n line-height: 1;\n color: var(--prism-measure);\n white-space: nowrap;\n background: var(--prism-measure-plate, var(--prism-stage));\n transform: translate(-50%, -50%);\n}\n\n.m-label.is-pinned {\n opacity: 0.62;\n}\n\n/* Visually hidden, not display:none \u2014 a status region only announces to a\n screen reader while it stays in the accessibility tree. */\n.m-live {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n white-space: nowrap;\n clip-path: inset(50%);\n}\n\n:host(:focus-visible) {\n outline: 2px solid var(--prism-measure);\n outline-offset: -2px;\n}\n"] }]
|
|
553
|
+
}], () => [], null); })();
|
|
554
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismCanvasMeasureComponent, { className: "PrismCanvasMeasureComponent", filePath: "app/canvas/prism-canvas-measure.component.ts", lineNumber: 163 }); })();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
/**
|
|
3
|
+
* The pinned measurements, as a row below the canvas.
|
|
4
|
+
*
|
|
5
|
+
* Not a panel in a corner of the stage: the top overlay band already carries
|
|
6
|
+
* the background pill on the left, the viewport dimension line in the
|
|
7
|
+
* middle and the toolrail on the right. A fourth floating surface there
|
|
8
|
+
* would be the first one to collide with another.
|
|
9
|
+
*
|
|
10
|
+
* A direct child of `.prism-canvas-wrap` and therefore without a capture
|
|
11
|
+
* guard of its own: `CAPTURE_CANVAS_ONLY_SELECTOR` structurally removes
|
|
12
|
+
* every child of the wrap that does not contain the stage. The rule is
|
|
13
|
+
* deliberately written structurally rather than as a list of selectors, so
|
|
14
|
+
* exactly this case works without anyone maintaining it.
|
|
15
|
+
*/
|
|
16
|
+
export declare class PrismCanvasPinRowComponent {
|
|
17
|
+
private readonly measure;
|
|
18
|
+
protected readonly chips: import("@angular/core").Signal<string[]>;
|
|
19
|
+
protected remove(index: number): void;
|
|
20
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PrismCanvasPinRowComponent, never>;
|
|
21
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PrismCanvasPinRowComponent, "prism-canvas-pin-row", never, {}, {}, never, never, true, never>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=prism-canvas-pin-row.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prism-canvas-pin-row.component.d.ts","sourceRoot":"","sources":["../../../src/app/canvas/prism-canvas-pin-row.component.ts"],"names":[],"mappings":";AAKA;;;;;;;;;;;;;GAaG;AACH,qBAQa,0BAA0B;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IAEvD,SAAS,CAAC,QAAQ,CAAC,KAAK,2CAAoG;IAE5H,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;yCAL5B,0BAA0B;2CAA1B,0BAA0B;CAQtC"}
|