ebta-workflow-prod 4.0.23 → 4.0.24
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/build/activitypack.json +1 -1
- package/build/main.js +2 -2
- package/ebta-workflow-prod-4.0.24.tgz +0 -0
- package/package.json +1 -1
- package/src/elements/Viewer360.tsx +154 -59
|
Binary file
|
package/package.json
CHANGED
|
@@ -12,6 +12,14 @@ import { Pannellum } from "pannellum-react-update";
|
|
|
12
12
|
* runtime, and passed in as `props`. You can update the properties by using
|
|
13
13
|
* `props.setProperty(key, value)`.
|
|
14
14
|
*/
|
|
15
|
+
|
|
16
|
+
interface Camera {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
pitch: number;
|
|
20
|
+
yaw: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
interface Viewer360Props extends FormElementProps<string> {
|
|
16
24
|
value: string //url of image
|
|
17
25
|
pitch: number; //pitch of the image
|
|
@@ -24,6 +32,7 @@ interface Viewer360Props extends FormElementProps<string> {
|
|
|
24
32
|
width?: string;
|
|
25
33
|
height?: string;
|
|
26
34
|
};
|
|
35
|
+
cameras?: Camera[];
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
/**
|
|
@@ -33,49 +42,155 @@ interface Viewer360Props extends FormElementProps<string> {
|
|
|
33
42
|
* @param props The props that will be provided by the Workflow runtime.
|
|
34
43
|
*/
|
|
35
44
|
function Viewer360(props: Viewer360Props): React.ReactElement {
|
|
36
|
-
const { setting, value, pitch, yaw, hfov, minHfov, haov, vaov, raiseEvent } = props;
|
|
45
|
+
const { setting, value, pitch, yaw, hfov, minHfov, haov, vaov, cameras = [], raiseEvent } = props;
|
|
37
46
|
|
|
38
|
-
const handleHotspotClick = (
|
|
39
|
-
raiseEvent("clicked", JSON.stringify({ args }))
|
|
47
|
+
const handleHotspotClick = (_evt: MouseEvent, args: { id: string; name: string }) => {
|
|
48
|
+
raiseEvent("clicked", JSON.stringify({ id: args.id, name: args.name }));
|
|
40
49
|
};
|
|
41
50
|
|
|
42
|
-
// Copy SVG assets to consumer's public folder and reference them
|
|
43
51
|
React.useEffect(() => {
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
const css = `
|
|
53
|
+
/* ── hotspot host ── */
|
|
46
54
|
.pnlm-hotspot.custom-hotspot {
|
|
47
|
-
width:
|
|
48
|
-
height:
|
|
49
|
-
overflow: visible;
|
|
55
|
+
width: 40px;
|
|
56
|
+
height: 40px;
|
|
50
57
|
background: none;
|
|
51
58
|
border: none;
|
|
59
|
+
overflow: visible;
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ── pulse ring ── */
|
|
66
|
+
.hs-btn {
|
|
67
|
+
position: relative;
|
|
68
|
+
width: 36px;
|
|
69
|
+
height: 36px;
|
|
70
|
+
border-radius: 50%;
|
|
71
|
+
background: rgba(255, 255, 255, 0.50);
|
|
72
|
+
backdrop-filter: blur(4px);
|
|
73
|
+
border: 1.5px solid rgba(255, 255, 255, 0.75);
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
justify-content: center;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
transition: background 0.2s ease, transform 0.2s ease;
|
|
79
|
+
animation: hs-shake 3s ease-in-out infinite;
|
|
52
80
|
}
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
|
|
82
|
+
.hs-btn::before {
|
|
83
|
+
content: '';
|
|
84
|
+
position: absolute;
|
|
85
|
+
inset: -6px;
|
|
86
|
+
border-radius: 50%;
|
|
87
|
+
border: 1.5px solid rgba(255, 255, 255, 0.35);
|
|
88
|
+
animation: hs-pulse 2.4s ease-out infinite;
|
|
89
|
+
pointer-events: none;
|
|
55
90
|
}
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
|
|
92
|
+
@keyframes hs-pulse {
|
|
93
|
+
0% { transform: scale(0.85); opacity: 0.6; }
|
|
94
|
+
70% { transform: scale(1.35); opacity: 0; }
|
|
95
|
+
100% { transform: scale(1.35); opacity: 0; }
|
|
58
96
|
}
|
|
59
|
-
|
|
60
|
-
|
|
97
|
+
|
|
98
|
+
@keyframes hs-shake {
|
|
99
|
+
0%, 100% { transform: translateY(0px); }
|
|
100
|
+
20% { transform: translateY(-3px); }
|
|
101
|
+
40% { transform: translateY(0px); }
|
|
102
|
+
60% { transform: translateY(-2px); }
|
|
103
|
+
80% { transform: translateY(0px); }
|
|
61
104
|
}
|
|
62
|
-
|
|
63
|
-
|
|
105
|
+
|
|
106
|
+
/* ── camera icon svg ── */
|
|
107
|
+
.hs-icon {
|
|
108
|
+
width: 16px;
|
|
109
|
+
height: 16px;
|
|
110
|
+
fill: white;
|
|
111
|
+
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4));
|
|
112
|
+
flex-shrink: 0;
|
|
64
113
|
}
|
|
65
|
-
|
|
66
|
-
|
|
114
|
+
|
|
115
|
+
/* ── tooltip ── */
|
|
116
|
+
.hs-tooltip {
|
|
117
|
+
position: absolute;
|
|
118
|
+
bottom: calc(100% + 10px);
|
|
119
|
+
left: 50%;
|
|
120
|
+
transform: translateX(-50%);
|
|
121
|
+
background: rgba(0, 0, 0, 0.45);
|
|
122
|
+
backdrop-filter: blur(6px);
|
|
123
|
+
color: #fff;
|
|
124
|
+
font-size: 12px;
|
|
125
|
+
font-family: sans-serif;
|
|
126
|
+
font-weight: 500;
|
|
127
|
+
white-space: nowrap;
|
|
128
|
+
padding: 4px 10px;
|
|
129
|
+
border-radius: 4px;
|
|
130
|
+
border: 1px solid rgba(255,255,255,0.2);
|
|
131
|
+
pointer-events: none;
|
|
132
|
+
opacity: 0;
|
|
133
|
+
transition: opacity 0.18s ease;
|
|
67
134
|
}
|
|
68
|
-
`;
|
|
69
135
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
136
|
+
.hs-tooltip::after {
|
|
137
|
+
content: '';
|
|
138
|
+
position: absolute;
|
|
139
|
+
top: 100%;
|
|
140
|
+
left: 50%;
|
|
141
|
+
transform: translateX(-50%);
|
|
142
|
+
border: 5px solid transparent;
|
|
143
|
+
border-top-color: rgba(0,0,0,0.45);
|
|
144
|
+
}
|
|
73
145
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
146
|
+
/* show tooltip + brighten on hover */
|
|
147
|
+
.hs-btn:hover {
|
|
148
|
+
background: rgba(255, 255, 255, 0.75);
|
|
149
|
+
animation: none;
|
|
150
|
+
transform: none;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.hs-btn:hover .hs-tooltip {
|
|
154
|
+
opacity: 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ── pannellum svg assets ── */
|
|
158
|
+
.pnlm-container { background-image: url('/img/background.svg'); }
|
|
159
|
+
.pnlm-compass { background-image: url('/img/compass.svg'); }
|
|
160
|
+
.pnlm-grab { cursor: url('/img/grab.svg'), grab; }
|
|
161
|
+
.pnlm-grabbing { cursor: url('/img/grabbing.svg'), grabbing; }
|
|
162
|
+
.pnlm-sprite { background-image: url('/img/sprites.svg'); }
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
const el = document.createElement("style");
|
|
166
|
+
el.textContent = css;
|
|
167
|
+
document.head.appendChild(el);
|
|
168
|
+
return () => { document.head.removeChild(el); };
|
|
77
169
|
}, []);
|
|
78
170
|
|
|
171
|
+
// Tooltip + icon injected once per hotspot div
|
|
172
|
+
const buildTooltip = (name: string) => (hotSpotDiv: HTMLElement) => {
|
|
173
|
+
hotSpotDiv.style.cssText = "width:40px;height:40px;overflow:visible;";
|
|
174
|
+
|
|
175
|
+
const btn = document.createElement("div");
|
|
176
|
+
btn.className = "hs-btn";
|
|
177
|
+
|
|
178
|
+
// Camera SVG icon
|
|
179
|
+
btn.innerHTML = `
|
|
180
|
+
<svg class="hs-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
181
|
+
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8
|
|
182
|
+
a2 2 0 0 1 2-2h4l2-3h6l2 3h4
|
|
183
|
+
a2 2 0 0 1 2 2z"/>
|
|
184
|
+
<circle cx="12" cy="13" r="4" fill="none"
|
|
185
|
+
stroke="rgba(0,0,0,0.55)" stroke-width="1.8"/>
|
|
186
|
+
</svg>
|
|
187
|
+
<div class="hs-tooltip">${name}</div>
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
hotSpotDiv.appendChild(btn);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
|
|
79
194
|
|
|
80
195
|
console.log("rendering 360VIEWER")
|
|
81
196
|
return (
|
|
@@ -96,39 +211,19 @@ function Viewer360(props: Viewer360Props): React.ReactElement {
|
|
|
96
211
|
console.log("panorama loaded");
|
|
97
212
|
}}
|
|
98
213
|
>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
padding: 6px 12px;
|
|
113
|
-
cursor: pointer;
|
|
114
|
-
font-size: 13px;
|
|
115
|
-
white-space: nowrap;
|
|
116
|
-
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
|
|
117
|
-
transform: translate(-50%, -100%);
|
|
118
|
-
`;
|
|
119
|
-
btn.textContent = "📍 View Detail";
|
|
120
|
-
hotSpotDiv.appendChild(btn); // append a child, don't style the host div
|
|
121
|
-
}}
|
|
122
|
-
tooltipArg={{}}
|
|
123
|
-
/>
|
|
124
|
-
|
|
125
|
-
<Pannellum.Hotspot
|
|
126
|
-
type="info"
|
|
127
|
-
pitch={31}
|
|
128
|
-
yaw={-57}
|
|
129
|
-
text="Info"
|
|
130
|
-
URL="https://github.com/farminf"
|
|
131
|
-
/>
|
|
214
|
+
{cameras.map((cam) => (
|
|
215
|
+
<Pannellum.Hotspot
|
|
216
|
+
key={cam.id}
|
|
217
|
+
type="custom"
|
|
218
|
+
pitch={cam.pitch}
|
|
219
|
+
yaw={cam.yaw}
|
|
220
|
+
cssClass="custom-hotspot"
|
|
221
|
+
handleClick={handleHotspotClick}
|
|
222
|
+
handleClickArg={{ id: cam.id, name: cam.name }}
|
|
223
|
+
tooltip={buildTooltip(cam.name)}
|
|
224
|
+
tooltipArg={{}}
|
|
225
|
+
/>
|
|
226
|
+
))}
|
|
132
227
|
|
|
133
228
|
</Pannellum>
|
|
134
229
|
</>
|