dsh-m 0.9.57 → 0.9.59
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/CHANGELOG.md +24 -0
- package/lib/client.js +38 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,18 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
8
8
|
|
|
9
9
|
## 中文
|
|
10
10
|
|
|
11
|
+
### 0.9.59 优化:分类行左缘内缩 8px + 全屏下空转 ⌃ 收起钮按行数派生隐藏
|
|
12
|
+
|
|
13
|
+
- **分类行内缩(`f42e5be`)**:分类 chips 行与搜索框/卡片左缘齐平,小颗粒胶囊贴着大元素左缘视觉憋仄;分类行整体右移 8px 留出呼吸空间。仅作用分类行(ZoneChips),上方分区 chips 行不随动。`offsetLeft` 与 absolute 定位同以 `.dshm-chips` 的 padding 盒为原点 → `+N` 跟随、右侧筛选钉位、`offsetTop` 裁剪几何均不受影响。
|
|
14
|
+
- **⌃ 空转修复(`7cbdf11`)**:全屏等宽容器下分类 chips 可能排进 ≤ maxRows(2)行,展开态 `⌃` 收起钮点击后布局零变化(`hiddenCount=0` 连 `+N` 都不会出现),成死按钮。新增展开态行数测量 `expRows`(复用 `chipRows` 纯函数,measure 展开态分支只写 `expRows` 不动 `geom`),`⌃` 仅当展开行数 > maxRows 时渲染;ResizeObserver 在展开态也挂载——全屏↔窗口切换的宽度变化正是重测触发源,全屏下 `⌃` 自动隐藏、退回窗口自动恢复,行数含 `⌃` 自身故显隐收敛不振荡。
|
|
15
|
+
- **验证**:1224 项测试全绿;真实 DSH Web 实测窗口/全屏两态 `⌃` 显隐与收起行为、分类行 8px 内缩生效。
|
|
16
|
+
|
|
17
|
+
### 0.9.58 优化:+N 动态紧跟末可见分类,并与其框高一致
|
|
18
|
+
|
|
19
|
+
- **位置(`4ade54f`)**:在 0.9.57 的独立安全槽位中,根据末可见分类 chip 的实测右缘设置 `+N` 左缘 = 右缘 + 6px;槽位由 52px 扩为 60px,窄宽时仍不与分类或筛选重叠。精选区没有筛选时不额外预留筛选 gutter;展开态 `⌃` 仍位于列表尾部。
|
|
20
|
+
- **框高(`aeed14c`)**:`+N` 直接采用末可见分类的实测 border-box 高度,并以 inline-flex 居中数字,不再依赖西文数字与中文标签各自的浏览器自动行高。只改 `+N`,不改变分区 chip 的尺寸。
|
|
21
|
+
- **验证**:1224 项测试与 typecheck 全绿;用真实 DSH 目录数据在 880/700/520/420/320px 社区区测得两框同高 21px、顶边差 0、水平间距约 5–6px、相交面积 0;260px 精选区也无重叠,精选↔社区往返正常。
|
|
22
|
+
|
|
11
23
|
### 0.9.57 修复:精选切换后面板崩溃/无法重开 + 折叠筛选双框与 +N 重叠
|
|
12
24
|
|
|
13
25
|
- **根因(0.9.56 真机复现)**:精选区的标签对象可能每次父级重渲染都重建;layout effect 依赖 `chips` 数组引用及自身更新的 `geom`,同步测量反复进入、最终触发 React `Maximum update depth exceeded`(#185)。React 清空面板树但留下 `#dshm-panel-root`,旧入口守卫只检查容器存在,导致点击入口永久无效。叠加折叠筛选定位壳和按钮的双边框、以及 +N 位置夹取/文档流试验在窄宽下压住相邻 chip。
|
|
@@ -426,6 +438,18 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
|
|
|
426
438
|
|
|
427
439
|
## English
|
|
428
440
|
|
|
441
|
+
### Improved in 0.9.59 — 8px category-row inset and a derived visibility for the no-op collapse toggle
|
|
442
|
+
|
|
443
|
+
- **Category-row inset (`f42e5be`)**: the category chip row sat flush with the search box and card grid, and small pills hugging large elements felt cramped; the row now starts 8px further right for breathing room. Only the category row (ZoneChips) moves — the zone chip row above stays put. `offsetLeft` and absolute positioning share the `.dshm-chips` padding-box origin, so `+N` tracking, the pinned Filter trigger and `offsetTop`-based clip geometry are all unaffected.
|
|
444
|
+
- **No-op `⌃` fix (`7cbdf11`)**: at fullscreen width all category chips can fit within maxRows (2) rows, where clicking the expanded-state `⌃` collapse toggle changed nothing visually (`hiddenCount=0`, not even a `+N` appears) — a dead button. Added an expanded-state row measurement `expRows` (reusing the pure `chipRows` helper; the expanded measure branch only writes `expRows`, never `geom`), and `⌃` renders only when expanded rows exceed maxRows. The ResizeObserver now also observes while expanded — the width change of fullscreen↔window switches is exactly the remeasure trigger — so `⌃` hides itself in fullscreen and returns back in the window; counting includes `⌃` itself, so visibility converges without oscillation.
|
|
445
|
+
- **Verification**: 1224 tests pass; verified against real DSH Web for `⌃` visibility and collapse behavior in both window and fullscreen, and for the 8px category-row inset.
|
|
446
|
+
|
|
447
|
+
### Improved in 0.9.58 — the +N toggle follows the last visible category at matching height
|
|
448
|
+
|
|
449
|
+
- **Position (`4ade54f`)**: within the 0.9.57 reserved, overlap-free slot, position `+N` from the measured right edge of the last visible category chip plus 6px. The slot grows from 52px to 60px to preserve clearance at narrow widths. Curated rows without a Filter trigger still reserve no unnecessary Filter gutter; expanded `⌃` remains at list end.
|
|
450
|
+
- **Equal height (`aeed14c`)**: use the last visible chip’s measured border-box height for `+N` and center its text with inline-flex, rather than letting Latin-only text and CJK category labels acquire different automatic line heights. Other chips keep their existing dimensions.
|
|
451
|
+
- **Verification**: 1224 tests and typecheck pass. Against the real DSH catalog, community layouts at 880/700/520/420/320px showed equal 21px boxes, 0px top-edge delta, roughly 5–6px horizontal separation, and zero overlap; the 260px curated layout had zero overlap and curated↔community switching remained stable.
|
|
452
|
+
|
|
429
453
|
### Fixed in 0.9.57 — curated-zone update loop, dead panel entry, and collapsed filter/overflow overlaps
|
|
430
454
|
|
|
431
455
|
- **Root cause (reproduced on 0.9.56)**: curated labels may get a fresh object on every parent render; the layout effect depended on the `chips` array identity and on its own `geom` update. Synchronous remeasurement repeatedly entered the same commit, eventually throwing React `Maximum update depth exceeded` (#185). React emptied the panel tree while leaving `#dshm-panel-root`; the old entry guard checked only for a container and silently blocked every later open. The pinned Filter wrapper also added a second border around its bordered button, while the clamped/inline `+N` iterations overlapped neighboring chips at narrow widths.
|
package/lib/client.js
CHANGED
|
@@ -1573,7 +1573,7 @@ var h = React.createElement;
|
|
|
1573
1573
|
var { useState, useEffect, useLayoutEffect, useCallback, useMemo, useRef } = React;
|
|
1574
1574
|
var PLUGIN_ID = "dsh-m";
|
|
1575
1575
|
var API = "/dshm";
|
|
1576
|
-
var { DEFAULT_PAGE_SIZE: DEFAULT_PAGE_SIZE2, MARKET_PAGE_SIZES: MARKET_PAGE_SIZES2, pageItems: pageItems2, createZoneState: createZoneState2, normalizeMarketQuery: normalizeMarketQuery2, resetPageOnFilterChange: resetPageOnFilterChange2, normalizeMarketResponse: normalizeMarketResponse2, registryNotice: registryNotice2, zoneChips: zoneChips2, marketNotice: marketNotice2, searchSourceOf: searchSourceOf2, mergeLatestFields: mergeLatestFields2, countBeyondRows: countBeyondRows2, clipTopOf: clipTopOf2, visibleRowTop: visibleRowTop2, autoExpandDecision: autoExpandDecision2 } = (init_market_state(), __toCommonJS(market_state_exports));
|
|
1576
|
+
var { DEFAULT_PAGE_SIZE: DEFAULT_PAGE_SIZE2, MARKET_PAGE_SIZES: MARKET_PAGE_SIZES2, pageItems: pageItems2, createZoneState: createZoneState2, normalizeMarketQuery: normalizeMarketQuery2, resetPageOnFilterChange: resetPageOnFilterChange2, normalizeMarketResponse: normalizeMarketResponse2, registryNotice: registryNotice2, zoneChips: zoneChips2, marketNotice: marketNotice2, searchSourceOf: searchSourceOf2, mergeLatestFields: mergeLatestFields2, chipRows: chipRows2, countBeyondRows: countBeyondRows2, clipTopOf: clipTopOf2, visibleRowTop: visibleRowTop2, autoExpandDecision: autoExpandDecision2 } = (init_market_state(), __toCommonJS(market_state_exports));
|
|
1577
1577
|
var { readMarketSnapshot: readMarketSnapshot2, writeMarketSnapshot: writeMarketSnapshot2, isDefaultFirstPageQuery: isDefaultFirstPageQuery2 } = (init_market_snapshot(), __toCommonJS(market_snapshot_exports));
|
|
1578
1578
|
var { backdropCloseHandlers: backdropCloseHandlers2 } = (init_backdrop(), __toCommonJS(backdrop_exports));
|
|
1579
1579
|
var { createMarkdown: createMarkdown2 } = (init_markdown(), __toCommonJS(markdown_exports));
|
|
@@ -2204,11 +2204,11 @@ var CSS = `
|
|
|
2204
2204
|
.dshm-chips{display:flex;flex-wrap:wrap;gap:6px;position:relative}
|
|
2205
2205
|
.dshm-chips-clip{overflow:hidden}
|
|
2206
2206
|
.dshm-chips-gutter{padding-right:var(--dshm-clip-gutter,96px)}
|
|
2207
|
-
/* +N/\u7B5B\u9009\u5206\u522B\u5360\u53F3\u4FA7
|
|
2208
|
-
.dshm-chips.dshm-chips-clip.dshm-chips-reserve{padding-right:
|
|
2209
|
-
.dshm-chips.dshm-chips-clip.dshm-chips-gutter.dshm-chips-reserve{padding-right:calc(var(--dshm-clip-gutter,96px) +
|
|
2207
|
+
/* +N/\u7B5B\u9009\u5206\u522B\u5360\u53F3\u4FA7 60/96px\uFF1B\u4FDD\u7559\u4F18\u5148\u7EA7\u9AD8\u4E8E\u5355\u72EC\u7684\u7B5B\u9009 gutter\u3002 */
|
|
2208
|
+
.dshm-chips.dshm-chips-clip.dshm-chips-reserve{padding-right:60px}
|
|
2209
|
+
.dshm-chips.dshm-chips-clip.dshm-chips-gutter.dshm-chips-reserve{padding-right:calc(var(--dshm-clip-gutter,96px) + 60px)}
|
|
2210
2210
|
.dsvm-chipmore{position:absolute;right:8px}
|
|
2211
|
-
.dsvm-chipmore-plus{position:absolute;
|
|
2211
|
+
.dsvm-chipmore-plus{position:absolute;z-index:1;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;background-color:var(--dsw-alias-bg-base,#fff);background-image:linear-gradient(var(--dsw-alias-bg-base,#fff),var(--dsw-alias-bg-base,#fff)),linear-gradient(var(--dsw-alias-bg-base,#fff),var(--dsw-alias-bg-base,#fff))}
|
|
2212
2212
|
.dsvm-searchrow{display:flex;align-items:center;gap:8px}
|
|
2213
2213
|
.dsvm-searchrow .dshm-search{flex:1;display:flex}
|
|
2214
2214
|
.dshm-search{position:relative}
|
|
@@ -2230,6 +2230,10 @@ var CSS = `
|
|
|
2230
2230
|
.dshm-chip:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06))}
|
|
2231
2231
|
.dshm-chip.on{background:var(--dsw-specific-sidebar-nav-item-active,rgba(38,49,72,.08));border-color:transparent;color:var(--dsw-alias-label-primary,inherit);font-weight:500}
|
|
2232
2232
|
.dsvm-chipswrap{position:sticky;top:-14px;z-index:5;background:var(--dsw-alias-bg-base,#fff);padding:8px 0;margin:-8px 0;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.14))}
|
|
2233
|
+
/* 0.9.59 \u5206\u7C7B\u884C\u5DE6\u7F18\u5185\u7F29 8px\uFF1A\u4E0E\u641C\u7D22\u6846/\u5361\u7247\u5DE6\u7F18\u9F50\u5E73\u65F6\u80F6\u56CA\u884C\u89C6\u89C9\u618B\u4EC4\uFF0C\u6574\u4F53\u53F3\u79FB\u7559\u51FA\u547C\u5438\u7A7A\u95F4\u3002
|
|
2234
|
+
\u4EC5\u4F5C\u7528\u5206\u7C7B\u884C\uFF08ZoneChips\uFF09\uFF0C\u4E0A\u65B9\u5206\u533A chips \u884C\u4E0D\u968F\u52A8\uFF1BoffsetLeft \u4E0E absolute \u5B9A\u4F4D\u540C\u4EE5
|
|
2235
|
+
.dshm-chips \u7684 padding \u76D2\u4E3A\u539F\u70B9 \u2192 +N \u8DDF\u968F\u3001\u53F3\u4FA7\u7B5B\u9009\u9489\u4F4D\u3001offsetTop \u88C1\u526A\u51E0\u4F55\u5747\u4E0D\u53D7\u5F71\u54CD\u3002 */
|
|
2236
|
+
.dsvm-chipswrap .dshm-chips{padding-left:8px}
|
|
2233
2237
|
.dsvm-pagejump{display:inline-flex;align-items:center;gap:4px;margin-left:8px}
|
|
2234
2238
|
.dsvm-pagejump-input{width:56px;height:24px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.28));background:transparent;color:var(--dsw-alias-label-secondary,#4b5563);border-radius:999px;padding:0 10px;font:inherit;font-size:12px;outline:none;text-align:center;transition:border-color .15s,color .15s}
|
|
2235
2239
|
.dsvm-pagejump-input:hover{color:var(--dsw-alias-label-primary,inherit)}
|
|
@@ -2732,6 +2736,7 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2732
2736
|
const chipsSignature = JSON.stringify(chips.map((c) => [c.id, c.labelKey ? lookup(c.labelKey) : c.label, c.count]));
|
|
2733
2737
|
const [expanded, setExpanded] = useState(false);
|
|
2734
2738
|
const [stuck, setStuck] = useState(false);
|
|
2739
|
+
const [expRows, setExpRows] = useState(0);
|
|
2735
2740
|
const [geom, setGeom] = useState(null);
|
|
2736
2741
|
const [rowHeight, setRowHeight] = useState(0);
|
|
2737
2742
|
const wrapRef = useRef(null);
|
|
@@ -2742,7 +2747,7 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2742
2747
|
const maxH = maxRows * (rowHeight || 22) + (maxRows - 1) * 6;
|
|
2743
2748
|
const measure = useCallback(() => {
|
|
2744
2749
|
const el = wrapRef.current;
|
|
2745
|
-
if (!el
|
|
2750
|
+
if (!el) return;
|
|
2746
2751
|
const tops = [];
|
|
2747
2752
|
let rh = 0;
|
|
2748
2753
|
for (const k of el.children) {
|
|
@@ -2751,12 +2756,26 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2751
2756
|
if (!rh && k.offsetHeight) rh = k.offsetHeight;
|
|
2752
2757
|
}
|
|
2753
2758
|
if (!tops.length) return;
|
|
2759
|
+
if (expanded) {
|
|
2760
|
+
const rows = chipRows2(tops);
|
|
2761
|
+
setExpRows((prev) => prev === rows ? prev : rows);
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2754
2764
|
setGeom((prev) => {
|
|
2755
2765
|
const clipTopV = clipTopOf2(tops, maxRows);
|
|
2756
2766
|
const hiddenCount2 = countBeyondRows2(tops, maxRows);
|
|
2757
2767
|
const lastVisTop2 = visibleRowTop2(tops, maxRows);
|
|
2758
|
-
|
|
2759
|
-
|
|
2768
|
+
let lastVisRight = 0;
|
|
2769
|
+
let lastVisHeight = rh;
|
|
2770
|
+
for (const k of el.children) {
|
|
2771
|
+
if (!(k instanceof HTMLElement) || k.getAttribute("data-chip") !== "1") continue;
|
|
2772
|
+
if (k.offsetTop < clipTopV) {
|
|
2773
|
+
lastVisRight = k.offsetLeft + k.offsetWidth;
|
|
2774
|
+
lastVisHeight = k.offsetHeight;
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
const next = { tops, clipTop: clipTopV, hiddenCount: hiddenCount2, lastVisTop: lastVisTop2, lastVisRight, lastVisHeight };
|
|
2778
|
+
const same = prev && prev.hiddenCount === next.hiddenCount && prev.clipTop === next.clipTop && prev.lastVisTop === next.lastVisTop && prev.lastVisRight === next.lastVisRight && prev.lastVisHeight === next.lastVisHeight && prev.tops.length === next.tops.length && prev.tops.every((t, i) => t === next.tops[i]);
|
|
2760
2779
|
return same ? prev : next;
|
|
2761
2780
|
});
|
|
2762
2781
|
setRowHeight((prev) => Math.abs(prev - rh) <= 0.5 ? prev : rh);
|
|
@@ -2775,7 +2794,7 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2775
2794
|
if (d.expand) setExpanded(true);
|
|
2776
2795
|
}, [active, activeIdx, expanded, geom]);
|
|
2777
2796
|
useLayoutEffect(() => {
|
|
2778
|
-
|
|
2797
|
+
measure();
|
|
2779
2798
|
}, [chipsSignature, active, expanded, zone, stuck, rowHeight, measure]);
|
|
2780
2799
|
useEffect(() => {
|
|
2781
2800
|
if (expanded || !geom || typeof requestAnimationFrame !== "function") return;
|
|
@@ -2784,7 +2803,7 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2784
2803
|
}, [geom, expanded, measure]);
|
|
2785
2804
|
useEffect(() => {
|
|
2786
2805
|
const el = wrapRef.current;
|
|
2787
|
-
if (!el ||
|
|
2806
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
2788
2807
|
let raf = 0;
|
|
2789
2808
|
const ro = new ResizeObserver(() => {
|
|
2790
2809
|
cancelAnimationFrame(raf);
|
|
@@ -2795,7 +2814,7 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2795
2814
|
ro.disconnect();
|
|
2796
2815
|
cancelAnimationFrame(raf);
|
|
2797
2816
|
};
|
|
2798
|
-
}, [
|
|
2817
|
+
}, [measure]);
|
|
2799
2818
|
useEffect(() => {
|
|
2800
2819
|
if (expanded || typeof document === "undefined" || !document.fonts?.ready) return;
|
|
2801
2820
|
let cancelled = false;
|
|
@@ -2818,6 +2837,8 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2818
2837
|
const clipTop = geom ? geom.clipTop : Infinity;
|
|
2819
2838
|
const hiddenCount = geom ? geom.hiddenCount : 0;
|
|
2820
2839
|
const lastVisTop = geom ? geom.lastVisTop : 0;
|
|
2840
|
+
const moreLeft = geom ? geom.lastVisRight + 6 : 0;
|
|
2841
|
+
const moreHeight = geom ? geom.lastVisHeight : rowHeight || 22;
|
|
2821
2842
|
const btn = (c, i) => {
|
|
2822
2843
|
const top = geom ? geom.tops[i + 1] : null;
|
|
2823
2844
|
const clipped = clip && geom && typeof top === "number" && top >= clipTop;
|
|
@@ -2858,16 +2879,18 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2858
2879
|
...chips.map((c, i) => btn(c, i)),
|
|
2859
2880
|
// 折叠态 +N(0.9.57 终版):绝对定位钉在「末可见行右端预留槽」——出流不挤压 chip、不参与
|
|
2860
2881
|
// 行分组(data-more);预留槽由 .dshm-chips-clip 的 padding-right 划出,几何保证零重叠;
|
|
2861
|
-
//
|
|
2882
|
+
// 左缘动态紧贴末可见 chip 右侧 6px,仍受右侧预留槽保护;垂直对齐末可见行。
|
|
2862
2883
|
clip && geom && hiddenCount > 0 ? h("button", {
|
|
2863
2884
|
key: "more",
|
|
2864
2885
|
"data-more": "1",
|
|
2865
2886
|
className: "dshm-chip dsvm-chipmore-plus",
|
|
2866
|
-
style: { top: `${lastVisTop}px` },
|
|
2887
|
+
style: { top: `${lastVisTop}px`, left: `${moreLeft}px`, height: `${moreHeight}px` },
|
|
2867
2888
|
onClick: () => setExpanded(true)
|
|
2868
2889
|
}, `+${hiddenCount}`) : null,
|
|
2869
2890
|
expanded ? [
|
|
2870
|
-
|
|
2891
|
+
// 0.9.59:仅当展开布局真实超过 maxRows 行才渲染 ⌃——否则收起是零变化空操作(死按钮)。
|
|
2892
|
+
// 行数含 ⌃ 自身:若它恰好独占一行,隐藏后行数回落仍 ≤ maxRows,显隐收敛不振荡。
|
|
2893
|
+
expRows > maxRows ? h("button", {
|
|
2871
2894
|
key: "collapse",
|
|
2872
2895
|
"data-chip": "1",
|
|
2873
2896
|
className: "dshm-chip",
|
|
@@ -2877,7 +2900,7 @@ function ZoneChips({ zone, counts, labels, active, onPick, trailing, wrapTitle }
|
|
|
2877
2900
|
if (active) autoRef.current = active;
|
|
2878
2901
|
setExpanded(false);
|
|
2879
2902
|
}
|
|
2880
|
-
}, "\u2303"),
|
|
2903
|
+
}, "\u2303") : null,
|
|
2881
2904
|
trailing || null
|
|
2882
2905
|
] : null
|
|
2883
2906
|
),
|