@cowwo/dsh-icon-custom 0.10.0 → 0.10.1
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/client/client.js +58 -7
- package/package.json +1 -1
package/client/client.js
CHANGED
|
@@ -1229,7 +1229,8 @@ window.__ModuleLoader__.load({
|
|
|
1229
1229
|
event.preventDefault();
|
|
1230
1230
|
event.stopPropagation();
|
|
1231
1231
|
const rect = node.getBoundingClientRect();
|
|
1232
|
-
|
|
1232
|
+
// `bottom` is what the panel drops below; see unreadPanelPlacement.
|
|
1233
|
+
toggleUnreadPanel({ left: rect.left, top: rect.top, bottom: rect.bottom });
|
|
1233
1234
|
});
|
|
1234
1235
|
}
|
|
1235
1236
|
const count = effectiveCount();
|
|
@@ -1284,6 +1285,52 @@ window.__ModuleLoader__.load({
|
|
|
1284
1285
|
headerBadgeHost = null;
|
|
1285
1286
|
headerBadgeHostPosition = "";
|
|
1286
1287
|
}
|
|
1288
|
+
/** Gap between the panel and whatever it is anchored to. */
|
|
1289
|
+
const UNREAD_PANEL_GAP = 6;
|
|
1290
|
+
/** Breathing room from the window edges. */
|
|
1291
|
+
const UNREAD_PANEL_MARGIN = 8;
|
|
1292
|
+
/** The panel's practical width (between its minWidth and maxWidth below). */
|
|
1293
|
+
const UNREAD_PANEL_WIDTH = 300;
|
|
1294
|
+
/** How tall the scrolling session list may get before it scrolls. */
|
|
1295
|
+
const UNREAD_PANEL_LIST_MAX = 320;
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* Where the unread panel goes, in viewport coordinates.
|
|
1299
|
+
*
|
|
1300
|
+
* It opens DOWNWARD from the badge. The original math pinned the panel's
|
|
1301
|
+
* BOTTOM edge just above the anchor — correct while the trigger sat in the
|
|
1302
|
+
* sidebar footer, and wrong the moment that trigger moved to the section
|
|
1303
|
+
* header: the same formula then parked the panel's bottom near the top of the
|
|
1304
|
+
* window and grew the panel upward, off the top of the screen (the title and
|
|
1305
|
+
* the first workspace heading were what got cut off). Anchoring on the
|
|
1306
|
+
* anchor's `bottom` is what drops it below the badge instead.
|
|
1307
|
+
*
|
|
1308
|
+
* Horizontally it stays clear of the sidebar: the panel is a frame-wide
|
|
1309
|
+
* overlay, and covering the sidebar — and the very badge that opened it — was
|
|
1310
|
+
* the other half of the complaint.
|
|
1311
|
+
* @param anchor - the badge's viewport rect (`{left, top, bottom}`), or null.
|
|
1312
|
+
* @param sidebarRight - the browsing region's right edge, or null.
|
|
1313
|
+
* @param viewportWidth - window width.
|
|
1314
|
+
* @param viewportHeight - window height.
|
|
1315
|
+
* @returns `{ left, top, listMaxHeight }` in viewport pixels.
|
|
1316
|
+
*/
|
|
1317
|
+
function unreadPanelPlacement(anchor, sidebarRight, viewportWidth, viewportHeight) {
|
|
1318
|
+
let left = UNREAD_PANEL_MARGIN;
|
|
1319
|
+
let top = UNREAD_PANEL_MARGIN;
|
|
1320
|
+
if (anchor !== null && typeof anchor === "object") {
|
|
1321
|
+
if (typeof anchor.left === "number") left = anchor.left;
|
|
1322
|
+
const below = typeof anchor.bottom === "number" ? anchor.bottom : anchor.top;
|
|
1323
|
+
if (typeof below === "number") top = below + UNREAD_PANEL_GAP;
|
|
1324
|
+
}
|
|
1325
|
+
if (typeof sidebarRight === "number" && sidebarRight > 0) left = Math.max(left, sidebarRight + UNREAD_PANEL_GAP);
|
|
1326
|
+
left = Math.max(UNREAD_PANEL_MARGIN, Math.min(left, viewportWidth - UNREAD_PANEL_WIDTH - UNREAD_PANEL_MARGIN));
|
|
1327
|
+
top = Math.max(UNREAD_PANEL_MARGIN, top);
|
|
1328
|
+
// Never taller than what is left below the panel's own top, so a long list
|
|
1329
|
+
// scrolls inside the panel instead of pushing it off the bottom edge.
|
|
1330
|
+
const listMaxHeight = Math.max(120, Math.min(UNREAD_PANEL_LIST_MAX, viewportHeight - top - 72));
|
|
1331
|
+
return { left: Math.round(left), top: Math.round(top), listMaxHeight: Math.round(listMaxHeight) };
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1287
1334
|
/**
|
|
1288
1335
|
* The list behind that number, in the frame-wide overlay layer so it escapes
|
|
1289
1336
|
* the sidebar's clipping and scroll container. Clicking a row opens that
|
|
@@ -1312,22 +1359,26 @@ window.__ModuleLoader__.load({
|
|
|
1312
1359
|
if (!open) return null;
|
|
1313
1360
|
const t = props.t;
|
|
1314
1361
|
const anchor = unreadPanelAnchor;
|
|
1362
|
+
// Measured, not assumed: the panel has to clear whatever width the sidebar
|
|
1363
|
+
// currently has (it is resizable and collapsible).
|
|
1364
|
+
let sidebarRight = null;
|
|
1365
|
+
try {
|
|
1366
|
+
const region = workspaceDotContainer();
|
|
1367
|
+
if (region !== null) sidebarRight = region.getBoundingClientRect().right;
|
|
1368
|
+
} catch { /* measurement is best effort; the margin fallback still applies */ }
|
|
1369
|
+
const place = unreadPanelPlacement(anchor, sidebarRight, window.innerWidth, window.innerHeight);
|
|
1315
1370
|
const style = {
|
|
1316
|
-
position: "fixed", left: "
|
|
1371
|
+
position: "fixed", left: place.left + "px", top: place.top + "px", zIndex: 60, pointerEvents: "auto",
|
|
1317
1372
|
minWidth: "240px", maxWidth: "340px", padding: "8px", borderRadius: "12px",
|
|
1318
1373
|
border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-1)",
|
|
1319
1374
|
boxShadow: "0 10px 30px rgba(0,0,0,.18)", color: "var(--dsw-alias-label-primary)", fontSize: "12px"
|
|
1320
1375
|
};
|
|
1321
|
-
if (anchor !== null && typeof anchor === "object") {
|
|
1322
|
-
style.left = Math.max(8, Math.round(anchor.left)) + "px";
|
|
1323
|
-
style.bottom = Math.max(8, Math.round(window.innerHeight - anchor.top + 6)) + "px";
|
|
1324
|
-
}
|
|
1325
1376
|
const items = Array.isArray(snapshot.items) ? snapshot.items : [];
|
|
1326
1377
|
return React.createElement("div", { "data-icon-custom-unread": "1", style },
|
|
1327
1378
|
React.createElement("div", { style: { fontWeight: 600, padding: "2px 6px 8px" } }, t("unreadPanelTitle")),
|
|
1328
1379
|
items.length === 0
|
|
1329
1380
|
? React.createElement("div", { style: { padding: "2px 6px 8px", opacity: 0.7 } }, t("unreadPanelEmpty"))
|
|
1330
|
-
: React.createElement("div", { style: { display: "flex", flexDirection: "column", maxHeight: "
|
|
1381
|
+
: React.createElement("div", { style: { display: "flex", flexDirection: "column", maxHeight: place.listMaxHeight + "px", overflowY: "auto" } },
|
|
1331
1382
|
groupUnreadByWorkspace(items).map((group) => React.createElement("div", { key: group.where === "" ? "\u0000none" : group.where },
|
|
1332
1383
|
React.createElement("div", { style: { padding: "7px 8px 3px", fontSize: "11px", fontWeight: 600, opacity: 0.55, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } },
|
|
1333
1384
|
group.where === "" ? t("unreadPanelOther") : group.where
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cowwo/dsh-icon-custom",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "自定义浏览器标签页图标:在设置页上传 SVG/PNG/ICO,立即生效并持久保存,自动适配安卓/iOS/平板 PWA 图标。· Custom browser tab favicon: upload SVG/PNG/ICO from the settings page, applies instantly and persists, auto-sizes PWA icons for Android/iOS/tablets.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|