Skip to content

Commit 61a616b

Browse files
feat: add status icons (WiFi, signal, battery) to MobilePhoneShape component
1 parent a32f1cc commit 61a616b

1 file changed

Lines changed: 70 additions & 2 deletions

File tree

src/common/components/mock-components/front-containers/mobilephone-shape.tsx

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { forwardRef } from 'react';
2-
import { Group, Rect, Circle } from 'react-konva';
1+
import { forwardRef, useEffect, useState } from 'react';
2+
import { Group, Rect, Circle, Image } from 'react-konva';
33
import { ShapeSizeRestrictions, ShapeType } from '@/core/model';
44
import { ShapeProps } from '../shape.model';
55
import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions';
66
import { useGroupShapeProps } from '../mock-components.utils';
7+
import { loadSvgWithFill } from '@/common/utils/svg.utils';
78

89
const mobilePhoneShapeSizeRestrictions: ShapeSizeRestrictions = {
910
minWidth: 150,
@@ -37,13 +38,45 @@ export const MobilePhoneShape = forwardRef<any, ShapeProps>((props, ref) => {
3738
const speakerRadius = 2;
3839
const buttonRadius = 9;
3940

41+
const [wifiIcon, setWifiIcon] = useState<HTMLImageElement | null>(null);
42+
const [batteryIcon, setBatteryIcon] = useState<HTMLImageElement | null>(null);
43+
const [signalIcon, setSignalIcon] = useState<HTMLImageElement | null>(null);
44+
45+
const adornerIconSize = 20;
46+
const adornerPadding = 5;
47+
const adornerTotalWidth = adornerIconSize * 3 + 17 * 2;
48+
49+
// Calculate inner screen coordinates (excluding frame margins)
50+
const screenX = margin + screenMargin; // Left edge of inner screen
51+
const screenY = screenMargin * 3; // Top edge of inner screen
52+
const screenWidth = restrictedWidth - 2 * margin - 2 * screenMargin; // Available width inside screen
53+
54+
// Position adorner in top-right corner of inner screen
55+
const adornerStartX = screenX + screenWidth - adornerTotalWidth; // Right-aligned positioning
56+
const adornerY = screenY + adornerPadding; // Top-aligned with padding
57+
58+
// Individual icon positions within the adorner
59+
const wifiX = adornerStartX;
60+
const signalX = adornerStartX + 17;
61+
const batteryX = adornerStartX + 20 * 2;
62+
4063
const commonGroupProps = useGroupShapeProps(
4164
props,
4265
restrictedSize,
4366
shapeType,
4467
ref
4568
);
4669

70+
useEffect(() => {
71+
loadSvgWithFill('/icons/wifi.svg', 'black').then(img => setWifiIcon(img));
72+
loadSvgWithFill('/icons/cellsignal.svg', 'black').then(img =>
73+
setSignalIcon(img)
74+
);
75+
loadSvgWithFill('/icons/batteryfull.svg', 'black').then(img =>
76+
setBatteryIcon(img)
77+
);
78+
}, []);
79+
4780
return (
4881
<Group {...commonGroupProps} {...shapeProps}>
4982
{/* Mobile Frame */}
@@ -82,6 +115,41 @@ export const MobilePhoneShape = forwardRef<any, ShapeProps>((props, ref) => {
82115
fill="white"
83116
/>
84117

118+
{/* Adorner */}
119+
120+
{/* Wifi */}
121+
{wifiIcon && (
122+
<Image
123+
image={wifiIcon}
124+
x={wifiX}
125+
y={adornerY - 2}
126+
width={adornerIconSize}
127+
height={adornerIconSize}
128+
/>
129+
)}
130+
131+
{/* Cell signal */}
132+
{signalIcon && (
133+
<Image
134+
image={signalIcon}
135+
x={signalX}
136+
y={adornerY}
137+
width={adornerIconSize}
138+
height={adornerIconSize}
139+
/>
140+
)}
141+
142+
{/* Battery */}
143+
{batteryIcon && (
144+
<Image
145+
image={batteryIcon}
146+
x={batteryX}
147+
y={adornerY}
148+
width={adornerIconSize}
149+
height={adornerIconSize}
150+
/>
151+
)}
152+
85153
{/* Init button */}
86154
<Circle
87155
x={restrictedWidth / 2}

0 commit comments

Comments
 (0)