@bluepic/embed 0.4.0-next.145 → 0.4.0-next.148
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/bluepic-embed.iife.js +181 -173
- package/dist/bluepic-embed.umd.js +181 -173
- package/dist/components/TemplateEditor/BxTemplateEditor.vue.d.ts +134 -0
- package/dist/components/TemplateEditor/FieldHintsLayer.vue.d.ts +49 -0
- package/dist/components/TemplateEditor/TemplateView.vue.d.ts +47 -0
- package/dist/embed/embed.d.ts +402 -0
- package/dist/main.cjs +106 -98
- package/dist/main.mjs +25655 -25062
- package/dist/style.css +1 -1
- package/dist/util/fieldHints/chipAction.d.ts +23 -0
- package/dist/util/fieldHints/fieldFocus.d.ts +1 -1
- package/dist/util/fieldHints/index.d.ts +8 -2
- package/dist/util/fieldHints/placement.d.ts +72 -0
- package/dist/util/fieldHints/probe.d.ts +11 -0
- package/dist/util/fieldHints/textProbe.d.ts +71 -0
- package/dist/util/fieldHints/useCanvasTextEditing.d.ts +125 -0
- package/dist/util/fieldHints/useFieldHitboxes.d.ts +51 -152
- package/dist/util/fieldHints/useHintReveal.d.ts +1 -1
- package/dist/util/fields.d.ts +0 -16
- package/dist/util/gallery.d.ts +2 -2
- package/dist/util/popoverArrow.d.ts +33 -0
- package/package.json +2 -2
- package/dist/util/fieldHints/interact.d.ts +0 -21
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BluepicField } from '@bluepic/types';
|
|
2
|
+
/**
|
|
3
|
+
* What clicking a hint chip does.
|
|
4
|
+
*
|
|
5
|
+
* There is no per-field hint MODE any more — hints are simply on or off for the
|
|
6
|
+
* whole campaign (`config.hints.enabled`). What a chip does is inferred from the
|
|
7
|
+
* field itself:
|
|
8
|
+
*
|
|
9
|
+
* - `upload` — an image field that accepts a device upload. Clicking goes
|
|
10
|
+
* straight to its own upload flow, which is the only thing anyone wants from
|
|
11
|
+
* an image chip.
|
|
12
|
+
* - `focus` — everything else, including image fields restricted to libraries.
|
|
13
|
+
* The chip scrolls the field into view and focuses its control, so the next
|
|
14
|
+
* keystroke already types into it.
|
|
15
|
+
*/
|
|
16
|
+
export type ChipAction = 'upload' | 'focus';
|
|
17
|
+
/**
|
|
18
|
+
* `field.props` entries may be author expressions (functions) rather than
|
|
19
|
+
* literals, so a prop can only be read through the host's evaluator — the same
|
|
20
|
+
* `wrapFieldProps` treatment `FieldsList` gives every field component.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readFieldProp<T>(value: T | (() => T), evaluate?: (fn: Function) => unknown): T | undefined;
|
|
23
|
+
export declare function chipActionFor(field: BluepicField, evaluate?: (fn: Function) => unknown): ChipAction;
|
|
@@ -49,7 +49,7 @@ export declare const FIELD_FOCUS_FLASH_CLASS = "bx-field-hint-target";
|
|
|
49
49
|
* container, and centering yanks the whole list even when the target was
|
|
50
50
|
* already comfortably visible.
|
|
51
51
|
*/
|
|
52
|
-
export declare function revealFieldElement(el: HTMLElement | null | undefined):
|
|
52
|
+
export declare function revealFieldElement(el: HTMLElement | null | undefined): HTMLElement | undefined;
|
|
53
53
|
/**
|
|
54
54
|
* Creates the desktop half: a registry keyed by the field object itself.
|
|
55
55
|
*
|
|
@@ -9,7 +9,13 @@ export type { FieldHitbox } from './useFieldHitboxes';
|
|
|
9
9
|
export { useHintReveal } from './useHintReveal';
|
|
10
10
|
export { createFieldElementRegistry, provideFieldFocus, revealFieldElement, useFieldFocus, FIELD_FOCUS_KEY, FIELD_FOCUS_FLASH_CLASS, } from './fieldFocus';
|
|
11
11
|
export type { FieldFocusController } from './fieldFocus';
|
|
12
|
-
export {
|
|
13
|
-
export type {
|
|
12
|
+
export { chipActionFor, readFieldProp } from './chipAction';
|
|
13
|
+
export type { ChipAction } from './chipAction';
|
|
14
|
+
export { textGeometryProbe } from './textProbe';
|
|
15
|
+
export type { CaretGeometry, SelectionQuad, TextGeometryResult, TextIndexResult } from './textProbe';
|
|
16
|
+
export { useCanvasTextEditing } from './useCanvasTextEditing';
|
|
17
|
+
export type { CanvasTextState, SelectionHandle } from './useCanvasTextEditing';
|
|
18
|
+
export { chooseChipPlacement, chipIsCentered, estimateChipWidth, arrowRotationForSide } from './placement';
|
|
19
|
+
export type { ChipSide, PlacementResult } from './placement';
|
|
14
20
|
export { useFieldActivator } from './fieldFocus';
|
|
15
21
|
export type { FieldActivator } from './fieldFocus';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { BluepicField } from '@bluepic/types';
|
|
2
|
+
/**
|
|
3
|
+
* Where a hint chip sits relative to its hitbox.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately a small synchronous solver rather than `@floating-ui`, which
|
|
6
|
+
* this package already depends on: hints re-project on EVERY pan/zoom frame,
|
|
7
|
+
* and floating-ui's `computePosition` is async and DOM-measuring. Running one
|
|
8
|
+
* per hint per frame would lag the overlay behind the canvas it is annotating.
|
|
9
|
+
* A pure function over numbers also means the placement rules can be tested
|
|
10
|
+
* without a DOM.
|
|
11
|
+
*/
|
|
12
|
+
export type ChipSide = 'top' | 'bottom' | 'left' | 'right';
|
|
13
|
+
export type Rect = {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
export type PlacementInput = {
|
|
20
|
+
/** Hitbox AABB in overlay pixels. */
|
|
21
|
+
box: Rect;
|
|
22
|
+
/** Measured (or estimated) chip size. */
|
|
23
|
+
chip: {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
};
|
|
27
|
+
/** Visible overlay area. */
|
|
28
|
+
viewport: {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
};
|
|
32
|
+
/** Distance from the hitbox edge to the chip edge — room for the arrow. */
|
|
33
|
+
gap: number;
|
|
34
|
+
/** Keep-inside-the-viewport padding. */
|
|
35
|
+
margin: number;
|
|
36
|
+
};
|
|
37
|
+
export type PlacementResult = {
|
|
38
|
+
side: ChipSide;
|
|
39
|
+
/** Chip top-left in overlay pixels. */
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
/**
|
|
43
|
+
* Arrow tip anchor, RELATIVE to the chip's top-left. Tracks the hitbox centre
|
|
44
|
+
* even after the chip has been shifted to stay on screen — otherwise a
|
|
45
|
+
* clamped chip would point at empty space.
|
|
46
|
+
*/
|
|
47
|
+
arrowX: number;
|
|
48
|
+
arrowY: number;
|
|
49
|
+
};
|
|
50
|
+
export declare function chipIsCentered(fieldType: BluepicField['type']): boolean;
|
|
51
|
+
export declare function chooseChipPlacement(input: PlacementInput): PlacementResult;
|
|
52
|
+
/**
|
|
53
|
+
* Chip width before the real element has been measured. Only used for the very
|
|
54
|
+
* first frame — the layer measures the rendered chip and re-solves — but a wild
|
|
55
|
+
* guess here would place the first paint visibly wrong.
|
|
56
|
+
*/
|
|
57
|
+
export declare function estimateChipWidth(label: string, max?: number): number;
|
|
58
|
+
/**
|
|
59
|
+
* The arrow artwork points RIGHT at rest — the path's tip is authored on the
|
|
60
|
+
* left, but `popoverArrow`'s outer `matrix(-1,0,-0,-1,...)` flips it. Verified
|
|
61
|
+
* by rasterising the mask: the left edge is ~97% covered (the flat base) and
|
|
62
|
+
* the right edge ~4% (the point).
|
|
63
|
+
*/
|
|
64
|
+
export declare const ARROW_NATIVE_DIRECTION: {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Degrees to rotate the arrow so it points from the chip back at its hitbox.
|
|
70
|
+
* CSS rotation is clockwise with y pointing down.
|
|
71
|
+
*/
|
|
72
|
+
export declare function arrowRotationForSide(side: ChipSide): number;
|
|
@@ -35,6 +35,17 @@ export type ElementQuad = {
|
|
|
35
35
|
*/
|
|
36
36
|
dataId: string;
|
|
37
37
|
quad: HintQuad;
|
|
38
|
+
/**
|
|
39
|
+
* Tighter box to POINT AT, when the element has one. Present only for text
|
|
40
|
+
* elements whose painted box is larger than the glyphs — i.e. text carrying a
|
|
41
|
+
* surface/background bar, where `quad` balloons out to the whole text frame.
|
|
42
|
+
*
|
|
43
|
+
* The hover region stays `quad` (generous, easy to hit); only the tooltip's
|
|
44
|
+
* anchor moves here, so a bubble describing a headline points at the words
|
|
45
|
+
* rather than at the far corner of the bar behind them. Absent whenever the
|
|
46
|
+
* two coincide, which is the common case.
|
|
47
|
+
*/
|
|
48
|
+
anchor?: HintQuad;
|
|
38
49
|
};
|
|
39
50
|
export type HitboxProbeResult = {
|
|
40
51
|
supported: false;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text geometry on the canvas: where the caret is, what the selection covers,
|
|
3
|
+
* and which character a point lands on.
|
|
4
|
+
*
|
|
5
|
+
* All three answers come from the glyphs the engine actually laid out, via
|
|
6
|
+
* `SVGTextContentElement`:
|
|
7
|
+
* `getStartPositionOfChar` / `getEndPositionOfChar` — caret and range edges
|
|
8
|
+
* `getExtentOfChar` — line height / em box
|
|
9
|
+
* `getCharNumAtPosition` — point → character
|
|
10
|
+
* plus the tspan's computed `fill` for colour. Verified against a live 0.2.65
|
|
11
|
+
* sandbox: hit-testing along a line returned the exact character at each
|
|
12
|
+
* position and −1 outside.
|
|
13
|
+
*
|
|
14
|
+
* ONE probe with an `op` rather than three: it ships as SOURCE TEXT (see the
|
|
15
|
+
* warning on `fieldHitboxProbe`), so helpers cannot be shared between separate
|
|
16
|
+
* probe functions — they would have to be duplicated inside each one. The line
|
|
17
|
+
* mapping below is subtle enough that having two copies drift apart is a real
|
|
18
|
+
* risk, so the ops share one body.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* A caret as a SEGMENT rather than a point plus a height.
|
|
22
|
+
*
|
|
23
|
+
* Both ends are computed in the line's local space and mapped out, so the
|
|
24
|
+
* segment carries the text's rotation, skew and scale intrinsically. Returning
|
|
25
|
+
* `{x, y, height}` instead forced the consumer to subtract the ascent in SERIAL
|
|
26
|
+
* y — which is only "up" for unrotated text, and left carets and handle stems
|
|
27
|
+
* standing vertically through slanted text.
|
|
28
|
+
*/
|
|
29
|
+
export type CaretGeometry = {
|
|
30
|
+
/** Top of the em box, in serial coordinates. */
|
|
31
|
+
top: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
};
|
|
35
|
+
/** Bottom of the em box, in serial coordinates. */
|
|
36
|
+
bottom: {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
};
|
|
40
|
+
/** Resolved text colour, so the caret matches the words it sits in. */
|
|
41
|
+
color: string;
|
|
42
|
+
lineIndex: number;
|
|
43
|
+
};
|
|
44
|
+
/** One selected run on one line, as a quad so rotated text still works. */
|
|
45
|
+
export type SelectionQuad = {
|
|
46
|
+
points: Array<{
|
|
47
|
+
x: number;
|
|
48
|
+
y: number;
|
|
49
|
+
}>;
|
|
50
|
+
lineIndex: number;
|
|
51
|
+
};
|
|
52
|
+
export type TextGeometryResult = {
|
|
53
|
+
found: false;
|
|
54
|
+
} | {
|
|
55
|
+
found: true;
|
|
56
|
+
/** Present only when the selection is collapsed — a caret, not a range. */
|
|
57
|
+
caret?: CaretGeometry;
|
|
58
|
+
selection: SelectionQuad[];
|
|
59
|
+
/** Range ends, for the mobile drag handles. Present when a range exists. */
|
|
60
|
+
startAnchor?: CaretGeometry;
|
|
61
|
+
endAnchor?: CaretGeometry;
|
|
62
|
+
};
|
|
63
|
+
export type TextIndexResult = {
|
|
64
|
+
index: number | null;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* ⚠️ SHIPPED AS SOURCE TEXT — no imports, no module-scope references, plain ES.
|
|
68
|
+
*
|
|
69
|
+
* @param argsJson `{ op, elementId, value, selectionStart, selectionEnd, x, y }`
|
|
70
|
+
*/
|
|
71
|
+
export declare function textGeometryProbe(argsJson: string): TextGeometryResult | TextIndexResult;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { type Ref } from 'vue';
|
|
2
|
+
import type { BluepicField } from '@bluepic/types';
|
|
3
|
+
import { type CaretGeometry, type SelectionQuad } from './textProbe';
|
|
4
|
+
type EvaluateFn = (func: Function, ...args: string[]) => Promise<unknown>;
|
|
5
|
+
export type CanvasTextState = {
|
|
6
|
+
caret?: CaretGeometry;
|
|
7
|
+
selection: SelectionQuad[];
|
|
8
|
+
startAnchor?: CaretGeometry;
|
|
9
|
+
endAnchor?: CaretGeometry;
|
|
10
|
+
};
|
|
11
|
+
export type SelectionHandle = 'start' | 'end';
|
|
12
|
+
export declare function useCanvasTextEditing(options: {
|
|
13
|
+
evaluate: () => EvaluateFn | undefined;
|
|
14
|
+
data: Ref<{
|
|
15
|
+
[k: string]: unknown;
|
|
16
|
+
}>;
|
|
17
|
+
}): {
|
|
18
|
+
state: Ref<{
|
|
19
|
+
caret?: {
|
|
20
|
+
top: {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
};
|
|
24
|
+
bottom: {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
};
|
|
28
|
+
color: string;
|
|
29
|
+
lineIndex: number;
|
|
30
|
+
} | undefined;
|
|
31
|
+
selection: {
|
|
32
|
+
points: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
}[];
|
|
36
|
+
lineIndex: number;
|
|
37
|
+
}[];
|
|
38
|
+
startAnchor?: {
|
|
39
|
+
top: {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
};
|
|
43
|
+
bottom: {
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
};
|
|
47
|
+
color: string;
|
|
48
|
+
lineIndex: number;
|
|
49
|
+
} | undefined;
|
|
50
|
+
endAnchor?: {
|
|
51
|
+
top: {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
};
|
|
55
|
+
bottom: {
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
};
|
|
59
|
+
color: string;
|
|
60
|
+
lineIndex: number;
|
|
61
|
+
} | undefined;
|
|
62
|
+
} | undefined, CanvasTextState | {
|
|
63
|
+
caret?: {
|
|
64
|
+
top: {
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
};
|
|
68
|
+
bottom: {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
};
|
|
72
|
+
color: string;
|
|
73
|
+
lineIndex: number;
|
|
74
|
+
} | undefined;
|
|
75
|
+
selection: {
|
|
76
|
+
points: {
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
}[];
|
|
80
|
+
lineIndex: number;
|
|
81
|
+
}[];
|
|
82
|
+
startAnchor?: {
|
|
83
|
+
top: {
|
|
84
|
+
x: number;
|
|
85
|
+
y: number;
|
|
86
|
+
};
|
|
87
|
+
bottom: {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
};
|
|
91
|
+
color: string;
|
|
92
|
+
lineIndex: number;
|
|
93
|
+
} | undefined;
|
|
94
|
+
endAnchor?: {
|
|
95
|
+
top: {
|
|
96
|
+
x: number;
|
|
97
|
+
y: number;
|
|
98
|
+
};
|
|
99
|
+
bottom: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
};
|
|
103
|
+
color: string;
|
|
104
|
+
lineIndex: number;
|
|
105
|
+
} | undefined;
|
|
106
|
+
} | undefined>;
|
|
107
|
+
activeElementId: Ref<string | undefined, string | undefined>;
|
|
108
|
+
isActive: import("vue").ComputedRef<boolean>;
|
|
109
|
+
begin: (next: {
|
|
110
|
+
field: BluepicField;
|
|
111
|
+
elementId: string;
|
|
112
|
+
binding: string;
|
|
113
|
+
input: HTMLInputElement | HTMLTextAreaElement;
|
|
114
|
+
}) => void;
|
|
115
|
+
end: () => void;
|
|
116
|
+
beginSelectionAt: (x: number, y: number) => Promise<void>;
|
|
117
|
+
beginHandleDrag: (handle: SelectionHandle, point: {
|
|
118
|
+
x: number;
|
|
119
|
+
y: number;
|
|
120
|
+
}) => void;
|
|
121
|
+
extendSelectionTo: (x: number, y: number) => Promise<void>;
|
|
122
|
+
endSelectionDrag: () => void;
|
|
123
|
+
selectWordAt: (x: number, y: number) => Promise<void>;
|
|
124
|
+
};
|
|
125
|
+
export {};
|