@maplat/ui 0.11.2 → 0.11.3
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/maplat-ui.es.js +2413 -2335
- package/dist/maplat-ui.umd.js +201 -201
- package/dist/ui_utils.d.ts +1 -0
- package/package.json +2 -16
- package/src/ui_init.ts +203 -1
- package/src/ui_utils.ts +11 -0
package/dist/ui_utils.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maplat/ui",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"description": "Maplat is the cool Historical Map/Illustrated Map Viewer.\nIt can transform each map coordinates with nonlinear but homeomorphic projection and makes possible that the maps can collaborate with GPS/accurate maps, without distorting original maps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/maplat-ui.umd.js",
|
|
@@ -37,14 +37,12 @@
|
|
|
37
37
|
"@c4h/chuci": "0.2.4",
|
|
38
38
|
"@c4h/weiwudi": "^0.2.0",
|
|
39
39
|
"@maplat/core": "0.12.2",
|
|
40
|
-
"@maplat/transform": "^0.4.0",
|
|
41
40
|
"@turf/turf": "^7.3.1",
|
|
42
41
|
"@types/page": "^1.11.9",
|
|
43
42
|
"bootstrap.native": "^5.1.6",
|
|
44
43
|
"page": "^1.11.6",
|
|
45
44
|
"qrcode": "^1.5.4",
|
|
46
45
|
"swiper": "^6.8.4",
|
|
47
|
-
"tiny-emitter": "^2.1.0",
|
|
48
46
|
"workbox-core": "^7.4.0",
|
|
49
47
|
"workbox-expiration": "^7.4.0",
|
|
50
48
|
"workbox-precaching": "^7.4.0",
|
|
@@ -72,25 +70,13 @@
|
|
|
72
70
|
"ol": "^10.7.0",
|
|
73
71
|
"prettier": "^3.7.4",
|
|
74
72
|
"typescript": "^5.9.3",
|
|
75
|
-
"typescript-eslint": "^8.
|
|
73
|
+
"typescript-eslint": "^8.51.0",
|
|
76
74
|
"vite": "^6.4.1",
|
|
77
75
|
"vite-plugin-dts": "^4.5.4",
|
|
78
76
|
"vitest": "^3.2.4",
|
|
79
77
|
"workbox-build": "^7.4.0",
|
|
80
78
|
"workbox-window": "^7.4.0"
|
|
81
79
|
},
|
|
82
|
-
"overrides": {
|
|
83
|
-
"@babel/traverse": "7.23.2",
|
|
84
|
-
"glob-parent": "5.1.2",
|
|
85
|
-
"axios": "1.6.0",
|
|
86
|
-
"minimist": "1.2.8",
|
|
87
|
-
"glob": "^11.0.0",
|
|
88
|
-
"rimraf": "^6.0.0",
|
|
89
|
-
"inflight": "npm:inflight-purged@^2.0.0",
|
|
90
|
-
"@types/minimatch": "^5.1.2",
|
|
91
|
-
"source-map": "^0.7.4",
|
|
92
|
-
"sourcemap-codec": "^1.4.8"
|
|
93
|
-
},
|
|
94
80
|
"scripts": {
|
|
95
81
|
"dev": "concurrently \"pnpm watch:sw\" \"vite --host\"",
|
|
96
82
|
"build:sw": "esbuild src/service-worker/index.ts --bundle --outfile=dist/service-worker.js --format=iife --define:self.__WB_MANIFEST=[] && node -e \"require('fs').copyFileSync('dist/service-worker.js', 'public/service-worker.js')\"",
|
package/src/ui_init.ts
CHANGED
|
@@ -21,7 +21,7 @@ import ContextMenu from "./contextmenu";
|
|
|
21
21
|
import Weiwudi from "@c4h/weiwudi";
|
|
22
22
|
import absoluteUrl from "./absolute_url";
|
|
23
23
|
import * as QRCode from "qrcode";
|
|
24
|
-
import { ellips, isBasemap } from "./ui_utils";
|
|
24
|
+
import { ellips, isBasemap, encBytes } from "./ui_utils";
|
|
25
25
|
|
|
26
26
|
import { poiWebControl } from "./ui_marker";
|
|
27
27
|
|
|
@@ -1117,6 +1117,7 @@ export async function uiInit(ui: MaplatUi, appOption: MaplatAppOption) {
|
|
|
1117
1117
|
});
|
|
1118
1118
|
}
|
|
1119
1119
|
});
|
|
1120
|
+
modal.show();
|
|
1120
1121
|
} else if (control === "copyright") {
|
|
1121
1122
|
ui.modalSetting("map");
|
|
1122
1123
|
const mapData = ui.core!.from!;
|
|
@@ -1156,6 +1157,207 @@ export async function uiInit(ui: MaplatUi, appOption: MaplatAppOption) {
|
|
|
1156
1157
|
}
|
|
1157
1158
|
}
|
|
1158
1159
|
});
|
|
1160
|
+
|
|
1161
|
+
const cacheDiv = modalRoot.querySelector(
|
|
1162
|
+
".modal_cache_content"
|
|
1163
|
+
) as HTMLElement;
|
|
1164
|
+
const cacheSize = cacheDiv.querySelector(".cache_size") as HTMLElement;
|
|
1165
|
+
let cacheFetch = cacheDiv.querySelector(
|
|
1166
|
+
".cache_fetch"
|
|
1167
|
+
) as HTMLButtonElement;
|
|
1168
|
+
let cacheDelete = cacheDiv.querySelector(
|
|
1169
|
+
".cache_delete"
|
|
1170
|
+
) as HTMLButtonElement;
|
|
1171
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1172
|
+
const weiwudi = (mapData as any).weiwudi;
|
|
1173
|
+
if (
|
|
1174
|
+
ui.core!.enableCache &&
|
|
1175
|
+
weiwudi &&
|
|
1176
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1177
|
+
!(mapData as any).vector
|
|
1178
|
+
) {
|
|
1179
|
+
cacheDiv.style.display = "block";
|
|
1180
|
+
cacheFetch.style.display = "none";
|
|
1181
|
+
cacheDelete.style.display = "none";
|
|
1182
|
+
const totalTile = weiwudi.totalTile;
|
|
1183
|
+
let isFetching = false;
|
|
1184
|
+
|
|
1185
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1186
|
+
let currentStats: any = undefined;
|
|
1187
|
+
|
|
1188
|
+
const updateButtons = () => {
|
|
1189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1190
|
+
const coreAny = ui.core as any;
|
|
1191
|
+
const t = coreAny.t
|
|
1192
|
+
? coreAny.t.bind(ui.core)
|
|
1193
|
+
: ui.core!.translate.bind(ui.core);
|
|
1194
|
+
|
|
1195
|
+
if (totalTile) {
|
|
1196
|
+
cacheFetch.style.display = "inline-block";
|
|
1197
|
+
if (isFetching) {
|
|
1198
|
+
cacheFetch.innerHTML =
|
|
1199
|
+
t("html.cache_cancel") || "Cancel download";
|
|
1200
|
+
cacheFetch.classList.remove("btn-default");
|
|
1201
|
+
cacheFetch.classList.add("btn-danger");
|
|
1202
|
+
if (!cacheFetch.classList.contains("btn-default"))
|
|
1203
|
+
cacheFetch.classList.add("btn-default");
|
|
1204
|
+
cacheFetch.disabled = false;
|
|
1205
|
+
} else {
|
|
1206
|
+
cacheFetch.innerHTML = t("html.cache_fetch") || "Bulk download";
|
|
1207
|
+
if (!cacheFetch.classList.contains("btn-default"))
|
|
1208
|
+
cacheFetch.classList.add("btn-default");
|
|
1209
|
+
cacheFetch.classList.remove("btn-danger");
|
|
1210
|
+
|
|
1211
|
+
// Disable if 100%
|
|
1212
|
+
if (currentStats && currentStats.count === currentStats.total) {
|
|
1213
|
+
cacheFetch.disabled = true;
|
|
1214
|
+
// User requested disabled, not hidden
|
|
1215
|
+
cacheFetch.style.display = "inline-block";
|
|
1216
|
+
} else {
|
|
1217
|
+
cacheFetch.disabled = false;
|
|
1218
|
+
cacheFetch.style.display = "inline-block";
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
} else {
|
|
1222
|
+
cacheFetch.style.display = "none";
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
if (currentStats && currentStats.size > 0) {
|
|
1226
|
+
cacheDelete.style.display = "inline-block";
|
|
1227
|
+
// Disable if fetching
|
|
1228
|
+
cacheDelete.disabled = isFetching;
|
|
1229
|
+
} else {
|
|
1230
|
+
// User requested disabled, not hidden
|
|
1231
|
+
cacheDelete.style.display = "inline-block";
|
|
1232
|
+
cacheDelete.disabled = true;
|
|
1233
|
+
}
|
|
1234
|
+
if (isFetching) cacheDelete.disabled = true; // Double ensure
|
|
1235
|
+
};
|
|
1236
|
+
|
|
1237
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1238
|
+
const showStats = async (stats: any | undefined = undefined) => {
|
|
1239
|
+
if (!stats) stats = await weiwudi.stats();
|
|
1240
|
+
currentStats = stats;
|
|
1241
|
+
|
|
1242
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1243
|
+
const coreAny = ui.core as any;
|
|
1244
|
+
const t = coreAny.t
|
|
1245
|
+
? coreAny.t.bind(ui.core)
|
|
1246
|
+
: ui.core!.translate.bind(ui.core);
|
|
1247
|
+
|
|
1248
|
+
const sizeStr = isFetching
|
|
1249
|
+
? t("html.cache_processing") || "Calculating..."
|
|
1250
|
+
: encBytes(stats.size || 0);
|
|
1251
|
+
|
|
1252
|
+
if (totalTile) {
|
|
1253
|
+
const count = stats.count || 0;
|
|
1254
|
+
const percent = Math.floor((1000 * count) / totalTile);
|
|
1255
|
+
cacheSize.innerText = `${sizeStr} (${
|
|
1256
|
+
count
|
|
1257
|
+
} / ${totalTile} tiles [${percent / 10}%])`;
|
|
1258
|
+
} else {
|
|
1259
|
+
cacheSize.innerText = `${sizeStr} (${stats.count || 0} tiles)`;
|
|
1260
|
+
}
|
|
1261
|
+
updateButtons();
|
|
1262
|
+
};
|
|
1263
|
+
showStats();
|
|
1264
|
+
|
|
1265
|
+
let updateFrame = 0;
|
|
1266
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1267
|
+
let latestStats: any = null;
|
|
1268
|
+
|
|
1269
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1270
|
+
const fetchHandler = (evt: any) => {
|
|
1271
|
+
if (evt.type === "proceed") {
|
|
1272
|
+
isFetching = true;
|
|
1273
|
+
latestStats = {
|
|
1274
|
+
size: evt.detail.size || currentStats?.size || 0,
|
|
1275
|
+
count: evt.detail.processed || evt.detail.count || 0,
|
|
1276
|
+
total: evt.detail.total || totalTile || 0
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
if (!updateFrame) {
|
|
1280
|
+
updateFrame = requestAnimationFrame(() => {
|
|
1281
|
+
updateFrame = 0;
|
|
1282
|
+
try {
|
|
1283
|
+
if (latestStats) showStats(latestStats);
|
|
1284
|
+
} catch (e) {
|
|
1285
|
+
console.error("Error in showStats:", e);
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
} else if (
|
|
1290
|
+
evt.type === "finish" ||
|
|
1291
|
+
evt.type === "stop" ||
|
|
1292
|
+
evt.type === "canceled"
|
|
1293
|
+
) {
|
|
1294
|
+
if (updateFrame) {
|
|
1295
|
+
cancelAnimationFrame(updateFrame);
|
|
1296
|
+
updateFrame = 0;
|
|
1297
|
+
}
|
|
1298
|
+
isFetching = false;
|
|
1299
|
+
latestStats = null;
|
|
1300
|
+
showStats();
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
// Weiwudi might dispatch 'proceed', 'finish', 'stop', 'canceled'
|
|
1304
|
+
weiwudi.addEventListener("proceed", fetchHandler);
|
|
1305
|
+
weiwudi.addEventListener("finish", fetchHandler);
|
|
1306
|
+
weiwudi.addEventListener("stop", fetchHandler);
|
|
1307
|
+
// Check if weiwudi dispatches 'canceled'. Based on my read, it does dispatch e.data.type
|
|
1308
|
+
// And weiwudi_gw_logic sends type: 'canceled'.
|
|
1309
|
+
weiwudi.addEventListener("canceled", fetchHandler);
|
|
1310
|
+
|
|
1311
|
+
const modalEl = modalRoot.querySelector(".modalBase") as HTMLElement;
|
|
1312
|
+
const removeListeners = () => {
|
|
1313
|
+
weiwudi.removeEventListener("proceed", fetchHandler);
|
|
1314
|
+
weiwudi.removeEventListener("finish", fetchHandler);
|
|
1315
|
+
weiwudi.removeEventListener("stop", fetchHandler);
|
|
1316
|
+
weiwudi.removeEventListener("canceled", fetchHandler);
|
|
1317
|
+
modalEl.removeEventListener("hidden.bs.modal", removeListeners);
|
|
1318
|
+
};
|
|
1319
|
+
modalEl.addEventListener("hidden.bs.modal", removeListeners);
|
|
1320
|
+
|
|
1321
|
+
const newElem = cacheFetch.cloneNode(true);
|
|
1322
|
+
cacheFetch.parentNode!.replaceChild(newElem, cacheFetch);
|
|
1323
|
+
// Initial update handled by showStats -> updateButtons
|
|
1324
|
+
cacheFetch = newElem as HTMLButtonElement;
|
|
1325
|
+
|
|
1326
|
+
cacheFetch.addEventListener("click", async () => {
|
|
1327
|
+
if (isFetching) {
|
|
1328
|
+
await weiwudi.cancel();
|
|
1329
|
+
} else {
|
|
1330
|
+
isFetching = true; // Set immediately to update UI
|
|
1331
|
+
updateButtons();
|
|
1332
|
+
try {
|
|
1333
|
+
await weiwudi.fetchAll();
|
|
1334
|
+
} catch {
|
|
1335
|
+
isFetching = false;
|
|
1336
|
+
updateButtons();
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
await showStats();
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
const newElem2 = cacheDelete.cloneNode(true);
|
|
1343
|
+
cacheDelete.parentNode!.replaceChild(newElem2, cacheDelete);
|
|
1344
|
+
cacheDelete = newElem2 as HTMLButtonElement;
|
|
1345
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1346
|
+
const t = (ui.core as any).t;
|
|
1347
|
+
cacheDelete.innerHTML =
|
|
1348
|
+
(t ? t.call(ui.core, "html.cache_delete") : undefined) ||
|
|
1349
|
+
ui.core!.translate("html.cache_delete") ||
|
|
1350
|
+
"Clear";
|
|
1351
|
+
|
|
1352
|
+
cacheDelete.addEventListener("click", async () => {
|
|
1353
|
+
if (isFetching) return; // Should be disabled, but safety check
|
|
1354
|
+
await weiwudi.clean();
|
|
1355
|
+
await showStats();
|
|
1356
|
+
});
|
|
1357
|
+
} else {
|
|
1358
|
+
cacheDiv.style.display = "none";
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1159
1361
|
modal.show();
|
|
1160
1362
|
} else if (control === "border") {
|
|
1161
1363
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/ui_utils.ts
CHANGED
|
@@ -85,3 +85,14 @@ export function isBasemap(source: unknown): boolean {
|
|
|
85
85
|
if (source.constructor && source.constructor.isBasemap_ === true) return true;
|
|
86
86
|
return true;
|
|
87
87
|
}
|
|
88
|
+
|
|
89
|
+
export function encBytes(bytes: number): string {
|
|
90
|
+
const suffixes = ["Bytes", "KBytes", "MBytes", "GBytes"];
|
|
91
|
+
let suffix = "Bytes";
|
|
92
|
+
for (let i = 0; i < suffixes.length; i++) {
|
|
93
|
+
suffix = suffixes[i];
|
|
94
|
+
if (bytes < 1000) break;
|
|
95
|
+
bytes = bytes / 1000;
|
|
96
|
+
}
|
|
97
|
+
return `${Math.floor(bytes * 10) / 10} ${suffix}`;
|
|
98
|
+
}
|