@opendfieldmap/sdk 0.1.0-alpha.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/LICENSE +661 -0
- package/README.md +30 -0
- package/dist/assets/map-pattern.svg +19 -0
- package/dist/assets/marker-hover.webp +0 -0
- package/dist/assets/marker-select.webp +0 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/scale.d.ts +5 -0
- package/dist/components/switches.d.ts +9 -0
- package/dist/components/types.d.ts +29 -0
- package/dist/fonts.d.ts +2 -0
- package/dist/icons.d.ts +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +846 -0
- package/dist/index.js.map +7 -0
- package/dist/state.d.ts +7 -0
- package/dist/style.css +1842 -0
- package/dist/types.d.ts +94 -0
- package/dist/widget.d.ts +3 -0
- package/package.json +29 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
// packages/sdk/src/widget.ts
|
|
2
|
+
import {
|
|
3
|
+
defaultOEMResources,
|
|
4
|
+
getOEMRegion as getOEMRegion3,
|
|
5
|
+
loadOEMManifest,
|
|
6
|
+
normalizeOEMLocale,
|
|
7
|
+
validateOEMManifest
|
|
8
|
+
} from "@opendfieldmap/core";
|
|
9
|
+
import { createOEM } from "@opendfieldmap/map";
|
|
10
|
+
|
|
11
|
+
// packages/sdk/src/components/scale.ts
|
|
12
|
+
import { getOEMRegion } from "@opendfieldmap/core";
|
|
13
|
+
|
|
14
|
+
// packages/sdk/src/components/types.ts
|
|
15
|
+
var createButton = (className) => {
|
|
16
|
+
const element = document.createElement("button");
|
|
17
|
+
element.type = "button";
|
|
18
|
+
element.className = className;
|
|
19
|
+
return element;
|
|
20
|
+
};
|
|
21
|
+
var setLabel = (element, label) => {
|
|
22
|
+
element.title = label;
|
|
23
|
+
element.setAttribute("aria-label", label);
|
|
24
|
+
};
|
|
25
|
+
var getMessages = (manifest, locale) => {
|
|
26
|
+
const messages = manifest.controls[locale] ?? manifest.controls[manifest.fallbackLocale];
|
|
27
|
+
if (!messages) throw new Error("Missing OEM control messages");
|
|
28
|
+
return messages;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// packages/sdk/src/components/scale.ts
|
|
32
|
+
var ZOOM_STEP = 0.5;
|
|
33
|
+
var clamp = (value, minimum, maximum) => Math.max(minimum, Math.min(maximum, value));
|
|
34
|
+
var createScaleControl = (context) => {
|
|
35
|
+
const { manifest, zoomLocked } = context;
|
|
36
|
+
const element = document.createElement("div");
|
|
37
|
+
element.className = "scaleControl";
|
|
38
|
+
element.classList.toggle("locked", zoomLocked);
|
|
39
|
+
const decorationTop = document.createElement("div");
|
|
40
|
+
decorationTop.className = "scaleDecoration scaleDecorationTop";
|
|
41
|
+
const decorationBottom = document.createElement("div");
|
|
42
|
+
decorationBottom.className = "scaleDecoration scaleDecorationBottom";
|
|
43
|
+
const zoomInFrame = document.createElement("div");
|
|
44
|
+
zoomInFrame.className = "scaleFrame zoomInFrame";
|
|
45
|
+
const zoomIn = createButton("zoomButton zoomIn");
|
|
46
|
+
zoomIn.textContent = "+";
|
|
47
|
+
zoomInFrame.append(zoomIn);
|
|
48
|
+
const track = document.createElement("div");
|
|
49
|
+
track.className = "scaleTrack";
|
|
50
|
+
const fill = document.createElement("div");
|
|
51
|
+
fill.className = "scaleFill";
|
|
52
|
+
track.append(fill);
|
|
53
|
+
const zoomOutFrame = document.createElement("div");
|
|
54
|
+
zoomOutFrame.className = "scaleFrame zoomOutFrame";
|
|
55
|
+
const zoomOut = createButton("zoomButton zoomOut");
|
|
56
|
+
zoomOut.textContent = "-";
|
|
57
|
+
zoomOutFrame.append(zoomOut);
|
|
58
|
+
element.append(decorationTop, zoomInFrame, track, zoomOutFrame, decorationBottom);
|
|
59
|
+
let currentState;
|
|
60
|
+
let targetZoom = null;
|
|
61
|
+
let scaleFrame = null;
|
|
62
|
+
let zoomFrame = null;
|
|
63
|
+
let dragPointerId = null;
|
|
64
|
+
let dragStartY = null;
|
|
65
|
+
let dragStartZoom = null;
|
|
66
|
+
let currentLocale = "";
|
|
67
|
+
const isCompact = () => (element.closest(".oemWidget")?.clientWidth ?? Number.POSITIVE_INFINITY) < 480;
|
|
68
|
+
const scaleFor = (zoom, minimum, maximum) => maximum === minimum ? 0 : clamp((zoom - minimum) / (maximum - minimum), 0, 1);
|
|
69
|
+
const updateScale = (zoom, minimum, maximum) => {
|
|
70
|
+
if (scaleFrame !== null) cancelAnimationFrame(scaleFrame);
|
|
71
|
+
scaleFrame = requestAnimationFrame(() => {
|
|
72
|
+
track.style.setProperty("--scale", String(scaleFor(zoom, minimum, maximum)));
|
|
73
|
+
scaleFrame = null;
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const applyZoom = (zoom, animate) => {
|
|
77
|
+
const region = getOEMRegion(manifest, currentState.regionId);
|
|
78
|
+
const nextZoom = clamp(zoom, region.minZoom, region.maxZoom);
|
|
79
|
+
if (targetZoom === nextZoom) return;
|
|
80
|
+
targetZoom = nextZoom;
|
|
81
|
+
updateScale(nextZoom, region.minZoom, region.maxZoom);
|
|
82
|
+
context.zoomTo(nextZoom, { animate });
|
|
83
|
+
};
|
|
84
|
+
const scheduleDragZoom = (zoom) => {
|
|
85
|
+
targetZoom = zoom;
|
|
86
|
+
if (zoomFrame !== null) return;
|
|
87
|
+
zoomFrame = requestAnimationFrame(() => {
|
|
88
|
+
zoomFrame = null;
|
|
89
|
+
if (targetZoom !== null) context.zoomTo(targetZoom, { animate: false });
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
zoomIn.addEventListener("click", () => {
|
|
93
|
+
if (!zoomLocked) applyZoom((targetZoom ?? currentState.zoom) + ZOOM_STEP, true);
|
|
94
|
+
});
|
|
95
|
+
zoomOut.addEventListener("click", () => {
|
|
96
|
+
if (!zoomLocked) applyZoom((targetZoom ?? currentState.zoom) - ZOOM_STEP, true);
|
|
97
|
+
});
|
|
98
|
+
track.addEventListener("click", (event) => {
|
|
99
|
+
if (zoomLocked || isCompact()) return;
|
|
100
|
+
const region = getOEMRegion(manifest, currentState.regionId);
|
|
101
|
+
const bounds = track.getBoundingClientRect();
|
|
102
|
+
const ratio = 1 - (event.clientY - bounds.top) / bounds.height;
|
|
103
|
+
applyZoom(region.minZoom + clamp(ratio, 0, 1) * (region.maxZoom - region.minZoom), true);
|
|
104
|
+
});
|
|
105
|
+
track.addEventListener("pointerdown", (event) => {
|
|
106
|
+
if (zoomLocked || !isCompact()) return;
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
event.stopPropagation();
|
|
109
|
+
track.setPointerCapture(event.pointerId);
|
|
110
|
+
dragPointerId = event.pointerId;
|
|
111
|
+
dragStartY = event.clientY;
|
|
112
|
+
dragStartZoom = targetZoom ?? currentState.zoom;
|
|
113
|
+
element.classList.add("dragging");
|
|
114
|
+
});
|
|
115
|
+
track.addEventListener("pointermove", (event) => {
|
|
116
|
+
if (dragPointerId !== event.pointerId || dragStartY === null || dragStartZoom === null) return;
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
const region = getOEMRegion(manifest, currentState.regionId);
|
|
119
|
+
const delta = (dragStartY - event.clientY) / Math.max(1, track.getBoundingClientRect().height) * (region.maxZoom - region.minZoom);
|
|
120
|
+
const nextZoom = clamp(dragStartZoom + delta, region.minZoom, region.maxZoom);
|
|
121
|
+
updateScale(nextZoom, region.minZoom, region.maxZoom);
|
|
122
|
+
scheduleDragZoom(nextZoom);
|
|
123
|
+
});
|
|
124
|
+
const finishDrag = (event) => {
|
|
125
|
+
if (dragPointerId !== event.pointerId) return;
|
|
126
|
+
if (track.hasPointerCapture(event.pointerId)) track.releasePointerCapture(event.pointerId);
|
|
127
|
+
dragPointerId = null;
|
|
128
|
+
dragStartY = null;
|
|
129
|
+
dragStartZoom = null;
|
|
130
|
+
element.classList.remove("dragging");
|
|
131
|
+
};
|
|
132
|
+
track.addEventListener("pointerup", finishDrag);
|
|
133
|
+
track.addEventListener("pointercancel", finishDrag);
|
|
134
|
+
track.addEventListener("lostpointercapture", finishDrag);
|
|
135
|
+
return {
|
|
136
|
+
element,
|
|
137
|
+
sync(state) {
|
|
138
|
+
currentState = state;
|
|
139
|
+
targetZoom = state.zoom;
|
|
140
|
+
const region = getOEMRegion(manifest, state.regionId);
|
|
141
|
+
updateScale(state.zoom, region.minZoom, region.maxZoom);
|
|
142
|
+
if (currentLocale !== state.locale) {
|
|
143
|
+
currentLocale = state.locale;
|
|
144
|
+
const messages = getMessages(manifest, state.locale);
|
|
145
|
+
setLabel(zoomIn, messages.zoomIn);
|
|
146
|
+
setLabel(zoomOut, messages.zoomOut);
|
|
147
|
+
}
|
|
148
|
+
zoomIn.disabled = zoomLocked || state.zoom >= region.maxZoom;
|
|
149
|
+
zoomOut.disabled = zoomLocked || state.zoom <= region.minZoom;
|
|
150
|
+
},
|
|
151
|
+
destroy() {
|
|
152
|
+
if (scaleFrame !== null) cancelAnimationFrame(scaleFrame);
|
|
153
|
+
if (zoomFrame !== null) cancelAnimationFrame(zoomFrame);
|
|
154
|
+
element.classList.remove("dragging");
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// packages/sdk/src/components/switches.ts
|
|
160
|
+
import { getOEMRegion as getOEMRegion2 } from "@opendfieldmap/core";
|
|
161
|
+
|
|
162
|
+
// packages/sdk/src/assets/Valley_4.svg
|
|
163
|
+
var Valley_4_default = '<?xml version="1.0" encoding="UTF-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 425.67 170.68">\n <g>\n <path class="cls-1" d="M15.21,170.68h410.46s-43.67-106.46-205.23-106.45c-160.59,0-205.23,106.45-205.23,106.45Z"/>\n <path class="cls-1" d="M0,63.31h244.09S218.12,0,122.04,0C26.54,0,0,63.31,0,63.31Z"/>\n </g>\n</svg>';
|
|
164
|
+
|
|
165
|
+
// packages/sdk/src/assets/Wuling.svg
|
|
166
|
+
var Wuling_default = '<?xml version="1.0" encoding="UTF-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 374.53 309.73">\n <g>\n <rect x="80.63" y="20.44" width="58.29" height="117.86" rx="1" ry="1"/>\n <path d="M237.6,223.35c-.53-.03-.94-.47-.94-.99v-89.26c0-.55-.45-1-1-1h-56.3c-.55,0-1,.45-1,1v87.86c0,.55-.44.99-.99.99-12.89.14-25.44.56-37.51,1.23-.53.03-.94.47-.94,1v45.23c0,.55-.45,1-1,1h-56.3c-.55,0-1-.45-1-1v-39.11c0-.61-.55-1.08-1.16-.99C31.4,236.85,0,249.69,0,265.81c0,27.25,83.84,43.92,187.26,43.92s187.26-16.67,187.26-43.92c0-21.91-57.95-37.74-136.93-42.46Z"/>\n <path d="M81.77,228.97c17.21-2.61,36.48-4.55,57.16-5.73v-70.69h-58.29v75.43c0,.61.53,1.07,1.14.98Z"/>\n <rect x="178.37" width="58.29" height="117.86" rx="1" ry="1"/>\n <path d="M249.61,156.62l57.93-49.76c.22-.19.35-.47.35-.76v-51.73c0-.85-1-1.31-1.64-.76l-57.93,49.76c-.22.19-.35.47-.35.76v51.73c0,.85,1,1.31,1.64.76Z"/>\n </g>\n</svg>';
|
|
167
|
+
|
|
168
|
+
// packages/sdk/src/assets/Dijiang.svg
|
|
169
|
+
var Dijiang_default = '<?xml version="1.0" encoding="UTF-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320.87 332.32">\n <path d="M306.45,266.64L241.82,0h-32.6l39.82,266.64h-26.81l-6.22-26.53h8.95v-47.7h-20.14L159.7,0l-45.13,192.41h-20.14v47.7h8.95l-6.22,26.53h-26.34L110.64,0h-32.6L13.4,266.64H0v65.68h320.87v-65.68h-14.42ZM70.81,312.22v-30.76h22.87l-7.21,30.76h-15.65ZM248.58,312.22h-15.65l-7.21-30.76h22.87v30.76Z"/>\n</svg>';
|
|
170
|
+
|
|
171
|
+
// packages/sdk/src/assets/Weekraid_1.svg
|
|
172
|
+
var Weekraid_1_default = '<?xml version="1.0" encoding="UTF-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 148.547 130.941">\n <polygon points="28.102 0 6.153 0 19.116 21.175 41.065 21.175 28.102 0"/>\n <polygon points="19.116 21.175 0 21.175 26.446 65.471 0 109.766 19.116 109.766 45.562 65.471 19.116 21.175"/>\n <polygon points="142.394 0 120.444 0 107.481 21.175 129.431 21.175 142.394 0"/>\n <polygon points="6.153 130.941 28.102 130.941 41.065 109.766 19.116 109.766 6.153 130.941"/>\n <path d="M109.749,94.194l-16.583-28.723,16.583-28.723h-11.259l8.991-15.573H41.065l8.991,15.573h-11.259l16.583,28.723-16.583,28.723h11.259l-8.991,15.573h66.416l-8.991-15.573h11.259ZM87.106,47.921l-10.132,17.549,10.132,17.549h-7.029l7.029,12.174h-25.665l7.029-12.174h-7.029l10.132-17.549-10.132-17.549h7.029l-7.029-12.174h25.665l-7.029,12.174h7.029Z"/>\n <polygon points="120.444 130.941 142.394 130.941 129.431 109.766 107.481 109.766 120.444 130.941"/>\n <polygon points="122.1 65.471 148.547 21.175 129.431 21.175 102.985 65.471 129.431 109.766 148.547 109.766 122.1 65.471"/>\n</svg>';
|
|
173
|
+
|
|
174
|
+
// packages/sdk/src/assets/layer.svg
|
|
175
|
+
var layer_default = '<svg width="40" height="37" viewBox="0 0 40 37" xmlns="http://www.w3.org/2000/svg">\n<path d="M20 0L39.2453 10.477L20 20.954L0.7547 10.477L20 0Z"/>\n<path d="M20.9333 24.9496L20 25.4576L19.0667 24.9496L10.6011 20.3409L0 26.1121L20 37L40 26.1121L29.3989 20.3409L20.9333 24.9496Z"/>\n</svg>\n';
|
|
176
|
+
|
|
177
|
+
// packages/sdk/src/icons.ts
|
|
178
|
+
var clean = (svg) => svg.replace(/<\?xml[^>]*>\s*/, "");
|
|
179
|
+
var REGION_ICONS = Object.freeze({
|
|
180
|
+
Valley_4: clean(Valley_4_default),
|
|
181
|
+
Wuling: clean(Wuling_default),
|
|
182
|
+
Dijiang: clean(Dijiang_default),
|
|
183
|
+
Weekraid_1: clean(Weekraid_1_default)
|
|
184
|
+
});
|
|
185
|
+
var LAYER_ICON = clean(layer_default);
|
|
186
|
+
|
|
187
|
+
// packages/sdk/src/components/switches.ts
|
|
188
|
+
var FLOOR_ORDER = ["L4", "L3", "L2", "L1", "M", "B1", "B2", "B3", "B4"];
|
|
189
|
+
var createSwitchAction = (className) => {
|
|
190
|
+
const element = document.createElement("div");
|
|
191
|
+
element.className = className;
|
|
192
|
+
element.tabIndex = 0;
|
|
193
|
+
element.setAttribute("role", "button");
|
|
194
|
+
return element;
|
|
195
|
+
};
|
|
196
|
+
var bindPersistentPanel = (owner, context) => {
|
|
197
|
+
owner.addEventListener("pointerenter", (event) => {
|
|
198
|
+
if (event.pointerType === "mouse") context.panels.collapseAll();
|
|
199
|
+
});
|
|
200
|
+
owner.addEventListener("pointerdown", (event) => {
|
|
201
|
+
if (event.pointerType === "touch" || event.pointerType === "pen") context.panels.expand(owner);
|
|
202
|
+
});
|
|
203
|
+
owner.addEventListener("focusin", () => {
|
|
204
|
+
if (owner.matches(":focus-visible") || owner.querySelector(":focus-visible")) context.panels.expand(owner);
|
|
205
|
+
});
|
|
206
|
+
owner.addEventListener("focusout", (event) => {
|
|
207
|
+
if (!(event.relatedTarget instanceof Node) || !owner.contains(event.relatedTarget)) context.panels.collapse(owner);
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
var positionIndicator = (indicator, target) => {
|
|
211
|
+
indicator.style.transform = `translateY(calc(${target.offsetTop + target.offsetHeight / 2}px - 50%))`;
|
|
212
|
+
};
|
|
213
|
+
var createRegionControl = (context) => {
|
|
214
|
+
const { manifest, apply } = context;
|
|
215
|
+
const element = document.createElement("div");
|
|
216
|
+
element.className = "switch regionSwitch";
|
|
217
|
+
const label = document.createElement("div");
|
|
218
|
+
label.className = "switchLabel regionLabel";
|
|
219
|
+
label.setAttribute("aria-hidden", "true");
|
|
220
|
+
const indicator = document.createElement("div");
|
|
221
|
+
indicator.className = "switchIndicator";
|
|
222
|
+
element.append(label, indicator);
|
|
223
|
+
const controls = /* @__PURE__ */ new Map();
|
|
224
|
+
for (const region of manifest.regions) {
|
|
225
|
+
const regionButton = createSwitchAction("switchItem regionEntry");
|
|
226
|
+
regionButton.dataset.region = region.id;
|
|
227
|
+
if (region.subregions.length > 1) bindPersistentPanel(regionButton, context);
|
|
228
|
+
const icon = document.createElement("span");
|
|
229
|
+
icon.className = "switchIcon";
|
|
230
|
+
const markup = REGION_ICONS[region.id];
|
|
231
|
+
if (!markup) throw new Error(`Missing Atlos region icon: ${region.id}`);
|
|
232
|
+
icon.innerHTML = markup;
|
|
233
|
+
regionButton.append(icon);
|
|
234
|
+
const selectRegion = () => {
|
|
235
|
+
const target = region.initialView;
|
|
236
|
+
apply({ region: region.id, subregion: null, floor: "M", center: { x: target.x, y: target.y }, zoom: target.zoom });
|
|
237
|
+
};
|
|
238
|
+
regionButton.addEventListener("click", selectRegion);
|
|
239
|
+
regionButton.addEventListener("keydown", (event) => {
|
|
240
|
+
if (event.target !== regionButton) return;
|
|
241
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
242
|
+
event.preventDefault();
|
|
243
|
+
selectRegion();
|
|
244
|
+
});
|
|
245
|
+
const subregionControls = /* @__PURE__ */ new Map();
|
|
246
|
+
let subregionIndicator;
|
|
247
|
+
if (region.subregions.length > 1) {
|
|
248
|
+
const panel = document.createElement("div");
|
|
249
|
+
panel.className = "subregionPanel";
|
|
250
|
+
panel.addEventListener("click", (event) => event.stopPropagation());
|
|
251
|
+
const list = document.createElement("div");
|
|
252
|
+
list.className = "subregionList";
|
|
253
|
+
subregionIndicator = document.createElement("div");
|
|
254
|
+
subregionIndicator.className = "switchIndicator";
|
|
255
|
+
list.append(subregionIndicator);
|
|
256
|
+
for (const subregion of region.subregions) {
|
|
257
|
+
const subregionButton = createButton("selectionItem subregionItem");
|
|
258
|
+
subregionButton.dataset.subregion = subregion.id;
|
|
259
|
+
subregionButton.addEventListener("click", () => {
|
|
260
|
+
context.panels.collapse(regionButton);
|
|
261
|
+
const [[x1, y1], [x2, y2]] = subregion.bounds ?? [
|
|
262
|
+
[region.initialView.x, region.initialView.y],
|
|
263
|
+
[region.initialView.x, region.initialView.y]
|
|
264
|
+
];
|
|
265
|
+
apply({
|
|
266
|
+
region: region.id,
|
|
267
|
+
subregion: subregion.id,
|
|
268
|
+
floor: "M",
|
|
269
|
+
center: { x: (x1 + x2) / 2, y: (y1 + y2) / 2 },
|
|
270
|
+
zoom: Math.max(region.minZoom, Math.min(region.maxZoom, 1))
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
subregionControls.set(subregion.id, { data: subregion, button: subregionButton });
|
|
274
|
+
list.append(subregionButton);
|
|
275
|
+
}
|
|
276
|
+
panel.append(list);
|
|
277
|
+
regionButton.append(panel);
|
|
278
|
+
}
|
|
279
|
+
controls.set(region.id, { region, button: regionButton, subregions: subregionControls, subregionIndicator });
|
|
280
|
+
element.append(regionButton);
|
|
281
|
+
}
|
|
282
|
+
let stateKey = "";
|
|
283
|
+
return {
|
|
284
|
+
element,
|
|
285
|
+
sync(state) {
|
|
286
|
+
const nextKey = `${state.locale}\0${state.regionId}\0${state.subregionId ?? ""}`;
|
|
287
|
+
if (nextKey !== stateKey) {
|
|
288
|
+
stateKey = nextKey;
|
|
289
|
+
for (const [regionId, control] of controls) {
|
|
290
|
+
const regionName = control.region.locales[state.locale] ?? control.region.locales[manifest.fallbackLocale] ?? control.region.name;
|
|
291
|
+
control.button.classList.toggle("selected", regionId === state.regionId);
|
|
292
|
+
setLabel(control.button, regionName);
|
|
293
|
+
for (const [subregionId, subregion] of control.subregions) {
|
|
294
|
+
const localized = subregion.data.locales?.[state.locale] ?? subregion.data.locales?.[manifest.fallbackLocale];
|
|
295
|
+
subregion.button.textContent = localized?.short ?? subregion.data.key;
|
|
296
|
+
setLabel(subregion.button, localized?.name ?? subregion.data.key);
|
|
297
|
+
subregion.button.classList.toggle("selected", subregionId === state.subregionId);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
for (const control of controls.values()) {
|
|
302
|
+
const selectedSubregion = state.subregionId ? control.subregions.get(state.subregionId) : void 0;
|
|
303
|
+
if (control.subregionIndicator) {
|
|
304
|
+
control.subregionIndicator.classList.toggle("hidden", !selectedSubregion);
|
|
305
|
+
if (selectedSubregion) positionIndicator(control.subregionIndicator, selectedSubregion.button);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const selectedControl = controls.get(state.regionId);
|
|
309
|
+
if (selectedControl) positionIndicator(indicator, selectedControl.button);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
var createLayerControl = (context) => {
|
|
314
|
+
const { manifest, apply } = context;
|
|
315
|
+
const element = document.createElement("div");
|
|
316
|
+
element.className = "switch layerSwitch";
|
|
317
|
+
const label = document.createElement("div");
|
|
318
|
+
label.className = "switchLabel layerLabel";
|
|
319
|
+
label.setAttribute("aria-hidden", "true");
|
|
320
|
+
const mainButton = createSwitchAction("switchItem");
|
|
321
|
+
bindPersistentPanel(mainButton, context);
|
|
322
|
+
const icon = document.createElement("span");
|
|
323
|
+
icon.className = "switchIcon";
|
|
324
|
+
icon.innerHTML = LAYER_ICON;
|
|
325
|
+
const panel = document.createElement("div");
|
|
326
|
+
panel.className = "floorPanel";
|
|
327
|
+
panel.addEventListener("click", (event) => event.stopPropagation());
|
|
328
|
+
const list = document.createElement("div");
|
|
329
|
+
list.className = "floorList";
|
|
330
|
+
panel.append(list);
|
|
331
|
+
mainButton.append(icon, panel);
|
|
332
|
+
element.append(label, mainButton);
|
|
333
|
+
mainButton.addEventListener("keydown", (event) => {
|
|
334
|
+
if (event.target !== mainButton || event.key !== "Enter" && event.key !== " ") return;
|
|
335
|
+
event.preventDefault();
|
|
336
|
+
mainButton.click();
|
|
337
|
+
});
|
|
338
|
+
const indicator = document.createElement("div");
|
|
339
|
+
indicator.className = "switchIndicator";
|
|
340
|
+
const floorButtons = /* @__PURE__ */ new Map();
|
|
341
|
+
let currentRegion = "";
|
|
342
|
+
let currentLocale = "";
|
|
343
|
+
let currentFloor = "M";
|
|
344
|
+
mainButton.addEventListener("click", () => {
|
|
345
|
+
if (currentFloor !== "M") apply({ floor: "M" });
|
|
346
|
+
});
|
|
347
|
+
return {
|
|
348
|
+
element,
|
|
349
|
+
sync(state) {
|
|
350
|
+
const region = getOEMRegion2(manifest, state.regionId);
|
|
351
|
+
if (currentRegion !== region.id) {
|
|
352
|
+
currentRegion = region.id;
|
|
353
|
+
floorButtons.clear();
|
|
354
|
+
list.replaceChildren(indicator);
|
|
355
|
+
const floors = [...region.floors].sort((left, right) => FLOOR_ORDER.indexOf(left.id) - FLOOR_ORDER.indexOf(right.id));
|
|
356
|
+
element.hidden = floors.length <= 1;
|
|
357
|
+
for (const floor of floors) {
|
|
358
|
+
const button = createButton("selectionItem floorItem");
|
|
359
|
+
button.textContent = floor.id;
|
|
360
|
+
button.addEventListener("click", () => {
|
|
361
|
+
context.panels.collapse(mainButton);
|
|
362
|
+
apply({ floor: floor.id });
|
|
363
|
+
});
|
|
364
|
+
floorButtons.set(floor.id, button);
|
|
365
|
+
list.append(button);
|
|
366
|
+
}
|
|
367
|
+
currentLocale = "";
|
|
368
|
+
}
|
|
369
|
+
if (currentLocale !== state.locale) {
|
|
370
|
+
currentLocale = state.locale;
|
|
371
|
+
const messages = getMessages(manifest, state.locale);
|
|
372
|
+
setLabel(mainButton, messages.layerSelect);
|
|
373
|
+
for (const [floorId, button] of floorButtons) setLabel(button, `${messages.layerSelect}: ${floorId}`);
|
|
374
|
+
}
|
|
375
|
+
if (currentFloor !== state.floorId) floorButtons.get(currentFloor)?.classList.remove("selected");
|
|
376
|
+
currentFloor = state.floorId;
|
|
377
|
+
mainButton.classList.toggle("selected", currentFloor !== "M");
|
|
378
|
+
const selectedButton = floorButtons.get(currentFloor);
|
|
379
|
+
selectedButton?.classList.add("selected");
|
|
380
|
+
if (selectedButton) positionIndicator(indicator, selectedButton);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// packages/sdk/src/components/index.ts
|
|
386
|
+
var mountControls = (root, visibility, context) => {
|
|
387
|
+
const hasSwitches = visibility.regionSelector || visibility.floorSelector;
|
|
388
|
+
if (!hasSwitches && !visibility.scaleBar) return { sync: () => {
|
|
389
|
+
} };
|
|
390
|
+
const overlay = document.createElement("div");
|
|
391
|
+
overlay.className = "controlOverlay";
|
|
392
|
+
root.append(overlay);
|
|
393
|
+
const components = [];
|
|
394
|
+
let expandedPanel = null;
|
|
395
|
+
const panels = {
|
|
396
|
+
expand(owner) {
|
|
397
|
+
if (expandedPanel === owner) return;
|
|
398
|
+
expandedPanel?.classList.remove("expanded");
|
|
399
|
+
expandedPanel = owner;
|
|
400
|
+
expandedPanel.classList.add("expanded");
|
|
401
|
+
},
|
|
402
|
+
collapse(owner) {
|
|
403
|
+
owner.classList.remove("expanded");
|
|
404
|
+
if (expandedPanel === owner) expandedPanel = null;
|
|
405
|
+
},
|
|
406
|
+
collapseAll() {
|
|
407
|
+
expandedPanel?.classList.remove("expanded");
|
|
408
|
+
expandedPanel = null;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
const controlContext = { ...context, panels };
|
|
412
|
+
let currentState = null;
|
|
413
|
+
const handleOutsidePointer = (event) => {
|
|
414
|
+
if (expandedPanel && event.target instanceof Node && !expandedPanel.contains(event.target)) panels.collapseAll();
|
|
415
|
+
};
|
|
416
|
+
if (hasSwitches) document.addEventListener("pointerdown", handleOutsidePointer, true);
|
|
417
|
+
const observer = !hasSwitches || typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
|
|
418
|
+
if (currentState) for (const component of components) component.sync(currentState);
|
|
419
|
+
});
|
|
420
|
+
observer?.observe(root);
|
|
421
|
+
if (hasSwitches) {
|
|
422
|
+
const switchArea = document.createElement("div");
|
|
423
|
+
switchArea.className = "switchArea";
|
|
424
|
+
if (visibility.regionSelector) {
|
|
425
|
+
const region = createRegionControl(controlContext);
|
|
426
|
+
components.push(region);
|
|
427
|
+
switchArea.append(region.element);
|
|
428
|
+
}
|
|
429
|
+
if (visibility.floorSelector) {
|
|
430
|
+
const layer = createLayerControl(controlContext);
|
|
431
|
+
components.push(layer);
|
|
432
|
+
switchArea.append(layer.element);
|
|
433
|
+
}
|
|
434
|
+
overlay.append(switchArea);
|
|
435
|
+
}
|
|
436
|
+
if (visibility.scaleBar) {
|
|
437
|
+
const scale = createScaleControl(controlContext);
|
|
438
|
+
components.push(scale);
|
|
439
|
+
overlay.append(scale.element);
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
sync(state) {
|
|
443
|
+
currentState = state;
|
|
444
|
+
for (const component of components) component.sync(state);
|
|
445
|
+
},
|
|
446
|
+
destroy() {
|
|
447
|
+
if (hasSwitches) document.removeEventListener("pointerdown", handleOutsidePointer, true);
|
|
448
|
+
observer?.disconnect();
|
|
449
|
+
panels.collapseAll();
|
|
450
|
+
for (const component of components) component.destroy?.();
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// packages/sdk/src/fonts.ts
|
|
456
|
+
import { resolveOEMAsset } from "@opendfieldmap/core";
|
|
457
|
+
var FONT_FAMILIES = /* @__PURE__ */ new Set([
|
|
458
|
+
"HMSans_EN",
|
|
459
|
+
"Novecento Bold",
|
|
460
|
+
"Novecento DemiBold",
|
|
461
|
+
"Novecento Medium",
|
|
462
|
+
"Novecento Cyrillic DemiBold",
|
|
463
|
+
"Novecento Cyrillic Medium",
|
|
464
|
+
"Novecento Vietnamese DemiBold",
|
|
465
|
+
"Novecento Vietnamese Medium"
|
|
466
|
+
]);
|
|
467
|
+
var installFonts = (root, manifest, resources) => {
|
|
468
|
+
if (!manifest.fonts?.length) return;
|
|
469
|
+
const rules = manifest.fonts.map((font) => {
|
|
470
|
+
if (!FONT_FAMILIES.has(font.family) || !Number.isFinite(font.weight) || font.weightRange && (!Number.isFinite(font.weightRange[0]) || !Number.isFinite(font.weightRange[1]) || font.weightRange[0] > font.weightRange[1])) {
|
|
471
|
+
throw new Error(`Unsupported OEM font: ${font.family}`);
|
|
472
|
+
}
|
|
473
|
+
const url = resolveOEMAsset(resources.baseUrl, font.path);
|
|
474
|
+
const weight = font.weightRange ? `${font.weightRange[0]} ${font.weightRange[1]}` : String(font.weight);
|
|
475
|
+
return `@font-face{font-family:${JSON.stringify(font.family)};src:url(${JSON.stringify(url)}) format("woff2");font-style:${font.style};font-weight:${weight};font-display:swap;}`;
|
|
476
|
+
});
|
|
477
|
+
const style = document.createElement("style");
|
|
478
|
+
style.dataset.oemFonts = manifest.releaseId;
|
|
479
|
+
style.textContent = rules.join("");
|
|
480
|
+
root.append(style);
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// packages/sdk/src/widget.ts
|
|
484
|
+
var REGION_ALIASES = Object.freeze({
|
|
485
|
+
VL: "Valley_4",
|
|
486
|
+
WL: "Wuling",
|
|
487
|
+
DJ: "Dijiang",
|
|
488
|
+
ES: "Weekraid_1"
|
|
489
|
+
});
|
|
490
|
+
var mounted = /* @__PURE__ */ new WeakSet();
|
|
491
|
+
var cloneState = (state) => ({
|
|
492
|
+
...state,
|
|
493
|
+
markerTypes: state.markerTypes === "*" ? "*" : [...state.markerTypes],
|
|
494
|
+
center: { ...state.center }
|
|
495
|
+
});
|
|
496
|
+
var cloneConfig = (config) => ({
|
|
497
|
+
...config,
|
|
498
|
+
markerTypes: Array.isArray(config.markerTypes) ? [...config.markerTypes] : config.markerTypes,
|
|
499
|
+
center: config.center ? { ...config.center } : config.center
|
|
500
|
+
});
|
|
501
|
+
var resolveContainer = (container) => {
|
|
502
|
+
const element = typeof container === "string" ? document.querySelector(container) : container;
|
|
503
|
+
if (!element) throw new Error(`OEM Widget container not found: ${container}`);
|
|
504
|
+
return element;
|
|
505
|
+
};
|
|
506
|
+
var resolveRegionId = (manifest, value) => {
|
|
507
|
+
const requested = value ? REGION_ALIASES[value.toUpperCase()] ?? value : manifest.defaultRegionId;
|
|
508
|
+
return getOEMRegion3(manifest, requested).id;
|
|
509
|
+
};
|
|
510
|
+
var normalizeMarkerTypes = (value) => {
|
|
511
|
+
if (value === "*") return "*";
|
|
512
|
+
if (!value || !Array.isArray(value)) return [];
|
|
513
|
+
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
|
|
514
|
+
};
|
|
515
|
+
var getPreset = (region, subregion) => {
|
|
516
|
+
if (!subregion?.bounds) {
|
|
517
|
+
return {
|
|
518
|
+
center: { x: region.initialView.x, y: region.initialView.y },
|
|
519
|
+
zoom: region.initialView.zoom
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
center: {
|
|
524
|
+
x: (subregion.bounds[0][0] + subregion.bounds[1][0]) / 2,
|
|
525
|
+
y: (subregion.bounds[0][1] + subregion.bounds[1][1]) / 2
|
|
526
|
+
},
|
|
527
|
+
zoom: Math.max(region.minZoom, Math.min(region.maxZoom, 1))
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
var normalizeState = (input, manifest) => {
|
|
531
|
+
let regionId = resolveRegionId(manifest, input.region);
|
|
532
|
+
const subregionId = input.subregion?.trim() || null;
|
|
533
|
+
if (subregionId) {
|
|
534
|
+
const owner = manifest.regions.find((region2) => region2.subregions.some((subregion2) => subregion2.id === subregionId));
|
|
535
|
+
if (!owner) throw new Error(`Unknown OEM subregion: ${subregionId}`);
|
|
536
|
+
if (input.region && owner.id !== regionId) {
|
|
537
|
+
throw new Error(`OEM subregion ${subregionId} does not belong to ${regionId}`);
|
|
538
|
+
}
|
|
539
|
+
regionId = owner.id;
|
|
540
|
+
}
|
|
541
|
+
const region = getOEMRegion3(manifest, regionId);
|
|
542
|
+
const subregion = subregionId ? region.subregions.find((entry) => entry.id === subregionId) : void 0;
|
|
543
|
+
const floorId = input.floor ?? "M";
|
|
544
|
+
if (!region.floors.some((floor) => floor.id === floorId)) {
|
|
545
|
+
throw new Error(`Unknown OEM floor ${floorId} in ${regionId}`);
|
|
546
|
+
}
|
|
547
|
+
const requestedCenter = input.center;
|
|
548
|
+
if (requestedCenter && (!Number.isFinite(requestedCenter.x) || !Number.isFinite(requestedCenter.y))) {
|
|
549
|
+
throw new Error("OEM Widget center coordinates must be finite");
|
|
550
|
+
}
|
|
551
|
+
const preset = getPreset(region, subregion);
|
|
552
|
+
const requestedLocale = input.locale ?? (typeof navigator === "undefined" ? manifest.fallbackLocale : navigator.language);
|
|
553
|
+
return {
|
|
554
|
+
regionId,
|
|
555
|
+
subregionId,
|
|
556
|
+
floorId,
|
|
557
|
+
locale: normalizeOEMLocale(requestedLocale, Object.keys(manifest.locales), manifest.fallbackLocale),
|
|
558
|
+
markerTypes: normalizeMarkerTypes(input.markerTypes),
|
|
559
|
+
labels: input.labels ?? true,
|
|
560
|
+
boundaries: input.boundaries ?? false,
|
|
561
|
+
markerClustering: input.markerClustering ?? true,
|
|
562
|
+
zoom: Math.max(region.minZoom, Math.min(region.maxZoom, input.zoom ?? preset.zoom)),
|
|
563
|
+
center: {
|
|
564
|
+
x: requestedCenter?.x ?? preset.center.x,
|
|
565
|
+
y: requestedCenter?.y ?? preset.center.y
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
};
|
|
569
|
+
var toConfig = (state) => ({
|
|
570
|
+
region: state.regionId,
|
|
571
|
+
subregion: state.subregionId,
|
|
572
|
+
floor: state.floorId,
|
|
573
|
+
locale: state.locale,
|
|
574
|
+
markerTypes: state.markerTypes,
|
|
575
|
+
labels: state.labels,
|
|
576
|
+
boundaries: state.boundaries,
|
|
577
|
+
markerClustering: state.markerClustering,
|
|
578
|
+
zoom: state.zoom,
|
|
579
|
+
center: { ...state.center }
|
|
580
|
+
});
|
|
581
|
+
var sameMarkers = (left, right) => left === right || left !== "*" && right !== "*" && left.length === right.length && left.every((marker, index) => marker === right[index]);
|
|
582
|
+
var sameState = (left, right) => left.regionId === right.regionId && left.subregionId === right.subregionId && left.floorId === right.floorId && left.locale === right.locale && sameMarkers(left.markerTypes, right.markerTypes) && left.labels === right.labels && left.boundaries === right.boundaries && left.markerClustering === right.markerClustering && left.zoom === right.zoom && left.center.x === right.center.x && left.center.y === right.center.y;
|
|
583
|
+
var hasMarkers = (state) => state.markerTypes === "*" || state.markerTypes.length > 0;
|
|
584
|
+
var Widget = class {
|
|
585
|
+
constructor(host, root, core, manifest, options, state) {
|
|
586
|
+
this.host = host;
|
|
587
|
+
this.root = root;
|
|
588
|
+
this.core = core;
|
|
589
|
+
this.manifest = manifest;
|
|
590
|
+
this.options = options;
|
|
591
|
+
this.state = state;
|
|
592
|
+
this.controls = mountControls(root, {
|
|
593
|
+
regionSelector: options.showRegionSelector ?? true,
|
|
594
|
+
floorSelector: options.showFloorSelector ?? true,
|
|
595
|
+
scaleBar: options.showScaleBar ?? true
|
|
596
|
+
}, {
|
|
597
|
+
manifest,
|
|
598
|
+
zoomLocked: options.lockZoom ?? false,
|
|
599
|
+
apply: (update) => {
|
|
600
|
+
void this.setOptions(update).catch((error) => this.report(error));
|
|
601
|
+
},
|
|
602
|
+
zoomTo: (zoom, zoomOptions) => core.setZoom(zoom, zoomOptions)
|
|
603
|
+
});
|
|
604
|
+
this.controls.sync(state);
|
|
605
|
+
this.unsubscribe = core.on("viewchange", this.syncView);
|
|
606
|
+
options.signal?.addEventListener("abort", this.destroy, { once: true });
|
|
607
|
+
}
|
|
608
|
+
destroyed = false;
|
|
609
|
+
applying = false;
|
|
610
|
+
updates = Promise.resolve();
|
|
611
|
+
controls;
|
|
612
|
+
unsubscribe;
|
|
613
|
+
assertAlive() {
|
|
614
|
+
if (this.destroyed) throw new Error("OEM Widget has been destroyed");
|
|
615
|
+
}
|
|
616
|
+
report(error) {
|
|
617
|
+
this.options.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
618
|
+
}
|
|
619
|
+
notify() {
|
|
620
|
+
this.options.onStateChange?.(cloneState(this.state));
|
|
621
|
+
}
|
|
622
|
+
syncView = () => {
|
|
623
|
+
if (this.destroyed || this.applying) return;
|
|
624
|
+
const view = this.core.getView();
|
|
625
|
+
this.state = {
|
|
626
|
+
...this.state,
|
|
627
|
+
regionId: view.regionId,
|
|
628
|
+
floorId: view.floorId ?? "M",
|
|
629
|
+
locale: this.core.getLocale().resolved,
|
|
630
|
+
center: { x: view.x, y: view.y },
|
|
631
|
+
zoom: view.zoom
|
|
632
|
+
};
|
|
633
|
+
this.controls.sync(this.state);
|
|
634
|
+
this.notify();
|
|
635
|
+
};
|
|
636
|
+
getState() {
|
|
637
|
+
this.assertAlive();
|
|
638
|
+
return cloneState(this.state);
|
|
639
|
+
}
|
|
640
|
+
setOptions(update) {
|
|
641
|
+
this.assertAlive();
|
|
642
|
+
const snapshot = cloneConfig(update);
|
|
643
|
+
const operation = this.updates.then(() => this.applyOptions(snapshot));
|
|
644
|
+
this.updates = operation.catch(() => void 0);
|
|
645
|
+
return operation;
|
|
646
|
+
}
|
|
647
|
+
async applyOptions(update) {
|
|
648
|
+
this.assertAlive();
|
|
649
|
+
const changes = Object.fromEntries(
|
|
650
|
+
Object.entries(update).filter(([, value]) => value !== void 0)
|
|
651
|
+
);
|
|
652
|
+
const merged = { ...toConfig(this.state), ...changes };
|
|
653
|
+
const nextRegionId = resolveRegionId(this.manifest, changes.region ?? this.state.regionId);
|
|
654
|
+
if (nextRegionId !== this.state.regionId) {
|
|
655
|
+
const preset = getPreset(getOEMRegion3(this.manifest, nextRegionId));
|
|
656
|
+
if (changes.floor === void 0) merged.floor = "M";
|
|
657
|
+
if (changes.subregion === void 0) merged.subregion = null;
|
|
658
|
+
if (changes.center === void 0) merged.center = preset.center;
|
|
659
|
+
if (changes.zoom === void 0) merged.zoom = preset.zoom;
|
|
660
|
+
}
|
|
661
|
+
if (changes.subregion !== void 0 && changes.subregion !== this.state.subregionId) {
|
|
662
|
+
const region = getOEMRegion3(this.manifest, nextRegionId);
|
|
663
|
+
const subregion = changes.subregion ? region.subregions.find((entry) => entry.id === changes.subregion) : void 0;
|
|
664
|
+
const preset = getPreset(region, subregion);
|
|
665
|
+
if (changes.center === void 0) merged.center = preset.center;
|
|
666
|
+
if (changes.zoom === void 0) merged.zoom = preset.zoom;
|
|
667
|
+
}
|
|
668
|
+
const previous = this.state;
|
|
669
|
+
const next = normalizeState(merged, this.manifest);
|
|
670
|
+
if (sameState(previous, next)) return;
|
|
671
|
+
const regionChanged = next.regionId !== previous.regionId;
|
|
672
|
+
const filterChanged = next.subregionId !== previous.subregionId || !sameMarkers(next.markerTypes, previous.markerTypes);
|
|
673
|
+
const viewChanged = regionChanged || next.floorId !== previous.floorId || next.zoom !== previous.zoom || next.center.x !== previous.center.x || next.center.y !== previous.center.y;
|
|
674
|
+
const previousMarkers = hasMarkers(previous);
|
|
675
|
+
const nextMarkers = hasMarkers(next);
|
|
676
|
+
this.applying = true;
|
|
677
|
+
this.root.classList.add("loading");
|
|
678
|
+
try {
|
|
679
|
+
if (previousMarkers && !nextMarkers) await this.core.setFeatures({ points: false });
|
|
680
|
+
if (filterChanged) {
|
|
681
|
+
this.core.setPointFilter({
|
|
682
|
+
types: next.markerTypes === "*" ? void 0 : next.markerTypes,
|
|
683
|
+
subregions: next.subregionId ? [next.subregionId] : void 0
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
if (regionChanged) await this.core.setRegion(next.regionId);
|
|
687
|
+
if (next.locale !== previous.locale) await this.core.setLocale(next.locale);
|
|
688
|
+
if (next.floorId !== previous.floorId) this.core.setFloor(next.floorId);
|
|
689
|
+
if (next.markerClustering !== previous.markerClustering) {
|
|
690
|
+
this.core.setMarkerClustering(next.markerClustering);
|
|
691
|
+
}
|
|
692
|
+
if (nextMarkers !== previousMarkers || next.labels !== previous.labels || next.boundaries !== previous.boundaries) {
|
|
693
|
+
await this.core.setFeatures({
|
|
694
|
+
points: nextMarkers,
|
|
695
|
+
labels: next.labels,
|
|
696
|
+
boundaries: next.boundaries
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
if (viewChanged) {
|
|
700
|
+
this.core.setView({
|
|
701
|
+
regionId: next.regionId,
|
|
702
|
+
floorId: next.floorId,
|
|
703
|
+
x: next.center.x,
|
|
704
|
+
y: next.center.y,
|
|
705
|
+
zoom: next.zoom
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
const view = this.core.getView();
|
|
709
|
+
this.state = {
|
|
710
|
+
...next,
|
|
711
|
+
locale: this.core.getLocale().resolved,
|
|
712
|
+
center: { x: view.x, y: view.y },
|
|
713
|
+
zoom: view.zoom
|
|
714
|
+
};
|
|
715
|
+
this.controls.sync(this.state);
|
|
716
|
+
} finally {
|
|
717
|
+
this.applying = false;
|
|
718
|
+
this.root.classList.remove("loading");
|
|
719
|
+
}
|
|
720
|
+
this.notify();
|
|
721
|
+
}
|
|
722
|
+
resize() {
|
|
723
|
+
this.assertAlive();
|
|
724
|
+
this.core.resize();
|
|
725
|
+
}
|
|
726
|
+
destroy = () => {
|
|
727
|
+
if (this.destroyed) return;
|
|
728
|
+
this.destroyed = true;
|
|
729
|
+
this.unsubscribe();
|
|
730
|
+
this.controls.destroy?.();
|
|
731
|
+
this.options.signal?.removeEventListener("abort", this.destroy);
|
|
732
|
+
this.core.destroy();
|
|
733
|
+
this.root.remove();
|
|
734
|
+
mounted.delete(this.host);
|
|
735
|
+
};
|
|
736
|
+
};
|
|
737
|
+
async function createOEMWidget(container, options = {}) {
|
|
738
|
+
if (typeof document === "undefined") throw new Error("createOEMWidget must run in a browser");
|
|
739
|
+
options.signal?.throwIfAborted();
|
|
740
|
+
const host = resolveContainer(container);
|
|
741
|
+
if (mounted.has(host)) throw new Error("This container already hosts an OEM Widget");
|
|
742
|
+
const root = document.createElement("div");
|
|
743
|
+
root.className = "oemWidget loading";
|
|
744
|
+
if (options.className) root.classList.add(...options.className.split(/\s+/).filter(Boolean));
|
|
745
|
+
root.dataset.theme = options.theme ?? "light";
|
|
746
|
+
const mapHost = document.createElement("div");
|
|
747
|
+
mapHost.className = "mapHost";
|
|
748
|
+
root.append(mapHost);
|
|
749
|
+
host.append(root);
|
|
750
|
+
mounted.add(host);
|
|
751
|
+
let core;
|
|
752
|
+
let widget;
|
|
753
|
+
try {
|
|
754
|
+
const resources = options.resources ?? defaultOEMResources;
|
|
755
|
+
const manifest = options.manifest ? validateOEMManifest(options.manifest) : await loadOEMManifest(resources, options.signal);
|
|
756
|
+
installFonts(root, manifest, resources);
|
|
757
|
+
const state = normalizeState(options, manifest);
|
|
758
|
+
core = await createOEM(mapHost, {
|
|
759
|
+
resources,
|
|
760
|
+
manifest,
|
|
761
|
+
regionId: state.regionId,
|
|
762
|
+
floorId: state.floorId,
|
|
763
|
+
locale: state.locale,
|
|
764
|
+
theme: options.theme,
|
|
765
|
+
view: {
|
|
766
|
+
regionId: state.regionId,
|
|
767
|
+
floorId: state.floorId,
|
|
768
|
+
x: state.center.x,
|
|
769
|
+
y: state.center.y,
|
|
770
|
+
zoom: state.zoom
|
|
771
|
+
},
|
|
772
|
+
features: {
|
|
773
|
+
points: hasMarkers(state),
|
|
774
|
+
labels: state.labels,
|
|
775
|
+
boundaries: state.boundaries
|
|
776
|
+
},
|
|
777
|
+
markerClustering: state.markerClustering,
|
|
778
|
+
pointFilter: {
|
|
779
|
+
types: state.markerTypes === "*" ? void 0 : state.markerTypes,
|
|
780
|
+
subregions: state.subregionId ? [state.subregionId] : void 0
|
|
781
|
+
},
|
|
782
|
+
lockDrag: options.lockDrag ?? false,
|
|
783
|
+
lockZoom: options.lockZoom ?? false,
|
|
784
|
+
signal: options.signal,
|
|
785
|
+
onError: options.onError
|
|
786
|
+
});
|
|
787
|
+
widget = new Widget(host, root, core, manifest, options, state);
|
|
788
|
+
root.classList.remove("loading");
|
|
789
|
+
options.onReady?.(widget);
|
|
790
|
+
return widget;
|
|
791
|
+
} catch (error) {
|
|
792
|
+
if (widget) widget.destroy();
|
|
793
|
+
else {
|
|
794
|
+
core?.destroy();
|
|
795
|
+
root.remove();
|
|
796
|
+
mounted.delete(host);
|
|
797
|
+
}
|
|
798
|
+
throw error;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// packages/sdk/src/state.ts
|
|
803
|
+
var parseBoolean = (value) => {
|
|
804
|
+
if (value === null) return void 0;
|
|
805
|
+
if (["1", "true", "on", "yes"].includes(value.toLowerCase())) return true;
|
|
806
|
+
if (["0", "false", "off", "no"].includes(value.toLowerCase())) return false;
|
|
807
|
+
return void 0;
|
|
808
|
+
};
|
|
809
|
+
var parseNumber = (value) => {
|
|
810
|
+
if (value === null || value.trim() === "") return void 0;
|
|
811
|
+
const number = Number(value);
|
|
812
|
+
return Number.isFinite(number) ? number : void 0;
|
|
813
|
+
};
|
|
814
|
+
var parseList = (value) => {
|
|
815
|
+
if (value === null) return void 0;
|
|
816
|
+
return [...new Set(value.split(",").map((entry) => entry.trim()).filter(Boolean))];
|
|
817
|
+
};
|
|
818
|
+
var toSearchParams = (source) => {
|
|
819
|
+
const trimmed = source.trim();
|
|
820
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(trimmed)) return new URL(trimmed).searchParams;
|
|
821
|
+
const query = trimmed.includes("?") ? trimmed.slice(trimmed.indexOf("?") + 1) : trimmed;
|
|
822
|
+
return new URLSearchParams(query.replace(/^\?/, ""));
|
|
823
|
+
};
|
|
824
|
+
function parseOEMUrlState(source) {
|
|
825
|
+
const params = typeof source === "string" ? toSearchParams(source) : source;
|
|
826
|
+
const filter = params.get("f");
|
|
827
|
+
const x = parseNumber(params.get("x"));
|
|
828
|
+
const y = parseNumber(params.get("y"));
|
|
829
|
+
return {
|
|
830
|
+
region: params.get("r") ?? void 0,
|
|
831
|
+
subregion: params.get("s"),
|
|
832
|
+
floor: params.get("layer") ?? void 0,
|
|
833
|
+
locale: params.get("l") ?? void 0,
|
|
834
|
+
markerTypes: filter === "*" ? "*" : filter === null ? void 0 : parseList(filter) ?? false,
|
|
835
|
+
labels: parseBoolean(params.get("labels") ?? params.get("names")),
|
|
836
|
+
boundaries: parseBoolean(params.get("boundaries") ?? params.get("boundary")),
|
|
837
|
+
markerClustering: parseBoolean(params.get("cluster")),
|
|
838
|
+
zoom: parseNumber(params.get("z")),
|
|
839
|
+
center: x === void 0 || y === void 0 ? void 0 : { x, y }
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
export {
|
|
843
|
+
createOEMWidget,
|
|
844
|
+
parseOEMUrlState
|
|
845
|
+
};
|
|
846
|
+
//# sourceMappingURL=index.js.map
|