hiwork-core 0.0.2 → 0.0.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/README.md +3 -0
- package/lib/client.js +78 -2
- package/lib/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,6 +103,9 @@ ctx.inject(['hiworkFeatureCenter'], (scope) => {
|
|
|
103
103
|
其余项按 order 排序。`icon` 是内联 SVG 字符串;缺省用兜底图标,保证侧栏折叠成
|
|
104
104
|
56px 图标栏时每个入口仍有可辨识图形(折叠态由宿主加在侧栏根上的 `*_collapsed`
|
|
105
105
|
类驱动纯 CSS:隐藏文字、图标放大到 18px,与原生控件同形)。
|
|
106
|
+
折叠态悬停 500ms 弹出提示气泡,黑底白字、圆角与字号逐项对齐框架 `ui-primitives`
|
|
107
|
+
的 `Tooltip`(`--dsw-alias-tooltip-bg`),取代浏览器自带的浅色 `title` 提示;
|
|
108
|
+
展开态文字已可见,与原生控件一致(`Tooltip disabled={wide}`)不出气泡。
|
|
106
109
|
找不到原生按钮时不抛错、不隐藏任何入口,菜单只列 feature(安全降级)。
|
|
107
110
|
- **中央区 takeover**:挂一个 `[data-hiwork-feature-root]` React root,用
|
|
108
111
|
`html[data-hiwork-active-page]` 上的纯 CSS 规则隐藏原生会话子树(保持挂载、保留状态);
|
package/lib/client.js
CHANGED
|
@@ -2352,6 +2352,8 @@ var MENU_ATTRIBUTE = "data-hiwork-feature-menu";
|
|
|
2352
2352
|
var MENU_ITEM_ATTRIBUTE = "data-hiwork-feature-menu-item";
|
|
2353
2353
|
var MENU_ICON_ATTRIBUTE = "data-hiwork-feature-menu-icon";
|
|
2354
2354
|
var MENU_LABEL_ATTRIBUTE = "data-hiwork-feature-menu-label";
|
|
2355
|
+
var TOOLTIP_ATTRIBUTE = "data-hiwork-feature-tooltip";
|
|
2356
|
+
var TOOLTIP_VISIBLE_ATTRIBUTE = "data-hiwork-feature-tooltip-visible";
|
|
2355
2357
|
var NATIVE_HIDDEN_ATTRIBUTE = "data-hiwork-native-new-session";
|
|
2356
2358
|
var FEATURE_ROOT_ATTRIBUTE = "data-hiwork-feature-root";
|
|
2357
2359
|
var OWNER_ATTRIBUTE = "data-hiwork-owner";
|
|
@@ -2359,6 +2361,8 @@ var ACTIVE_PAGE_ATTRIBUTE = "data-hiwork-active-page";
|
|
|
2359
2361
|
var NATIVE_MENU_ITEM_ID = "__native_new_session__";
|
|
2360
2362
|
var DEFAULT_NEW_SESSION_LABEL = "\u65B0\u4F1A\u8BDD";
|
|
2361
2363
|
var STYLE_ELEMENT_ID = "hiwork-feature-center-styles";
|
|
2364
|
+
var TOOLTIP_DELAY_MS = 500;
|
|
2365
|
+
var TOOLTIP_GAP_PX = 8;
|
|
2362
2366
|
var DEFAULT_NEW_SESSION_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M8 3.4v9.2M3.4 8h9.2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>';
|
|
2363
2367
|
var DEFAULT_FEATURE_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><rect x="2.75" y="2.75" width="10.5" height="10.5" rx="3" stroke="currentColor" stroke-width="1.5"/><circle cx="8" cy="8" r="1.4" fill="currentColor"/></svg>';
|
|
2364
2368
|
var SESSION_ROW_SELECTOR = [
|
|
@@ -2382,6 +2386,12 @@ var featureCenterCss = [
|
|
|
2382
2386
|
`[class*="sidebarCol"] [class*="collapsed"] [${MENU_LABEL_ATTRIBUTE}] { display: none; }`,
|
|
2383
2387
|
`[class*="sidebarCol"] [class*="collapsed"] [${MENU_ITEM_ATTRIBUTE}] { justify-content: center; padding: 8px 0; }`,
|
|
2384
2388
|
`[class*="sidebarCol"] [class*="collapsed"] [${MENU_ICON_ATTRIBUTE}], [class*="sidebarCol"] [class*="collapsed"] [${MENU_ICON_ATTRIBUTE}] > svg { width: 18px; height: 18px; }`,
|
|
2389
|
+
// 折叠态提示气泡:几何与配色逐项对齐框架 `ui-primitives` 的 `Tooltip.module.css`
|
|
2390
|
+
// (黑底白字、3px 7px 内边距、8px 圆角、13/20 字号、150ms 淡入、最大半屏宽)。
|
|
2391
|
+
`[${TOOLTIP_ATTRIBUTE}] { position: fixed; z-index: 100; display: none; width: max-content; max-width: 50vw; padding: 3px 7px; border-radius: 8px; background: var(--dsw-alias-tooltip-bg, #2c2c2e); color: var(--dsw-static-neutral-bluish-00, #fff); font-size: 13px; line-height: 20px; white-space: pre-line; overflow-wrap: break-word; pointer-events: none; transform: translateY(-50%); }`,
|
|
2392
|
+
`[${TOOLTIP_ATTRIBUTE}][${TOOLTIP_VISIBLE_ATTRIBUTE}="true"] { display: block; animation: hiwork-feature-tooltip-in 150ms var(--ds-ease-in-out, ease); }`,
|
|
2393
|
+
`@keyframes hiwork-feature-tooltip-in { from { opacity: 0; } }`,
|
|
2394
|
+
`@media (prefers-reduced-motion: reduce) { [${TOOLTIP_ATTRIBUTE}][${TOOLTIP_VISIBLE_ATTRIBUTE}="true"] { animation: none; } }`,
|
|
2385
2395
|
// 与原生会话根同一背景(`background: var(--dsw-alias-bg-base)`);不显式上色的话
|
|
2386
2396
|
// takeover 区会透出框架灰底,和侧栏同色而不是原生页面的白色/深色。
|
|
2387
2397
|
`[${FEATURE_ROOT_ATTRIBUTE}] { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; overflow: hidden; background: var(--dsw-alias-bg-base, #fff); }`,
|
|
@@ -2418,6 +2428,12 @@ function nativeNewSessionButton(root) {
|
|
|
2418
2428
|
function conversationContainer() {
|
|
2419
2429
|
return document.querySelector(CONVERSATION_SELECTOR) ?? void 0;
|
|
2420
2430
|
}
|
|
2431
|
+
function isCollapsedRail(item) {
|
|
2432
|
+
const host = item.closest('[class*="collapsed"]');
|
|
2433
|
+
if (host === null) return false;
|
|
2434
|
+
const column = item.closest(SIDEBAR_SELECTOR);
|
|
2435
|
+
return column === null || column.contains(host);
|
|
2436
|
+
}
|
|
2421
2437
|
function mountFeatureCenter(options) {
|
|
2422
2438
|
const { registry } = options;
|
|
2423
2439
|
const interval = options.retryIntervalMs ?? 200;
|
|
@@ -2428,7 +2444,7 @@ function mountFeatureCenter(options) {
|
|
|
2428
2444
|
return { dispose: () => void 0, openNativeHome: () => void 0 };
|
|
2429
2445
|
}
|
|
2430
2446
|
const isActiveOwner = () => activeOwner === owner;
|
|
2431
|
-
for (const stale of Array.from(document.querySelectorAll(`[${MENU_ATTRIBUTE}], [${FEATURE_ROOT_ATTRIBUTE}], #${STYLE_ELEMENT_ID}`))) {
|
|
2447
|
+
for (const stale of Array.from(document.querySelectorAll(`[${MENU_ATTRIBUTE}], [${FEATURE_ROOT_ATTRIBUTE}], [${TOOLTIP_ATTRIBUTE}], #${STYLE_ELEMENT_ID}`))) {
|
|
2432
2448
|
stale.remove();
|
|
2433
2449
|
}
|
|
2434
2450
|
for (const hidden of Array.from(document.querySelectorAll(`[${NATIVE_HIDDEN_ATTRIBUTE}]`))) {
|
|
@@ -2449,6 +2465,58 @@ function mountFeatureCenter(options) {
|
|
|
2449
2465
|
let retryTimer;
|
|
2450
2466
|
let retries = 0;
|
|
2451
2467
|
let disposed = false;
|
|
2468
|
+
let tooltipElement;
|
|
2469
|
+
let tooltipTimer;
|
|
2470
|
+
let tooltipItem;
|
|
2471
|
+
const hideTooltip = () => {
|
|
2472
|
+
if (tooltipTimer !== void 0) {
|
|
2473
|
+
clearTimeout(tooltipTimer);
|
|
2474
|
+
tooltipTimer = void 0;
|
|
2475
|
+
}
|
|
2476
|
+
tooltipItem = void 0;
|
|
2477
|
+
tooltipElement?.removeAttribute(TOOLTIP_VISIBLE_ATTRIBUTE);
|
|
2478
|
+
};
|
|
2479
|
+
const ensureTooltip = () => {
|
|
2480
|
+
let element = tooltipElement;
|
|
2481
|
+
if (element === void 0) {
|
|
2482
|
+
element = document.createElement("div");
|
|
2483
|
+
element.setAttribute(TOOLTIP_ATTRIBUTE, "");
|
|
2484
|
+
element.setAttribute(OWNER_ATTRIBUTE, owner);
|
|
2485
|
+
element.setAttribute("role", "tooltip");
|
|
2486
|
+
document.body.appendChild(element);
|
|
2487
|
+
tooltipElement = element;
|
|
2488
|
+
}
|
|
2489
|
+
return element;
|
|
2490
|
+
};
|
|
2491
|
+
const placeTooltip = (item, element) => {
|
|
2492
|
+
const rect = item.getBoundingClientRect();
|
|
2493
|
+
const width = element.offsetWidth;
|
|
2494
|
+
const overflowRight = rect.right + TOOLTIP_GAP_PX + width > window.innerWidth;
|
|
2495
|
+
const left = overflowRight ? rect.left - TOOLTIP_GAP_PX - width : rect.right + TOOLTIP_GAP_PX;
|
|
2496
|
+
element.style.left = `${Math.max(0, left)}px`;
|
|
2497
|
+
element.style.top = `${rect.top + rect.height / 2}px`;
|
|
2498
|
+
};
|
|
2499
|
+
const showTooltip = (item, label) => {
|
|
2500
|
+
const element = ensureTooltip();
|
|
2501
|
+
element.textContent = label;
|
|
2502
|
+
element.setAttribute(TOOLTIP_VISIBLE_ATTRIBUTE, "true");
|
|
2503
|
+
tooltipItem = item;
|
|
2504
|
+
placeTooltip(item, element);
|
|
2505
|
+
};
|
|
2506
|
+
const bindTooltip = (item, label) => {
|
|
2507
|
+
item.addEventListener("pointerenter", () => {
|
|
2508
|
+
hideTooltip();
|
|
2509
|
+
tooltipTimer = setTimeout(() => {
|
|
2510
|
+
tooltipTimer = void 0;
|
|
2511
|
+
if (disposed || !item.isConnected || !isCollapsedRail(item)) return;
|
|
2512
|
+
showTooltip(item, label);
|
|
2513
|
+
}, TOOLTIP_DELAY_MS);
|
|
2514
|
+
});
|
|
2515
|
+
item.addEventListener("pointerleave", () => {
|
|
2516
|
+
if (tooltipItem === item || tooltipTimer !== void 0) hideTooltip();
|
|
2517
|
+
});
|
|
2518
|
+
};
|
|
2519
|
+
document.addEventListener("scroll", hideTooltip, true);
|
|
2452
2520
|
const clearTakeover = () => {
|
|
2453
2521
|
const reactRoot = root;
|
|
2454
2522
|
root = void 0;
|
|
@@ -2494,6 +2562,7 @@ function mountFeatureCenter(options) {
|
|
|
2494
2562
|
if (!(target instanceof Element)) return;
|
|
2495
2563
|
const item = target.closest(`[${MENU_ITEM_ATTRIBUTE}]`);
|
|
2496
2564
|
if (item === null || !(menuElement?.contains(item) ?? false)) return;
|
|
2565
|
+
hideTooltip();
|
|
2497
2566
|
const id = item.getAttribute(MENU_ITEM_ATTRIBUTE);
|
|
2498
2567
|
if (id === null) return;
|
|
2499
2568
|
if (id === NATIVE_MENU_ITEM_ID) {
|
|
@@ -2507,7 +2576,8 @@ function mountFeatureCenter(options) {
|
|
|
2507
2576
|
item.type = "button";
|
|
2508
2577
|
item.setAttribute(MENU_ITEM_ATTRIBUTE, id);
|
|
2509
2578
|
if (active) item.setAttribute("data-hiwork-feature-active", "true");
|
|
2510
|
-
item.
|
|
2579
|
+
item.setAttribute("aria-label", label);
|
|
2580
|
+
bindTooltip(item, label);
|
|
2511
2581
|
const iconSpan = document.createElement("span");
|
|
2512
2582
|
iconSpan.setAttribute(MENU_ICON_ATTRIBUTE, "");
|
|
2513
2583
|
iconSpan.innerHTML = icon;
|
|
@@ -2538,6 +2608,7 @@ function mountFeatureCenter(options) {
|
|
|
2538
2608
|
].join("|");
|
|
2539
2609
|
if (signature === menuSignature) return;
|
|
2540
2610
|
menuSignature = signature;
|
|
2611
|
+
hideTooltip();
|
|
2541
2612
|
const items = [];
|
|
2542
2613
|
if (native !== void 0) items.push(createMenuItem(NATIVE_MENU_ITEM_ID, newSessionLabel, nativeIcon(native), false));
|
|
2543
2614
|
for (const item of features) {
|
|
@@ -2675,6 +2746,11 @@ function mountFeatureCenter(options) {
|
|
|
2675
2746
|
unsubscribeRegistry();
|
|
2676
2747
|
unsubscribeLocale?.();
|
|
2677
2748
|
document.removeEventListener("click", onDocumentClickCapture, true);
|
|
2749
|
+
document.removeEventListener("scroll", hideTooltip, true);
|
|
2750
|
+
hideTooltip();
|
|
2751
|
+
const tooltip = tooltipElement;
|
|
2752
|
+
tooltipElement = void 0;
|
|
2753
|
+
if (tooltip !== void 0 && tooltip.getAttribute(OWNER_ATTRIBUTE) === owner) tooltip.remove();
|
|
2678
2754
|
clearTakeover();
|
|
2679
2755
|
const element = menuElement;
|
|
2680
2756
|
menuElement = void 0;
|
package/lib/index.js
CHANGED