@mmtitanl/tablets-core 0.3.0 → 0.4.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 +93 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +93 -46
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -700,6 +700,14 @@ function ensureBuiltinsRegistered() {
|
|
|
700
700
|
}
|
|
701
701
|
didAutoRegister = true;
|
|
702
702
|
}
|
|
703
|
+
var ROTATE_MS = 450;
|
|
704
|
+
var EASE = "cubic-bezier(0.4, 0, 0.2, 1)";
|
|
705
|
+
var SVG_FADE_MS = 240;
|
|
706
|
+
var SVG_FADE_DELAY_MS = 100;
|
|
707
|
+
var CONTENT_FADE_OUT_MS = 140;
|
|
708
|
+
var CONTENT_SWAP_MS = 180;
|
|
709
|
+
var CONTENT_FADE_IN_DELAY_MS = 220;
|
|
710
|
+
var CONTENT_FADE_IN_MS = 230;
|
|
703
711
|
function sendDeviceInfo(iframe, contract, orientation) {
|
|
704
712
|
if (!iframe?.contentWindow) return;
|
|
705
713
|
iframe.contentWindow.postMessage(
|
|
@@ -737,7 +745,24 @@ function DeviceFrame({
|
|
|
737
745
|
const resolvedId = device ?? deviceId;
|
|
738
746
|
if (!resolvedId) throw new Error("DeviceFrame requires `device` or `deviceId`");
|
|
739
747
|
const meta = getDeviceMetadata(resolvedId);
|
|
740
|
-
const
|
|
748
|
+
const [contentOrientation, setContentOrientation] = useState8(orientation);
|
|
749
|
+
const [contentVisible, setContentVisible] = useState8(true);
|
|
750
|
+
const animTimers = useRef2([]);
|
|
751
|
+
const prevOrientation = useRef2(orientation);
|
|
752
|
+
useEffect5(() => {
|
|
753
|
+
if (prevOrientation.current === orientation) return;
|
|
754
|
+
prevOrientation.current = orientation;
|
|
755
|
+
for (const t of animTimers.current) window.clearTimeout(t);
|
|
756
|
+
setContentVisible(false);
|
|
757
|
+
animTimers.current = [
|
|
758
|
+
window.setTimeout(() => setContentOrientation(orientation), CONTENT_SWAP_MS),
|
|
759
|
+
window.setTimeout(() => setContentVisible(true), CONTENT_FADE_IN_DELAY_MS)
|
|
760
|
+
];
|
|
761
|
+
}, [orientation]);
|
|
762
|
+
useEffect5(() => () => {
|
|
763
|
+
for (const t of animTimers.current) window.clearTimeout(t);
|
|
764
|
+
}, []);
|
|
765
|
+
const contract = useMemo3(() => getDeviceContract2(resolvedId, contentOrientation), [resolvedId, contentOrientation]);
|
|
741
766
|
const portW = meta.screen.width;
|
|
742
767
|
const portH = meta.screen.height;
|
|
743
768
|
const rotateFrame = orientation === "landscape";
|
|
@@ -746,12 +771,36 @@ function DeviceFrame({
|
|
|
746
771
|
const sentinelRef = useRef2(null);
|
|
747
772
|
const frameContainerRef = useRef2(null);
|
|
748
773
|
const containerSize = useContainerSize(sentinelRef);
|
|
749
|
-
const
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
const
|
|
753
|
-
const
|
|
754
|
-
const
|
|
774
|
+
const svgEntry = getDeviceSVG(resolvedId);
|
|
775
|
+
const SVGComponent = svgEntry?.component ?? null;
|
|
776
|
+
const LandscapeSVGComponent = svgEntry?.landscapeComponent ?? null;
|
|
777
|
+
const registeredLandscapeFrame = svgEntry?.landscapeFrame;
|
|
778
|
+
const hasLandscapeSVG = !!LandscapeSVGComponent && !!registeredLandscapeFrame;
|
|
779
|
+
const portFrame = svgEntry?.frame ?? {
|
|
780
|
+
bezelTop: 0,
|
|
781
|
+
bezelBottom: 0,
|
|
782
|
+
bezelLeft: 0,
|
|
783
|
+
bezelRight: 0,
|
|
784
|
+
totalWidth: portW,
|
|
785
|
+
totalHeight: portH,
|
|
786
|
+
screenWidth: portW,
|
|
787
|
+
screenHeight: portH,
|
|
788
|
+
screenRadius: 0
|
|
789
|
+
};
|
|
790
|
+
const landFrame = hasLandscapeSVG && registeredLandscapeFrame ? registeredLandscapeFrame : {
|
|
791
|
+
bezelTop: portFrame.totalWidth - portFrame.bezelLeft - portFrame.screenWidth,
|
|
792
|
+
bezelBottom: portFrame.bezelLeft,
|
|
793
|
+
bezelLeft: portFrame.bezelTop,
|
|
794
|
+
bezelRight: portFrame.totalHeight - portFrame.bezelTop - portFrame.screenHeight,
|
|
795
|
+
totalWidth: portFrame.totalHeight,
|
|
796
|
+
totalHeight: portFrame.totalWidth,
|
|
797
|
+
screenWidth: portFrame.screenHeight,
|
|
798
|
+
screenHeight: portFrame.screenWidth,
|
|
799
|
+
screenRadius: portFrame.screenRadius
|
|
800
|
+
};
|
|
801
|
+
const activeFrame = rotateFrame ? landFrame : portFrame;
|
|
802
|
+
const fitW = activeFrame.totalWidth;
|
|
803
|
+
const fitH = activeFrame.totalHeight;
|
|
755
804
|
const fitResult = useMemo3(
|
|
756
805
|
() => computeFullScale(fitW, fitH, containerSize.width, containerSize.height, {
|
|
757
806
|
snapToSteps: scaleMode === "steps"
|
|
@@ -774,41 +823,32 @@ function DeviceFrame({
|
|
|
774
823
|
}, [scale, onScaleChange]);
|
|
775
824
|
useEffect5(() => {
|
|
776
825
|
if (!iframeRef?.current) return;
|
|
777
|
-
sendDeviceInfo(iframeRef.current, contract,
|
|
778
|
-
const onLoad = () => sendDeviceInfo(iframeRef.current, contract,
|
|
826
|
+
sendDeviceInfo(iframeRef.current, contract, contentOrientation);
|
|
827
|
+
const onLoad = () => sendDeviceInfo(iframeRef.current, contract, contentOrientation);
|
|
779
828
|
iframeRef.current.addEventListener("load", onLoad);
|
|
780
829
|
return () => iframeRef.current?.removeEventListener("load", onLoad);
|
|
781
|
-
}, [iframeRef, contract,
|
|
830
|
+
}, [iframeRef, contract, contentOrientation]);
|
|
782
831
|
useEffect5(() => {
|
|
783
832
|
if (!iframeRef) return;
|
|
784
833
|
const handler = (event) => {
|
|
785
834
|
const data = event.data;
|
|
786
835
|
if (!data || typeof data !== "object") return;
|
|
787
836
|
if (data.type === "biela:requestDeviceInfo") {
|
|
788
|
-
sendDeviceInfo(iframeRef.current, contract,
|
|
837
|
+
sendDeviceInfo(iframeRef.current, contract, contentOrientation);
|
|
789
838
|
} else if (data.type === "biela:colorScheme" && data.payload?.scheme) {
|
|
790
839
|
onColorSchemeChange?.(data.payload.scheme);
|
|
791
840
|
}
|
|
792
841
|
};
|
|
793
842
|
window.addEventListener("message", handler);
|
|
794
843
|
return () => window.removeEventListener("message", handler);
|
|
795
|
-
}, [iframeRef, contract,
|
|
796
|
-
const svgEntry = getDeviceSVG(resolvedId);
|
|
797
|
-
const SVGComponent = svgEntry?.component ?? null;
|
|
798
|
-
const portraitFrame = svgEntry?.frame;
|
|
799
|
-
const LandscapeSVGComponent = svgEntry?.landscapeComponent ?? null;
|
|
800
|
-
const landscapeFrame = svgEntry?.landscapeFrame;
|
|
801
|
-
const hasLandscapeSVG = !!LandscapeSVGComponent && !!landscapeFrame;
|
|
844
|
+
}, [iframeRef, contract, contentOrientation, onColorSchemeChange]);
|
|
802
845
|
const cssVarsStyle = contract.cssVariables;
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
const
|
|
806
|
-
const
|
|
807
|
-
const
|
|
808
|
-
const
|
|
809
|
-
const contentScreenH = activeFrame?.screenHeight ?? dh;
|
|
810
|
-
const useRotationFallback = rotateFrame && !hasLandscapeSVG;
|
|
811
|
-
const scalerTransform = useRotationFallback ? `scale(${scale}) translate(0px, ${scalerW}px) rotate(-90deg)` : `scale(${scale})`;
|
|
846
|
+
const scalerW = portFrame.totalWidth;
|
|
847
|
+
const scalerH = portFrame.totalHeight;
|
|
848
|
+
const scalerTransform = rotateFrame ? `scale(${scale}) translate(0px, ${landFrame.totalHeight}px) rotate(-90deg)` : `scale(${scale})`;
|
|
849
|
+
const contentLandscape = contentOrientation === "landscape";
|
|
850
|
+
const contentBox = contentLandscape ? { left: landFrame.totalHeight - landFrame.bezelTop, top: landFrame.bezelLeft, width: landFrame.screenWidth, height: landFrame.screenHeight } : { left: portFrame.bezelLeft, top: portFrame.bezelTop, width: portFrame.screenWidth, height: portFrame.screenHeight };
|
|
851
|
+
const contentRadius = contentLandscape ? landFrame.screenRadius : portFrame.screenRadius;
|
|
812
852
|
return /* @__PURE__ */ jsxs4(
|
|
813
853
|
"div",
|
|
814
854
|
{
|
|
@@ -825,8 +865,10 @@ function DeviceFrame({
|
|
|
825
865
|
height: hostHeight,
|
|
826
866
|
position: "relative",
|
|
827
867
|
flexShrink: 0,
|
|
828
|
-
overflow:
|
|
829
|
-
|
|
868
|
+
// No overflow clipping: at rest the frame fits the host exactly; while
|
|
869
|
+
// rotating, the corners sweep outside and clipping them flat looks bad.
|
|
870
|
+
// The sentinel still clips at the component boundary.
|
|
871
|
+
transition: `width ${ROTATE_MS}ms ${EASE}, height ${ROTATE_MS}ms ${EASE}`
|
|
830
872
|
},
|
|
831
873
|
children: /* @__PURE__ */ jsxs4(
|
|
832
874
|
"div",
|
|
@@ -842,7 +884,7 @@ function DeviceFrame({
|
|
|
842
884
|
transform: scalerTransform,
|
|
843
885
|
transformOrigin: "top left",
|
|
844
886
|
willChange: "transform",
|
|
845
|
-
transition:
|
|
887
|
+
transition: `transform ${ROTATE_MS}ms ${EASE}`
|
|
846
888
|
},
|
|
847
889
|
children: [
|
|
848
890
|
/* @__PURE__ */ jsxs4(
|
|
@@ -851,24 +893,27 @@ function DeviceFrame({
|
|
|
851
893
|
className: "bielaframe-content",
|
|
852
894
|
style: {
|
|
853
895
|
position: "absolute",
|
|
854
|
-
left:
|
|
855
|
-
top:
|
|
856
|
-
width:
|
|
857
|
-
height:
|
|
896
|
+
left: contentBox.left,
|
|
897
|
+
top: contentBox.top,
|
|
898
|
+
width: contentBox.width,
|
|
899
|
+
height: contentBox.height,
|
|
900
|
+
...contentLandscape ? { transform: "rotate(90deg)", transformOrigin: "top left" } : null,
|
|
858
901
|
overflow: "hidden",
|
|
859
902
|
zIndex: 0,
|
|
860
903
|
background: colorScheme === "dark" ? "#000" : "#fff",
|
|
861
|
-
borderRadius:
|
|
904
|
+
borderRadius: contentRadius,
|
|
905
|
+
opacity: contentVisible ? 1 : 0,
|
|
906
|
+
transition: `opacity ${contentVisible ? CONTENT_FADE_IN_MS : CONTENT_FADE_OUT_MS}ms ${EASE}`,
|
|
862
907
|
...cssVarsStyle
|
|
863
908
|
},
|
|
864
909
|
children: [
|
|
865
910
|
/* @__PURE__ */ jsx6(DeviceErrorBoundary, { children }),
|
|
866
|
-
showStatusBar && /* @__PURE__ */ jsx6(DynamicStatusBar, { contract, orientation, colorScheme }),
|
|
867
|
-
showSafeAreaOverlay && /* @__PURE__ */ jsx6(SafeAreaOverlay, { contract, orientation })
|
|
911
|
+
showStatusBar && /* @__PURE__ */ jsx6(DynamicStatusBar, { contract, orientation: contentOrientation, colorScheme }),
|
|
912
|
+
showSafeAreaOverlay && /* @__PURE__ */ jsx6(SafeAreaOverlay, { contract, orientation: contentOrientation })
|
|
868
913
|
]
|
|
869
914
|
}
|
|
870
915
|
),
|
|
871
|
-
SVGComponent &&
|
|
916
|
+
SVGComponent && /* @__PURE__ */ jsx6(
|
|
872
917
|
"div",
|
|
873
918
|
{
|
|
874
919
|
"aria-hidden": true,
|
|
@@ -876,12 +921,12 @@ function DeviceFrame({
|
|
|
876
921
|
position: "absolute",
|
|
877
922
|
top: 0,
|
|
878
923
|
left: 0,
|
|
879
|
-
width:
|
|
880
|
-
height:
|
|
924
|
+
width: portFrame.totalWidth,
|
|
925
|
+
height: portFrame.totalHeight,
|
|
881
926
|
pointerEvents: "none",
|
|
882
927
|
zIndex: 1,
|
|
883
928
|
opacity: hasLandscapeSVG && rotateFrame ? 0 : 1,
|
|
884
|
-
transition:
|
|
929
|
+
transition: `opacity ${SVG_FADE_MS}ms ${EASE} ${SVG_FADE_DELAY_MS}ms`
|
|
885
930
|
},
|
|
886
931
|
children: /* @__PURE__ */ jsx6(
|
|
887
932
|
SVGComponent,
|
|
@@ -892,20 +937,22 @@ function DeviceFrame({
|
|
|
892
937
|
)
|
|
893
938
|
}
|
|
894
939
|
),
|
|
895
|
-
hasLandscapeSVG && LandscapeSVGComponent &&
|
|
940
|
+
hasLandscapeSVG && LandscapeSVGComponent && /* @__PURE__ */ jsx6(
|
|
896
941
|
"div",
|
|
897
942
|
{
|
|
898
943
|
"aria-hidden": true,
|
|
899
944
|
style: {
|
|
900
945
|
position: "absolute",
|
|
946
|
+
left: landFrame.totalHeight,
|
|
901
947
|
top: 0,
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
948
|
+
width: landFrame.totalWidth,
|
|
949
|
+
height: landFrame.totalHeight,
|
|
950
|
+
transform: "rotate(90deg)",
|
|
951
|
+
transformOrigin: "top left",
|
|
905
952
|
pointerEvents: "none",
|
|
906
953
|
zIndex: 1,
|
|
907
954
|
opacity: rotateFrame ? 1 : 0,
|
|
908
|
-
transition:
|
|
955
|
+
transition: `opacity ${SVG_FADE_MS}ms ${EASE} ${SVG_FADE_DELAY_MS}ms`
|
|
909
956
|
},
|
|
910
957
|
children: /* @__PURE__ */ jsx6(
|
|
911
958
|
LandscapeSVGComponent,
|