@realnation/builder-shared-sdk 2.4.0 → 2.4.2
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/souvenir/index.d.ts +1 -8
- package/dist/souvenir/index.d.ts.map +1 -1
- package/dist/souvenir/index.js +2 -14
- package/dist/souvenir/outputId.d.ts +9 -0
- package/dist/souvenir/outputId.d.ts.map +1 -0
- package/dist/souvenir/outputId.js +14 -0
- package/dist/souvenir/render.d.ts +16 -1
- package/dist/souvenir/render.d.ts.map +1 -1
- package/dist/souvenir/render.js +88 -47
- package/dist/souvenir/types.d.ts +13 -0
- package/dist/souvenir/types.d.ts.map +1 -1
- package/dist/souvenir/types.js +19 -0
- package/package.json +64 -64
package/dist/souvenir/index.d.ts
CHANGED
|
@@ -13,14 +13,7 @@ export * from './imageFit.js';
|
|
|
13
13
|
export { wrapText, tokenize, layoutLines, type MeasureText, type TextAlign, type TextVAlign, type PlacedLine, } from './text.js';
|
|
14
14
|
export { renderSouvenir, drawOrder, pageHasOpaqueBackground, souvenirSignature, loadSouvenirImage, } from './render.js';
|
|
15
15
|
import { type SouvenirOptions, type SouvenirPage, type SouvenirResult, type SouvenirValues } from './types.js';
|
|
16
|
-
|
|
17
|
-
* Element ids for output-bound elements are `outputs@@nodeId@@key[@@tab@@rand]`.
|
|
18
|
-
* Modules know field keys; the page knows element ids. This bridges the two.
|
|
19
|
-
*/
|
|
20
|
-
export declare function parseSouvenirOutputId(eid: string): {
|
|
21
|
-
nodeId: string;
|
|
22
|
-
key: string;
|
|
23
|
-
} | null;
|
|
16
|
+
export { parseSouvenirOutputId } from './outputId.js';
|
|
24
17
|
/**
|
|
25
18
|
* Spreads `{ fieldKey: value }` over every element on the page bound to that
|
|
26
19
|
* field, so a module never has to know how element ids are built.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/souvenir/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/souvenir/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EACtD,MAAM,CAAC,EAAE,MAAM,GACd,cAAc,CAUhB;AAYD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,YAAY,EAClB,MAAM,GAAE,cAAmB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAqBzB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,GAAE,cAAmB,GAAG,MAAM,CAEnF"}
|
package/dist/souvenir/index.js
CHANGED
|
@@ -14,20 +14,8 @@ export { wrapText, tokenize, layoutLines, } from './text.js';
|
|
|
14
14
|
export { renderSouvenir, drawOrder, pageHasOpaqueBackground, souvenirSignature, loadSouvenirImage, } from './render.js';
|
|
15
15
|
import { pageHasOpaqueBackground, renderSouvenir, souvenirSignature } from './render.js';
|
|
16
16
|
import { SouvenirError, } from './types.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Element ids for output-bound elements are `outputs@@nodeId@@key[@@tab@@rand]`.
|
|
21
|
-
* Modules know field keys; the page knows element ids. This bridges the two.
|
|
22
|
-
*/
|
|
23
|
-
export function parseSouvenirOutputId(eid) {
|
|
24
|
-
if (!eid.startsWith(OUTPUT_PREFIX + OUTPUT_SEPARATOR))
|
|
25
|
-
return null;
|
|
26
|
-
const parts = eid.split(OUTPUT_SEPARATOR);
|
|
27
|
-
if (parts.length < 3)
|
|
28
|
-
return null;
|
|
29
|
-
return { nodeId: parts[1], key: parts[2] };
|
|
30
|
-
}
|
|
17
|
+
import { parseSouvenirOutputId } from './outputId.js';
|
|
18
|
+
export { parseSouvenirOutputId } from './outputId.js';
|
|
31
19
|
/**
|
|
32
20
|
* Spreads `{ fieldKey: value }` over every element on the page bound to that
|
|
33
21
|
* field, so a module never has to know how element ids are built.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element ids for output-bound elements are `outputs@@nodeId@@key[@@tab@@rand]`.
|
|
3
|
+
* Modules know field keys; the page knows element ids. This bridges the two.
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseSouvenirOutputId(eid: string): {
|
|
6
|
+
nodeId: string;
|
|
7
|
+
key: string;
|
|
8
|
+
} | null;
|
|
9
|
+
//# sourceMappingURL=outputId.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outputId.d.ts","sourceRoot":"","sources":["../../src/souvenir/outputId.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKzF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const OUTPUT_SEPARATOR = '@@';
|
|
2
|
+
const OUTPUT_PREFIX = 'outputs';
|
|
3
|
+
/**
|
|
4
|
+
* Element ids for output-bound elements are `outputs@@nodeId@@key[@@tab@@rand]`.
|
|
5
|
+
* Modules know field keys; the page knows element ids. This bridges the two.
|
|
6
|
+
*/
|
|
7
|
+
export function parseSouvenirOutputId(eid) {
|
|
8
|
+
if (!eid.startsWith(OUTPUT_PREFIX + OUTPUT_SEPARATOR))
|
|
9
|
+
return null;
|
|
10
|
+
const parts = eid.split(OUTPUT_SEPARATOR);
|
|
11
|
+
if (parts.length < 3)
|
|
12
|
+
return null;
|
|
13
|
+
return { nodeId: parts[1], key: parts[2] };
|
|
14
|
+
}
|
|
@@ -9,7 +9,22 @@
|
|
|
9
9
|
* mirrors the runner's `elementStyle`, which is what the operator sees in the
|
|
10
10
|
* editor and the preview.
|
|
11
11
|
*/
|
|
12
|
-
import { type SouvenirOptions, type SouvenirPage, type SouvenirValues } from './types.js';
|
|
12
|
+
import { type SouvenirOptions, type SouvenirPage, type SouvenirProperty, type SouvenirValues } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* An element bound to a Data field whose field has no value is left out, the
|
|
15
|
+
* way the runner hides it on a live page. Falling back to what the Builder
|
|
16
|
+
* stored for it would draw the field's name (such as "Message") into the image.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isUnfilledOutput(eid: string, values: SouvenirValues): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The border the runner shows for an element, or null when it shows none:
|
|
21
|
+
* border style None, no width, or a transparent colour. Shapes and every other
|
|
22
|
+
* element share these rules, as they share the runner's `elementStyle`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function borderOf(property: SouvenirProperty): {
|
|
25
|
+
width: number;
|
|
26
|
+
color: string;
|
|
27
|
+
} | null;
|
|
13
28
|
/** Loads one image with EXIF orientation applied, or throws a SouvenirError. */
|
|
14
29
|
export declare function loadSouvenirImage(url: string, crossOrigin: string | null | undefined): Promise<CanvasImageSource & {
|
|
15
30
|
width: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/souvenir/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/souvenir/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAC;AA6CpB;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO5F;AAyPD,gFAAgF;AAChF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,OAAO,CAAC,iBAAiB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBhE;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAStD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAYnE;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAMpF;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,YAAY,EAClB,MAAM,GAAE,cAAmB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAsE5B"}
|
package/dist/souvenir/render.js
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* editor and the preview.
|
|
11
11
|
*/
|
|
12
12
|
import { computeImageFitDrawRect, hashSignature, normalizeImageFit } from './imageFit.js';
|
|
13
|
+
import { parseSouvenirOutputId } from './outputId.js';
|
|
13
14
|
import { layoutLines, wrapText } from './text.js';
|
|
14
|
-
import { isSouvenirSizeAllowed, SouvenirError, SOUVENIR_ELEMENT_TYPES, } from './types.js';
|
|
15
|
+
import { isSouvenirInteractive, isSouvenirSizeAllowed, SouvenirError, SOUVENIR_ELEMENT_TYPES, } from './types.js';
|
|
15
16
|
const DEFAULT_LINE_HEIGHT = 1.2;
|
|
16
17
|
const TRANSPARENT = 'TRANSPARENT';
|
|
17
18
|
const num = (value, fallback = 0) => {
|
|
@@ -40,6 +41,33 @@ function geometryOf(property) {
|
|
|
40
41
|
function isHidden(property) {
|
|
41
42
|
return property['display'] === 'off';
|
|
42
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* An element bound to a Data field whose field has no value is left out, the
|
|
46
|
+
* way the runner hides it on a live page. Falling back to what the Builder
|
|
47
|
+
* stored for it would draw the field's name (such as "Message") into the image.
|
|
48
|
+
*/
|
|
49
|
+
export function isUnfilledOutput(eid, values) {
|
|
50
|
+
if (!parseSouvenirOutputId(eid))
|
|
51
|
+
return false;
|
|
52
|
+
const value = values[eid];
|
|
53
|
+
return value === undefined || value === null || String(value) === '';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The border the runner shows for an element, or null when it shows none:
|
|
57
|
+
* border style None, no width, or a transparent colour. Shapes and every other
|
|
58
|
+
* element share these rules, as they share the runner's `elementStyle`.
|
|
59
|
+
*/
|
|
60
|
+
export function borderOf(property) {
|
|
61
|
+
if (property['border-style'] === 'none')
|
|
62
|
+
return null;
|
|
63
|
+
const width = num(property['border-width']);
|
|
64
|
+
if (width <= 0)
|
|
65
|
+
return null;
|
|
66
|
+
const raw = typeof property['border-color'] === 'string' ? property['border-color'].trim() : '';
|
|
67
|
+
if (raw === TRANSPARENT)
|
|
68
|
+
return null;
|
|
69
|
+
return { width, color: raw || '#94a3b8' };
|
|
70
|
+
}
|
|
43
71
|
function roundedRectPath(ctx, x, y, width, height, radius) {
|
|
44
72
|
const r = Math.max(0, Math.min(radius, Math.min(width, height) / 2));
|
|
45
73
|
ctx.beginPath();
|
|
@@ -95,21 +123,16 @@ function fillBackground(ctx, property, width, height, radius, ellipse) {
|
|
|
95
123
|
* size the operator gave it, at the cost of half a border width along the edge.
|
|
96
124
|
*/
|
|
97
125
|
function strokeBorder(ctx, property, width, height, radius, ellipse) {
|
|
98
|
-
|
|
126
|
+
const border = borderOf(property);
|
|
127
|
+
if (!border)
|
|
99
128
|
return;
|
|
100
|
-
const
|
|
101
|
-
if (borderWidth <= 0)
|
|
102
|
-
return;
|
|
103
|
-
const color = colorOf(property['border-color']) || '#94a3b8';
|
|
104
|
-
if (!color)
|
|
105
|
-
return;
|
|
106
|
-
const inset = borderWidth / 2;
|
|
129
|
+
const inset = border.width / 2;
|
|
107
130
|
if (ellipse)
|
|
108
|
-
ellipsePath(ctx, inset, inset, width -
|
|
131
|
+
ellipsePath(ctx, inset, inset, width - border.width, height - border.width);
|
|
109
132
|
else
|
|
110
|
-
roundedRectPath(ctx, inset, inset, width -
|
|
111
|
-
ctx.lineWidth =
|
|
112
|
-
ctx.strokeStyle = color;
|
|
133
|
+
roundedRectPath(ctx, inset, inset, width - border.width, height - border.width, Math.max(0, radius - inset));
|
|
134
|
+
ctx.lineWidth = border.width;
|
|
135
|
+
ctx.strokeStyle = border.color;
|
|
113
136
|
ctx.stroke();
|
|
114
137
|
}
|
|
115
138
|
function fontOf(property, fontSize) {
|
|
@@ -121,39 +144,55 @@ function fontOf(property, fontSize) {
|
|
|
121
144
|
const quoted = /[",]/.test(family) || !/\s/.test(family) ? family : `"${family}"`;
|
|
122
145
|
return `${style}${weight}${fontSize}px ${quoted}`;
|
|
123
146
|
}
|
|
124
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Lays the text out inside the box already in element space. `padX` is the side
|
|
149
|
+
* padding the runner leaves on shape text.
|
|
150
|
+
*/
|
|
151
|
+
function drawTextInBox(c, property, radius, width, height, content, padX = 0) {
|
|
125
152
|
const fontSize = num(property['font-size'], 14);
|
|
126
153
|
if (fontSize <= 0 || !content)
|
|
127
154
|
return;
|
|
128
155
|
const lineHeight = num(property['line-height'], DEFAULT_LINE_HEIGHT) || DEFAULT_LINE_HEIGHT;
|
|
129
156
|
const align = (property['alignment'] || 'center');
|
|
130
157
|
const vAlign = (property['v-alignment'] || 'middle');
|
|
158
|
+
const boxWidth = Math.max(0, width - padX * 2);
|
|
159
|
+
if (boxWidth <= 0)
|
|
160
|
+
return;
|
|
161
|
+
c.save();
|
|
162
|
+
roundedRectPath(c, 0, 0, width, height, radius);
|
|
163
|
+
c.clip();
|
|
164
|
+
c.font = fontOf(property, fontSize);
|
|
165
|
+
c.textBaseline = 'alphabetic';
|
|
166
|
+
c.textAlign = 'left';
|
|
167
|
+
c.fillStyle = colorOf(property['color']) || '#000000';
|
|
168
|
+
const measure = (text) => c.measureText(text).width;
|
|
169
|
+
const lines = wrapText(content, boxWidth, measure);
|
|
170
|
+
const placed = layoutLines(lines, { x: padX, y: 0, width: boxWidth, height }, fontSize, lineHeight, align, vAlign, measure);
|
|
171
|
+
const formatting = Array.isArray(property['formatting']) ? property['formatting'] : [];
|
|
172
|
+
const underline = formatting.includes('Underline');
|
|
173
|
+
const strike = formatting.includes('Strikethrough');
|
|
174
|
+
for (const line of placed) {
|
|
175
|
+
if (!line.text)
|
|
176
|
+
continue;
|
|
177
|
+
c.fillText(line.text, line.x, line.y);
|
|
178
|
+
if (underline)
|
|
179
|
+
c.fillRect(line.x, line.y + fontSize * 0.12, line.width, Math.max(1, fontSize / 16));
|
|
180
|
+
if (strike)
|
|
181
|
+
c.fillRect(line.x, line.y - fontSize * 0.28, line.width, Math.max(1, fontSize / 16));
|
|
182
|
+
}
|
|
183
|
+
c.restore();
|
|
184
|
+
}
|
|
185
|
+
function drawText(ctx, property, geo, content) {
|
|
186
|
+
if (!content)
|
|
187
|
+
return;
|
|
131
188
|
inElementSpace(ctx, geo, (c, width, height) => {
|
|
132
189
|
fillBackground(c, property, width, height, geo.radius, false);
|
|
133
|
-
|
|
134
|
-
c.clip();
|
|
135
|
-
c.font = fontOf(property, fontSize);
|
|
136
|
-
c.textBaseline = 'alphabetic';
|
|
137
|
-
c.textAlign = 'left';
|
|
138
|
-
c.fillStyle = colorOf(property['color']) || '#000000';
|
|
139
|
-
const measure = (text) => c.measureText(text).width;
|
|
140
|
-
const lines = wrapText(content, width, measure);
|
|
141
|
-
const placed = layoutLines(lines, { x: 0, y: 0, width, height }, fontSize, lineHeight, align, vAlign, measure);
|
|
142
|
-
const formatting = Array.isArray(property['formatting']) ? property['formatting'] : [];
|
|
143
|
-
const underline = formatting.includes('Underline');
|
|
144
|
-
const strike = formatting.includes('Strikethrough');
|
|
145
|
-
for (const line of placed) {
|
|
146
|
-
if (!line.text)
|
|
147
|
-
continue;
|
|
148
|
-
c.fillText(line.text, line.x, line.y);
|
|
149
|
-
if (underline)
|
|
150
|
-
c.fillRect(line.x, line.y + fontSize * 0.12, line.width, Math.max(1, fontSize / 16));
|
|
151
|
-
if (strike)
|
|
152
|
-
c.fillRect(line.x, line.y - fontSize * 0.28, line.width, Math.max(1, fontSize / 16));
|
|
153
|
-
}
|
|
190
|
+
drawTextInBox(c, property, geo.radius, width, height, content);
|
|
154
191
|
});
|
|
155
192
|
inElementSpace(ctx, geo, (c, width, height) => strokeBorder(c, property, width, height, geo.radius, false));
|
|
156
193
|
}
|
|
194
|
+
/** The runner pads shape text by 4px on each side; match it so the wrap is the same. */
|
|
195
|
+
const SHAPE_TEXT_PADDING = 4;
|
|
157
196
|
function drawImage(ctx, property, geo, image) {
|
|
158
197
|
inElementSpace(ctx, geo, (c, width, height) => {
|
|
159
198
|
fillBackground(c, property, width, height, geo.radius, false);
|
|
@@ -207,18 +246,14 @@ function drawShape(ctx, type, property, geo) {
|
|
|
207
246
|
c.fillStyle = fill;
|
|
208
247
|
c.fill();
|
|
209
248
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
c.lineWidth = strokeWidth;
|
|
219
|
-
c.strokeStyle = strokeColor;
|
|
220
|
-
c.stroke();
|
|
221
|
-
}
|
|
249
|
+
// Rectangles and ellipses take their outline from the border settings, like
|
|
250
|
+
// on a live page; a new shape stores a 1px grey border with style None.
|
|
251
|
+
strokeBorder(c, property, width, height, geo.radius, ellipse);
|
|
252
|
+
// Text typed into a shape (a Button preset is a shape with text) is part of
|
|
253
|
+
// the picture, the same as it is on a live page.
|
|
254
|
+
const content = typeof property['content'] === 'string' ? property['content'] : '';
|
|
255
|
+
if (content)
|
|
256
|
+
drawTextInBox(c, property, geo.radius, width, height, content, SHAPE_TEXT_PADDING);
|
|
222
257
|
});
|
|
223
258
|
}
|
|
224
259
|
/** Loads one image with EXIF orientation applied, or throws a SouvenirError. */
|
|
@@ -251,6 +286,8 @@ export function drawOrder(page) {
|
|
|
251
286
|
return Object.keys(page.elements ?? {})
|
|
252
287
|
.filter((eid) => SOUVENIR_ELEMENT_TYPES.includes(page.elements[eid]?.type))
|
|
253
288
|
.filter((eid) => !isHidden(page.properties?.[eid] ?? {}))
|
|
289
|
+
// Controls stay on screen over the picture, so they are not in it
|
|
290
|
+
.filter((eid) => !isSouvenirInteractive(page.elements[eid]?.type, page.properties?.[eid] ?? {}))
|
|
254
291
|
.map((eid, index) => ({ eid, index, z: num((page.properties?.[eid] ?? {})['zIndex']) }))
|
|
255
292
|
.sort((a, b) => (a.z === b.z ? a.index - b.index : a.z - b.z))
|
|
256
293
|
.map((entry) => entry.eid);
|
|
@@ -317,6 +354,8 @@ export async function renderSouvenir(page, values = {}, options = {}) {
|
|
|
317
354
|
const type = page.elements[eid]?.type;
|
|
318
355
|
if (type !== 'image' && type !== 'image-frame' && type !== 'bg')
|
|
319
356
|
return;
|
|
357
|
+
if (isUnfilledOutput(eid, values))
|
|
358
|
+
return;
|
|
320
359
|
const property = page.properties?.[eid] ?? {};
|
|
321
360
|
const url = (values[eid] ?? property['source'] ?? '').toString().trim();
|
|
322
361
|
if (!url)
|
|
@@ -335,6 +374,8 @@ export async function renderSouvenir(page, values = {}, options = {}) {
|
|
|
335
374
|
const geo = geometryOf(property);
|
|
336
375
|
if (geo.width <= 0 || geo.height <= 0)
|
|
337
376
|
continue;
|
|
377
|
+
if (isUnfilledOutput(eid, values))
|
|
378
|
+
continue;
|
|
338
379
|
if (type === 'text') {
|
|
339
380
|
const content = (values[eid] ?? property['content'] ?? '').toString();
|
|
340
381
|
drawText(ctx, property, geo, content);
|
package/dist/souvenir/types.d.ts
CHANGED
|
@@ -14,6 +14,19 @@
|
|
|
14
14
|
/** Element types the composer draws. Anything else on the page is skipped. */
|
|
15
15
|
export declare const SOUVENIR_ELEMENT_TYPES: readonly ["bg", "image", "image-frame", "text", "shape-rect", "shape-ellipse", "shape-line"];
|
|
16
16
|
export type SouvenirElementType = (typeof SOUVENIR_ELEMENT_TYPES)[number];
|
|
17
|
+
/**
|
|
18
|
+
* Element types that do something when the participant taps them, or that count
|
|
19
|
+
* down. On a page the module composes and displays itself, the runner keeps
|
|
20
|
+
* these on screen over the picture, so the composer must leave them out or the
|
|
21
|
+
* same thing would appear twice (`qik-sense-runner/src/services/pageRender.ts`
|
|
22
|
+
* keeps the same list).
|
|
23
|
+
*/
|
|
24
|
+
export declare const SOUVENIR_INTERACTIVE_TYPES: readonly ["timeout", "button", "qr", "link"];
|
|
25
|
+
/**
|
|
26
|
+
* True when this element is a control rather than part of the picture: an
|
|
27
|
+
* interactive type, or any element the operator gave a target to.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isSouvenirInteractive(type: string | undefined | null, property?: SouvenirProperty): boolean;
|
|
17
30
|
/** Anything the Builder stores for one element. Values arrive untyped from the API. */
|
|
18
31
|
export type SouvenirProperty = Record<string, unknown>;
|
|
19
32
|
/** One element as the Builder's `detail.elements` records it. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/souvenir/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,8FAQzB,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E,uFAAuF;AACvF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAEvE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,wGAAwG;IACxG,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,iBAAiB,CAAC;IAC1B,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,0EAA0E;AAC1E,qBAAa,aAAc,SAAQ,KAAK;aAEpB,IAAI,EAChB,QAAQ,GACR,UAAU,GACV,WAAW,GACX,oBAAoB,GACpB,cAAc,GACd,eAAe;gBANH,IAAI,EAChB,QAAQ,GACR,UAAU,GACV,WAAW,GACX,oBAAoB,GACpB,cAAc,GACd,eAAe,EACnB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,WAAa,CAAC;AAE5C,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAK5E;AAED,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,EAAE,CAW5E"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/souvenir/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,8FAQzB,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,8CAA+C,CAAC;AAKvF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,QAAQ,GAAE,gBAAqB,GAAG,OAAO,CAG/G;AAED,uFAAuF;AACvF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAEvE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,wGAAwG;IACxG,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,iBAAiB,CAAC;IAC1B,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,0EAA0E;AAC1E,qBAAa,aAAc,SAAQ,KAAK;aAEpB,IAAI,EAChB,QAAQ,GACR,UAAU,GACV,WAAW,GACX,oBAAoB,GACpB,cAAc,GACd,eAAe;gBANH,IAAI,EAChB,QAAQ,GACR,UAAU,GACV,WAAW,GACX,oBAAoB,GACpB,cAAc,GACd,eAAe,EACnB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,WAAa,CAAC;AAE5C,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAK5E;AAED,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,EAAE,CAW5E"}
|
package/dist/souvenir/types.js
CHANGED
|
@@ -21,6 +21,25 @@ export const SOUVENIR_ELEMENT_TYPES = [
|
|
|
21
21
|
'shape-ellipse',
|
|
22
22
|
'shape-line',
|
|
23
23
|
];
|
|
24
|
+
/**
|
|
25
|
+
* Element types that do something when the participant taps them, or that count
|
|
26
|
+
* down. On a page the module composes and displays itself, the runner keeps
|
|
27
|
+
* these on screen over the picture, so the composer must leave them out or the
|
|
28
|
+
* same thing would appear twice (`qik-sense-runner/src/services/pageRender.ts`
|
|
29
|
+
* keeps the same list).
|
|
30
|
+
*/
|
|
31
|
+
export const SOUVENIR_INTERACTIVE_TYPES = ['timeout', 'button', 'qr', 'link'];
|
|
32
|
+
/** Property keys that turn any element into something tappable. */
|
|
33
|
+
const INTERACTIVE_PROPERTIES = ['connection-key', 'url', 'key-code'];
|
|
34
|
+
/**
|
|
35
|
+
* True when this element is a control rather than part of the picture: an
|
|
36
|
+
* interactive type, or any element the operator gave a target to.
|
|
37
|
+
*/
|
|
38
|
+
export function isSouvenirInteractive(type, property = {}) {
|
|
39
|
+
if (SOUVENIR_INTERACTIVE_TYPES.includes(String(type ?? '')))
|
|
40
|
+
return true;
|
|
41
|
+
return INTERACTIVE_PROPERTIES.some((key) => String(property?.[key] ?? '').trim() !== '');
|
|
42
|
+
}
|
|
24
43
|
/** Thrown for every refusal the caller is expected to handle and show. */
|
|
25
44
|
export class SouvenirError extends Error {
|
|
26
45
|
code;
|
package/package.json
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@realnation/builder-shared-sdk",
|
|
3
|
-
"version": "2.4.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"private": false,
|
|
6
|
-
"exports": {
|
|
7
|
-
"./package.json": "./package.json",
|
|
8
|
-
".": {
|
|
9
|
-
"types": "./dist/index.d.ts",
|
|
10
|
-
"import": "./dist/index.js"
|
|
11
|
-
},
|
|
12
|
-
"./runtime": {
|
|
13
|
-
"types": "./dist/runtime/index.d.ts",
|
|
14
|
-
"import": "./dist/runtime/index.js"
|
|
15
|
-
},
|
|
16
|
-
"./runtime/vue": {
|
|
17
|
-
"types": "./dist/runtime/vue/index.d.ts",
|
|
18
|
-
"import": "./dist/runtime/vue/index.js"
|
|
19
|
-
},
|
|
20
|
-
"./interaction": {
|
|
21
|
-
"types": "./dist/interaction/index.d.ts",
|
|
22
|
-
"import": "./dist/interaction/index.js"
|
|
23
|
-
},
|
|
24
|
-
"./interaction/vue": {
|
|
25
|
-
"types": "./dist/interaction/vue.d.ts",
|
|
26
|
-
"import": "./dist/interaction/vue.js"
|
|
27
|
-
},
|
|
28
|
-
"./souvenir": {
|
|
29
|
-
"types": "./dist/souvenir/index.d.ts",
|
|
30
|
-
"import": "./dist/souvenir/index.js"
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"files": [
|
|
34
|
-
"dist"
|
|
35
|
-
],
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsc -p tsconfig.json",
|
|
38
|
-
"clean": "rimraf dist",
|
|
39
|
-
"test": "vitest run",
|
|
40
|
-
"test:watch": "vitest",
|
|
41
|
-
"prepublishOnly": "npm run clean && npm run test && npm run build",
|
|
42
|
-
"prepare": "npm run clean && npm run build"
|
|
43
|
-
},
|
|
44
|
-
"dependencies": {
|
|
45
|
-
"ali-oss": "^6.23.0",
|
|
46
|
-
"axios": "^1.7.7"
|
|
47
|
-
},
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"vue": "^3.4.0"
|
|
50
|
-
},
|
|
51
|
-
"peerDependenciesMeta": {
|
|
52
|
-
"vue": {
|
|
53
|
-
"optional": true
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"devDependencies": {
|
|
57
|
-
"@types/ali-oss": "^6.16.11",
|
|
58
|
-
"@types/node": "^26.1.2",
|
|
59
|
-
"rimraf": "^5.0.5",
|
|
60
|
-
"typescript": "^5.4.5",
|
|
61
|
-
"vitest": "^2.1.0",
|
|
62
|
-
"vue": "^3.4.0"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@realnation/builder-shared-sdk",
|
|
3
|
+
"version": "2.4.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": false,
|
|
6
|
+
"exports": {
|
|
7
|
+
"./package.json": "./package.json",
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./runtime": {
|
|
13
|
+
"types": "./dist/runtime/index.d.ts",
|
|
14
|
+
"import": "./dist/runtime/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./runtime/vue": {
|
|
17
|
+
"types": "./dist/runtime/vue/index.d.ts",
|
|
18
|
+
"import": "./dist/runtime/vue/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./interaction": {
|
|
21
|
+
"types": "./dist/interaction/index.d.ts",
|
|
22
|
+
"import": "./dist/interaction/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./interaction/vue": {
|
|
25
|
+
"types": "./dist/interaction/vue.d.ts",
|
|
26
|
+
"import": "./dist/interaction/vue.js"
|
|
27
|
+
},
|
|
28
|
+
"./souvenir": {
|
|
29
|
+
"types": "./dist/souvenir/index.d.ts",
|
|
30
|
+
"import": "./dist/souvenir/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -p tsconfig.json",
|
|
38
|
+
"clean": "rimraf dist",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"prepublishOnly": "npm run clean && npm run test && npm run build",
|
|
42
|
+
"prepare": "npm run clean && npm run build"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"ali-oss": "^6.23.0",
|
|
46
|
+
"axios": "^1.7.7"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"vue": "^3.4.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"vue": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/ali-oss": "^6.16.11",
|
|
58
|
+
"@types/node": "^26.1.2",
|
|
59
|
+
"rimraf": "^5.0.5",
|
|
60
|
+
"typescript": "^5.4.5",
|
|
61
|
+
"vitest": "^2.1.0",
|
|
62
|
+
"vue": "^3.4.0"
|
|
63
|
+
}
|
|
64
|
+
}
|