@mmtitanl/tablets-core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +67 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +67 -42
- package/dist/index.js.map +1 -1
- package/package.json +16 -5
package/dist/index.cjs
CHANGED
|
@@ -329,7 +329,40 @@ function registerDeviceSVG(deviceId, component, frame, screenRect, landscape) {
|
|
|
329
329
|
function getDeviceSVG(deviceId) {
|
|
330
330
|
return SVG_REGISTRY.get(deviceId);
|
|
331
331
|
}
|
|
332
|
-
function
|
|
332
|
+
function readSVGViewBox(svg) {
|
|
333
|
+
const vb = svg.match(/viewBox\s*=\s*["']([^"']+)["']/i);
|
|
334
|
+
if (vb) {
|
|
335
|
+
const parts = vb[1].split(/[\s,]+/).map(Number);
|
|
336
|
+
if (parts.length === 4 && parts.every((n) => Number.isFinite(n))) {
|
|
337
|
+
return { x: parts[0], y: parts[1], w: parts[2], h: parts[3] };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const wm = svg.match(/<svg\b[^>]*\swidth\s*=\s*["']([\d.]+)/i);
|
|
341
|
+
const hm = svg.match(/<svg\b[^>]*\sheight\s*=\s*["']([\d.]+)/i);
|
|
342
|
+
if (wm && hm) return { x: 0, y: 0, w: parseFloat(wm[1]), h: parseFloat(hm[1]) };
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
function injectScreenMask(svg, frame, suffix) {
|
|
346
|
+
if (frame.totalWidth <= 0 || frame.totalHeight <= 0) return svg;
|
|
347
|
+
if (frame.screenWidth <= 0 || frame.screenHeight <= 0) return svg;
|
|
348
|
+
const vb = readSVGViewBox(svg);
|
|
349
|
+
if (!vb) return svg;
|
|
350
|
+
const sx = vb.w / frame.totalWidth;
|
|
351
|
+
const sy = vb.h / frame.totalHeight;
|
|
352
|
+
const x = vb.x + frame.bezelLeft * sx;
|
|
353
|
+
const y = vb.y + frame.bezelTop * sy;
|
|
354
|
+
const w = frame.screenWidth * sx;
|
|
355
|
+
const h = frame.screenHeight * sy;
|
|
356
|
+
const rt = Math.max(0, frame.screenRadiusTop ?? frame.screenRadius) * Math.min(sx, sy);
|
|
357
|
+
const rb = Math.max(0, frame.screenRadiusBottom ?? frame.screenRadius) * Math.min(sx, sy);
|
|
358
|
+
const maskId = `biela-screen-mask-${suffix || "default"}`;
|
|
359
|
+
const innerPath = `M${x + rt},${y} H${x + w - rt} a${rt},${rt} 0 0 1 ${rt},${rt} V${y + h - rb} a${rb},${rb} 0 0 1 ${-rb},${rb} H${x + rb} a${rb},${rb} 0 0 1 ${-rb},${-rb} V${y + rt} a${rt},${rt} 0 0 1 ${rt},${-rt} Z`;
|
|
360
|
+
const maskDef = `<defs><mask id="${maskId}" maskUnits="userSpaceOnUse" x="${vb.x}" y="${vb.y}" width="${vb.w}" height="${vb.h}"><rect x="${vb.x}" y="${vb.y}" width="${vb.w}" height="${vb.h}" fill="white"/><path d="${innerPath}" fill="black"/></mask></defs>`;
|
|
361
|
+
let result = svg.replace(/(<svg\b[^>]*>)/i, (m) => `${m}${maskDef}<g mask="url(#${maskId})">`);
|
|
362
|
+
result = result.replace(/<\/svg\s*>/i, `</g></svg>`);
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
function buildCustomComponent(deviceId, svgString, cropViewBox, suffix, frame, dieCut = false) {
|
|
333
366
|
const scopeKey = suffix ? `${deviceId}${suffix}` : deviceId;
|
|
334
367
|
let svg = (0, import_tablets2.scopeSVGIds)(svgString, scopeKey);
|
|
335
368
|
if (cropViewBox) {
|
|
@@ -349,7 +382,7 @@ function buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, suff
|
|
|
349
382
|
}
|
|
350
383
|
);
|
|
351
384
|
}
|
|
352
|
-
|
|
385
|
+
if (frame && dieCut) svg = injectScreenMask(svg, frame, scopeKey);
|
|
353
386
|
const Component2 = ({ style }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
354
387
|
"span",
|
|
355
388
|
{
|
|
@@ -360,9 +393,10 @@ function buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, suff
|
|
|
360
393
|
Component2.displayName = `CustomDeviceSVG(${scopeKey})`;
|
|
361
394
|
return Component2;
|
|
362
395
|
}
|
|
363
|
-
function registerCustomDeviceSVG(deviceId, svgString, frame, cropViewBox, screenRect, landscape) {
|
|
364
|
-
const
|
|
365
|
-
const
|
|
396
|
+
function registerCustomDeviceSVG(deviceId, svgString, frame, cropViewBox, screenRect, landscape, options) {
|
|
397
|
+
const dieCut = options?.dieCutScreen ?? false;
|
|
398
|
+
const portraitComponent = buildCustomComponent(deviceId, svgString, cropViewBox, "", frame, dieCut);
|
|
399
|
+
const landscapeComponent = landscape ? buildCustomComponent(deviceId, landscape.svgString, landscape.cropViewBox, "-landscape", landscape.frame, dieCut) : void 0;
|
|
366
400
|
SVG_REGISTRY.set(deviceId, {
|
|
367
401
|
component: portraitComponent,
|
|
368
402
|
frame,
|
|
@@ -680,7 +714,9 @@ function DeviceFrame({
|
|
|
680
714
|
manualScale = 1,
|
|
681
715
|
showSafeAreaOverlay = false,
|
|
682
716
|
showScaleBar = true,
|
|
683
|
-
|
|
717
|
+
// Implicit oprit: SVG-urile device-urilor au status bar-ul desenat în ramă
|
|
718
|
+
// (ceas, semnal, baterie); cel sintetic ar apărea dublat peste conținut.
|
|
719
|
+
showStatusBar = false,
|
|
684
720
|
colorScheme = "light",
|
|
685
721
|
iframeRef,
|
|
686
722
|
onColorSchemeChange,
|
|
@@ -762,9 +798,6 @@ function DeviceFrame({
|
|
|
762
798
|
const contentBezelTop = activeFrame?.bezelTop ?? 0;
|
|
763
799
|
const contentScreenW = activeFrame?.screenWidth ?? dw;
|
|
764
800
|
const contentScreenH = activeFrame?.screenHeight ?? dh;
|
|
765
|
-
const baseRadius = activeFrame?.screenRadius ?? meta.screen.cornerRadius ?? 0;
|
|
766
|
-
const radiusTop = activeFrame?.screenRadiusTop ?? baseRadius;
|
|
767
|
-
const radiusBottom = activeFrame?.screenRadiusBottom ?? baseRadius;
|
|
768
801
|
const useRotationFallback = rotateFrame && !hasLandscapeSVG;
|
|
769
802
|
const scalerTransform = useRotationFallback ? `scale(${scale}) translate(0px, ${scalerW}px) rotate(-90deg)` : `scale(${scale})`;
|
|
770
803
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
@@ -803,6 +836,29 @@ function DeviceFrame({
|
|
|
803
836
|
transition: "transform 400ms cubic-bezier(0.4, 0, 0.2, 1)"
|
|
804
837
|
},
|
|
805
838
|
children: [
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
840
|
+
"div",
|
|
841
|
+
{
|
|
842
|
+
className: "bielaframe-content",
|
|
843
|
+
style: {
|
|
844
|
+
position: "absolute",
|
|
845
|
+
left: `${contentBezelLeft / scalerW * 100}%`,
|
|
846
|
+
top: `${contentBezelTop / scalerH * 100}%`,
|
|
847
|
+
width: `${contentScreenW / scalerW * 100}%`,
|
|
848
|
+
height: `${contentScreenH / scalerH * 100}%`,
|
|
849
|
+
overflow: "hidden",
|
|
850
|
+
zIndex: 0,
|
|
851
|
+
background: colorScheme === "dark" ? "#000" : "#fff",
|
|
852
|
+
borderRadius: activeFrame?.screenRadius ?? 0,
|
|
853
|
+
...cssVarsStyle
|
|
854
|
+
},
|
|
855
|
+
children: [
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DeviceErrorBoundary, { children }),
|
|
857
|
+
showStatusBar && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DynamicStatusBar, { contract, orientation, colorScheme }),
|
|
858
|
+
showSafeAreaOverlay && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SafeAreaOverlay, { contract, orientation })
|
|
859
|
+
]
|
|
860
|
+
}
|
|
861
|
+
),
|
|
806
862
|
SVGComponent && portraitFrame && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
807
863
|
"div",
|
|
808
864
|
{
|
|
@@ -850,38 +906,6 @@ function DeviceFrame({
|
|
|
850
906
|
}
|
|
851
907
|
)
|
|
852
908
|
}
|
|
853
|
-
),
|
|
854
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
855
|
-
"div",
|
|
856
|
-
{
|
|
857
|
-
className: "bielaframe-content",
|
|
858
|
-
style: {
|
|
859
|
-
position: "absolute",
|
|
860
|
-
left: contentBezelLeft,
|
|
861
|
-
top: contentBezelTop,
|
|
862
|
-
width: contentScreenW,
|
|
863
|
-
height: contentScreenH,
|
|
864
|
-
// `border-radius + overflow:hidden` clips the <iframe> child
|
|
865
|
-
// in every browser. clip-path is kept as belt-and-braces — some
|
|
866
|
-
// engines apply it to the iframe, some don't. Per-edge radii
|
|
867
|
-
// (top vs bottom) let devices with asymmetric corners — e.g.
|
|
868
|
-
// flat-bottom — clip correctly.
|
|
869
|
-
borderRadius: `${radiusTop}px ${radiusTop}px ${radiusBottom}px ${radiusBottom}px`,
|
|
870
|
-
clipPath: `inset(0 round ${radiusTop}px ${radiusTop}px ${radiusBottom}px ${radiusBottom}px)`,
|
|
871
|
-
overflow: "hidden",
|
|
872
|
-
isolation: "isolate",
|
|
873
|
-
backfaceVisibility: "hidden",
|
|
874
|
-
transform: "translateZ(0)",
|
|
875
|
-
background: colorScheme === "dark" ? "#000" : "#fff",
|
|
876
|
-
zIndex: 5,
|
|
877
|
-
...cssVarsStyle
|
|
878
|
-
},
|
|
879
|
-
children: [
|
|
880
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DeviceErrorBoundary, { children }),
|
|
881
|
-
showStatusBar && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DynamicStatusBar, { contract, orientation, colorScheme }),
|
|
882
|
-
showSafeAreaOverlay && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SafeAreaOverlay, { contract, orientation })
|
|
883
|
-
]
|
|
884
|
-
}
|
|
885
909
|
)
|
|
886
910
|
]
|
|
887
911
|
}
|
|
@@ -1196,7 +1220,8 @@ var CustomSVGStore = class {
|
|
|
1196
1220
|
},
|
|
1197
1221
|
void 0,
|
|
1198
1222
|
sr,
|
|
1199
|
-
landscape
|
|
1223
|
+
landscape,
|
|
1224
|
+
{ dieCutScreen: entry.dieCutScreen ?? false }
|
|
1200
1225
|
);
|
|
1201
1226
|
}
|
|
1202
1227
|
persist(all) {
|