@realnation/builder-shared-sdk 2.4.1 → 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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/souvenir/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,
|
|
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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { computeImageFitDrawRect, hashSignature, normalizeImageFit } from './imageFit.js';
|
|
13
13
|
import { parseSouvenirOutputId } from './outputId.js';
|
|
14
14
|
import { layoutLines, wrapText } from './text.js';
|
|
15
|
-
import { isSouvenirSizeAllowed, SouvenirError, SOUVENIR_ELEMENT_TYPES, } from './types.js';
|
|
15
|
+
import { isSouvenirInteractive, isSouvenirSizeAllowed, SouvenirError, SOUVENIR_ELEMENT_TYPES, } from './types.js';
|
|
16
16
|
const DEFAULT_LINE_HEIGHT = 1.2;
|
|
17
17
|
const TRANSPARENT = 'TRANSPARENT';
|
|
18
18
|
const num = (value, fallback = 0) => {
|
|
@@ -144,39 +144,55 @@ function fontOf(property, fontSize) {
|
|
|
144
144
|
const quoted = /[",]/.test(family) || !/\s/.test(family) ? family : `"${family}"`;
|
|
145
145
|
return `${style}${weight}${fontSize}px ${quoted}`;
|
|
146
146
|
}
|
|
147
|
-
|
|
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) {
|
|
148
152
|
const fontSize = num(property['font-size'], 14);
|
|
149
153
|
if (fontSize <= 0 || !content)
|
|
150
154
|
return;
|
|
151
155
|
const lineHeight = num(property['line-height'], DEFAULT_LINE_HEIGHT) || DEFAULT_LINE_HEIGHT;
|
|
152
156
|
const align = (property['alignment'] || 'center');
|
|
153
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;
|
|
154
188
|
inElementSpace(ctx, geo, (c, width, height) => {
|
|
155
189
|
fillBackground(c, property, width, height, geo.radius, false);
|
|
156
|
-
|
|
157
|
-
c.clip();
|
|
158
|
-
c.font = fontOf(property, fontSize);
|
|
159
|
-
c.textBaseline = 'alphabetic';
|
|
160
|
-
c.textAlign = 'left';
|
|
161
|
-
c.fillStyle = colorOf(property['color']) || '#000000';
|
|
162
|
-
const measure = (text) => c.measureText(text).width;
|
|
163
|
-
const lines = wrapText(content, width, measure);
|
|
164
|
-
const placed = layoutLines(lines, { x: 0, y: 0, width, height }, fontSize, lineHeight, align, vAlign, measure);
|
|
165
|
-
const formatting = Array.isArray(property['formatting']) ? property['formatting'] : [];
|
|
166
|
-
const underline = formatting.includes('Underline');
|
|
167
|
-
const strike = formatting.includes('Strikethrough');
|
|
168
|
-
for (const line of placed) {
|
|
169
|
-
if (!line.text)
|
|
170
|
-
continue;
|
|
171
|
-
c.fillText(line.text, line.x, line.y);
|
|
172
|
-
if (underline)
|
|
173
|
-
c.fillRect(line.x, line.y + fontSize * 0.12, line.width, Math.max(1, fontSize / 16));
|
|
174
|
-
if (strike)
|
|
175
|
-
c.fillRect(line.x, line.y - fontSize * 0.28, line.width, Math.max(1, fontSize / 16));
|
|
176
|
-
}
|
|
190
|
+
drawTextInBox(c, property, geo.radius, width, height, content);
|
|
177
191
|
});
|
|
178
192
|
inElementSpace(ctx, geo, (c, width, height) => strokeBorder(c, property, width, height, geo.radius, false));
|
|
179
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;
|
|
180
196
|
function drawImage(ctx, property, geo, image) {
|
|
181
197
|
inElementSpace(ctx, geo, (c, width, height) => {
|
|
182
198
|
fillBackground(c, property, width, height, geo.radius, false);
|
|
@@ -233,6 +249,11 @@ function drawShape(ctx, type, property, geo) {
|
|
|
233
249
|
// Rectangles and ellipses take their outline from the border settings, like
|
|
234
250
|
// on a live page; a new shape stores a 1px grey border with style None.
|
|
235
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);
|
|
236
257
|
});
|
|
237
258
|
}
|
|
238
259
|
/** Loads one image with EXIF orientation applied, or throws a SouvenirError. */
|
|
@@ -265,6 +286,8 @@ export function drawOrder(page) {
|
|
|
265
286
|
return Object.keys(page.elements ?? {})
|
|
266
287
|
.filter((eid) => SOUVENIR_ELEMENT_TYPES.includes(page.elements[eid]?.type))
|
|
267
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] ?? {}))
|
|
268
291
|
.map((eid, index) => ({ eid, index, z: num((page.properties?.[eid] ?? {})['zIndex']) }))
|
|
269
292
|
.sort((a, b) => (a.z === b.z ? a.index - b.index : a.z - b.z))
|
|
270
293
|
.map((entry) => entry.eid);
|
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;
|