@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.js
CHANGED
|
@@ -271,7 +271,40 @@ function registerDeviceSVG(deviceId, component, frame, screenRect, landscape) {
|
|
|
271
271
|
function getDeviceSVG(deviceId) {
|
|
272
272
|
return SVG_REGISTRY.get(deviceId);
|
|
273
273
|
}
|
|
274
|
-
function
|
|
274
|
+
function readSVGViewBox(svg) {
|
|
275
|
+
const vb = svg.match(/viewBox\s*=\s*["']([^"']+)["']/i);
|
|
276
|
+
if (vb) {
|
|
277
|
+
const parts = vb[1].split(/[\s,]+/).map(Number);
|
|
278
|
+
if (parts.length === 4 && parts.every((n) => Number.isFinite(n))) {
|
|
279
|
+
return { x: parts[0], y: parts[1], w: parts[2], h: parts[3] };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const wm = svg.match(/<svg\b[^>]*\swidth\s*=\s*["']([\d.]+)/i);
|
|
283
|
+
const hm = svg.match(/<svg\b[^>]*\sheight\s*=\s*["']([\d.]+)/i);
|
|
284
|
+
if (wm && hm) return { x: 0, y: 0, w: parseFloat(wm[1]), h: parseFloat(hm[1]) };
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
function injectScreenMask(svg, frame, suffix) {
|
|
288
|
+
if (frame.totalWidth <= 0 || frame.totalHeight <= 0) return svg;
|
|
289
|
+
if (frame.screenWidth <= 0 || frame.screenHeight <= 0) return svg;
|
|
290
|
+
const vb = readSVGViewBox(svg);
|
|
291
|
+
if (!vb) return svg;
|
|
292
|
+
const sx = vb.w / frame.totalWidth;
|
|
293
|
+
const sy = vb.h / frame.totalHeight;
|
|
294
|
+
const x = vb.x + frame.bezelLeft * sx;
|
|
295
|
+
const y = vb.y + frame.bezelTop * sy;
|
|
296
|
+
const w = frame.screenWidth * sx;
|
|
297
|
+
const h = frame.screenHeight * sy;
|
|
298
|
+
const rt = Math.max(0, frame.screenRadiusTop ?? frame.screenRadius) * Math.min(sx, sy);
|
|
299
|
+
const rb = Math.max(0, frame.screenRadiusBottom ?? frame.screenRadius) * Math.min(sx, sy);
|
|
300
|
+
const maskId = `biela-screen-mask-${suffix || "default"}`;
|
|
301
|
+
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`;
|
|
302
|
+
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>`;
|
|
303
|
+
let result = svg.replace(/(<svg\b[^>]*>)/i, (m) => `${m}${maskDef}<g mask="url(#${maskId})">`);
|
|
304
|
+
result = result.replace(/<\/svg\s*>/i, `</g></svg>`);
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
function buildCustomComponent(deviceId, svgString, cropViewBox, suffix, frame, dieCut = false) {
|
|
275
308
|
const scopeKey = suffix ? `${deviceId}${suffix}` : deviceId;
|
|
276
309
|
let svg = scopeSVGIds(svgString, scopeKey);
|
|
277
310
|
if (cropViewBox) {
|
|
@@ -291,7 +324,7 @@ function buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, suff
|
|
|
291
324
|
}
|
|
292
325
|
);
|
|
293
326
|
}
|
|
294
|
-
|
|
327
|
+
if (frame && dieCut) svg = injectScreenMask(svg, frame, scopeKey);
|
|
295
328
|
const Component2 = ({ style }) => /* @__PURE__ */ jsx(
|
|
296
329
|
"span",
|
|
297
330
|
{
|
|
@@ -302,9 +335,10 @@ function buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, suff
|
|
|
302
335
|
Component2.displayName = `CustomDeviceSVG(${scopeKey})`;
|
|
303
336
|
return Component2;
|
|
304
337
|
}
|
|
305
|
-
function registerCustomDeviceSVG(deviceId, svgString, frame, cropViewBox, screenRect, landscape) {
|
|
306
|
-
const
|
|
307
|
-
const
|
|
338
|
+
function registerCustomDeviceSVG(deviceId, svgString, frame, cropViewBox, screenRect, landscape, options) {
|
|
339
|
+
const dieCut = options?.dieCutScreen ?? false;
|
|
340
|
+
const portraitComponent = buildCustomComponent(deviceId, svgString, cropViewBox, "", frame, dieCut);
|
|
341
|
+
const landscapeComponent = landscape ? buildCustomComponent(deviceId, landscape.svgString, landscape.cropViewBox, "-landscape", landscape.frame, dieCut) : void 0;
|
|
308
342
|
SVG_REGISTRY.set(deviceId, {
|
|
309
343
|
component: portraitComponent,
|
|
310
344
|
frame,
|
|
@@ -689,7 +723,9 @@ function DeviceFrame({
|
|
|
689
723
|
manualScale = 1,
|
|
690
724
|
showSafeAreaOverlay = false,
|
|
691
725
|
showScaleBar = true,
|
|
692
|
-
|
|
726
|
+
// Implicit oprit: SVG-urile device-urilor au status bar-ul desenat în ramă
|
|
727
|
+
// (ceas, semnal, baterie); cel sintetic ar apărea dublat peste conținut.
|
|
728
|
+
showStatusBar = false,
|
|
693
729
|
colorScheme = "light",
|
|
694
730
|
iframeRef,
|
|
695
731
|
onColorSchemeChange,
|
|
@@ -771,9 +807,6 @@ function DeviceFrame({
|
|
|
771
807
|
const contentBezelTop = activeFrame?.bezelTop ?? 0;
|
|
772
808
|
const contentScreenW = activeFrame?.screenWidth ?? dw;
|
|
773
809
|
const contentScreenH = activeFrame?.screenHeight ?? dh;
|
|
774
|
-
const baseRadius = activeFrame?.screenRadius ?? meta.screen.cornerRadius ?? 0;
|
|
775
|
-
const radiusTop = activeFrame?.screenRadiusTop ?? baseRadius;
|
|
776
|
-
const radiusBottom = activeFrame?.screenRadiusBottom ?? baseRadius;
|
|
777
810
|
const useRotationFallback = rotateFrame && !hasLandscapeSVG;
|
|
778
811
|
const scalerTransform = useRotationFallback ? `scale(${scale}) translate(0px, ${scalerW}px) rotate(-90deg)` : `scale(${scale})`;
|
|
779
812
|
return /* @__PURE__ */ jsxs4(
|
|
@@ -812,6 +845,29 @@ function DeviceFrame({
|
|
|
812
845
|
transition: "transform 400ms cubic-bezier(0.4, 0, 0.2, 1)"
|
|
813
846
|
},
|
|
814
847
|
children: [
|
|
848
|
+
/* @__PURE__ */ jsxs4(
|
|
849
|
+
"div",
|
|
850
|
+
{
|
|
851
|
+
className: "bielaframe-content",
|
|
852
|
+
style: {
|
|
853
|
+
position: "absolute",
|
|
854
|
+
left: `${contentBezelLeft / scalerW * 100}%`,
|
|
855
|
+
top: `${contentBezelTop / scalerH * 100}%`,
|
|
856
|
+
width: `${contentScreenW / scalerW * 100}%`,
|
|
857
|
+
height: `${contentScreenH / scalerH * 100}%`,
|
|
858
|
+
overflow: "hidden",
|
|
859
|
+
zIndex: 0,
|
|
860
|
+
background: colorScheme === "dark" ? "#000" : "#fff",
|
|
861
|
+
borderRadius: activeFrame?.screenRadius ?? 0,
|
|
862
|
+
...cssVarsStyle
|
|
863
|
+
},
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ jsx6(DeviceErrorBoundary, { children }),
|
|
866
|
+
showStatusBar && /* @__PURE__ */ jsx6(DynamicStatusBar, { contract, orientation, colorScheme }),
|
|
867
|
+
showSafeAreaOverlay && /* @__PURE__ */ jsx6(SafeAreaOverlay, { contract, orientation })
|
|
868
|
+
]
|
|
869
|
+
}
|
|
870
|
+
),
|
|
815
871
|
SVGComponent && portraitFrame && /* @__PURE__ */ jsx6(
|
|
816
872
|
"div",
|
|
817
873
|
{
|
|
@@ -859,38 +915,6 @@ function DeviceFrame({
|
|
|
859
915
|
}
|
|
860
916
|
)
|
|
861
917
|
}
|
|
862
|
-
),
|
|
863
|
-
/* @__PURE__ */ jsxs4(
|
|
864
|
-
"div",
|
|
865
|
-
{
|
|
866
|
-
className: "bielaframe-content",
|
|
867
|
-
style: {
|
|
868
|
-
position: "absolute",
|
|
869
|
-
left: contentBezelLeft,
|
|
870
|
-
top: contentBezelTop,
|
|
871
|
-
width: contentScreenW,
|
|
872
|
-
height: contentScreenH,
|
|
873
|
-
// `border-radius + overflow:hidden` clips the <iframe> child
|
|
874
|
-
// in every browser. clip-path is kept as belt-and-braces — some
|
|
875
|
-
// engines apply it to the iframe, some don't. Per-edge radii
|
|
876
|
-
// (top vs bottom) let devices with asymmetric corners — e.g.
|
|
877
|
-
// flat-bottom — clip correctly.
|
|
878
|
-
borderRadius: `${radiusTop}px ${radiusTop}px ${radiusBottom}px ${radiusBottom}px`,
|
|
879
|
-
clipPath: `inset(0 round ${radiusTop}px ${radiusTop}px ${radiusBottom}px ${radiusBottom}px)`,
|
|
880
|
-
overflow: "hidden",
|
|
881
|
-
isolation: "isolate",
|
|
882
|
-
backfaceVisibility: "hidden",
|
|
883
|
-
transform: "translateZ(0)",
|
|
884
|
-
background: colorScheme === "dark" ? "#000" : "#fff",
|
|
885
|
-
zIndex: 5,
|
|
886
|
-
...cssVarsStyle
|
|
887
|
-
},
|
|
888
|
-
children: [
|
|
889
|
-
/* @__PURE__ */ jsx6(DeviceErrorBoundary, { children }),
|
|
890
|
-
showStatusBar && /* @__PURE__ */ jsx6(DynamicStatusBar, { contract, orientation, colorScheme }),
|
|
891
|
-
showSafeAreaOverlay && /* @__PURE__ */ jsx6(SafeAreaOverlay, { contract, orientation })
|
|
892
|
-
]
|
|
893
|
-
}
|
|
894
918
|
)
|
|
895
919
|
]
|
|
896
920
|
}
|
|
@@ -1205,7 +1229,8 @@ var CustomSVGStore = class {
|
|
|
1205
1229
|
},
|
|
1206
1230
|
void 0,
|
|
1207
1231
|
sr,
|
|
1208
|
-
landscape
|
|
1232
|
+
landscape,
|
|
1233
|
+
{ dieCutScreen: entry.dieCutScreen ?? false }
|
|
1209
1234
|
);
|
|
1210
1235
|
}
|
|
1211
1236
|
persist(all) {
|