📝 docs(code-reader): useState 状态卡片原型的调用树挪进解构表每一行
This commit is contained in:
1 parent
785bfd4f78
commit
719453a28b
5 files changed
+2619
-2215
No files matched your search
Binary file not shown.
|
Before Width: | Height: | Size: 767 KiB After Width: | Height: | Size: 775 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 268 KiB |
+28
-18
@@ -503,9 +503,25 @@ function group(parent, segs, color, bg) {
|
|||||||
// ─── 卡片 ────────────────────────────────────────────────────────────────────
|
// ─── 卡片 ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卡片右上角:问 AI、编辑;给了 `calls` 再加调用树,同 webview 的 BlockTools:调用次数大于 0 时图标后面跟一个小圆标
|
* 调用树按钮,同 webview 的 BlockTools:git-branch 图标,调用次数大于 0 时后面跟一个小圆标
|
||||||
* (徽标底色、字色取 Dark Modern 的 badge),是 0 时只有图标。返回这一排的宽度。
|
* (徽标底色、字色取 Dark Modern 的 badge),是 0 时只有图标。`tip` 是悬停文字,记在图层名里。返回按钮的宽度。
|
||||||
*/
|
*/
|
||||||
|
function callTreeButton(parent, calls, tip) {
|
||||||
|
const name = tip ? "git-branch / " + tip : "git-branch";
|
||||||
|
if (calls === 0) {
|
||||||
|
const tree = row(parent, name, { width: 22, height: 20, justify: "center", radius: 4 });
|
||||||
|
icon(tree, "git-branch", C.desc, 14);
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
const tree = row(parent, name, { width: "fit_content", height: 20, padding: [0, 4], gap: 1, radius: 4 });
|
||||||
|
icon(tree, "git-branch", C.desc, 14);
|
||||||
|
const badge = row(tree, "Count", { width: 14, height: 14, justify: "center", radius: 7, fill: "#616161" });
|
||||||
|
// 一两个字的数字按内容长会被导出时挤小,宽度定死
|
||||||
|
text(badge, String(calls), { size: 9, weight: 700, color: "#f8f8f8", lh: 14, fixedW: Math.ceil(widthOf(String(calls), 9, false)) + 1 });
|
||||||
|
return 4 + 14 + 1 + 14 + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 卡片右上角:问 AI、编辑;给了 `calls` 再加调用树按钮。返回这一排的宽度。 */
|
||||||
function tools(parent, calls) {
|
function tools(parent, calls) {
|
||||||
const bar = row(parent, "Tools", { width: "fit_content", height: 20, gap: 2 });
|
const bar = row(parent, "Tools", { width: "fit_content", height: 20, gap: 2 });
|
||||||
for (const name of ["sparkles", "pencil"]) {
|
for (const name of ["sparkles", "pencil"]) {
|
||||||
@@ -514,28 +530,22 @@ function tools(parent, calls) {
|
|||||||
}
|
}
|
||||||
if (calls == null)
|
if (calls == null)
|
||||||
return 22 * 2 + 2;
|
return 22 * 2 + 2;
|
||||||
if (calls === 0) {
|
return 22 * 2 + 2 + 2 + callTreeButton(bar, calls);
|
||||||
const tree = row(bar, "git-branch", { width: 22, height: 20, justify: "center", radius: 4 });
|
|
||||||
icon(tree, "git-branch", C.desc, 14);
|
|
||||||
return 22 * 3 + 2 * 2;
|
|
||||||
}
|
|
||||||
const tree = row(bar, "git-branch", { width: "fit_content", height: 20, padding: [0, 4], gap: 1, radius: 4 });
|
|
||||||
icon(tree, "git-branch", C.desc, 14);
|
|
||||||
const badge = row(tree, "Count", { width: 14, height: 14, justify: "center", radius: 7, fill: "#616161" });
|
|
||||||
text(badge, String(calls), { size: 9, weight: 700, color: "#f8f8f8", lh: 14 });
|
|
||||||
return 22 * 2 + 2 + 2 + (4 + 14 + 1 + 14 + 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 悬停提示气泡,样子同 webview 的 data-tooltip:悬停浮层的底色、边框、11px 字。`x`、`y` 是它在父节点里的位置。 */
|
/**
|
||||||
|
* 悬停提示气泡,样子同 webview 的 data-tooltip:悬停浮层的底色、边框、11px 字。
|
||||||
|
* `x`、`y` 是它在自由摆放的父节点里的位置;不给时跟着自动排版排在前一个兄弟后面,高度压成一行的 20px。
|
||||||
|
*/
|
||||||
function tooltip(parent, label, x, y) {
|
function tooltip(parent, label, x, y) {
|
||||||
const bw = Math.ceil(widthOf(label, 11, false)) + 16;
|
const bw = Math.ceil(widthOf(label, 11, false)) + 16;
|
||||||
const tip = I(parent, {
|
const placed = x != null ? { x: Math.round(x), y: Math.round(y) } : {};
|
||||||
|
const tip = I(parent, Object.assign({
|
||||||
type: "frame",
|
type: "frame",
|
||||||
name: "Tooltip",
|
name: "Tooltip",
|
||||||
x: Math.round(x),
|
}, placed, {
|
||||||
y: Math.round(y),
|
|
||||||
width: bw,
|
width: bw,
|
||||||
height: 22,
|
height: x != null ? 22 : 20,
|
||||||
layout: "horizontal",
|
layout: "horizontal",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
@@ -543,7 +553,7 @@ function tooltip(parent, label, x, y) {
|
|||||||
fill: solid("#202020"),
|
fill: solid("#202020"),
|
||||||
stroke: { thickness: 1, fill: solid("#454545") },
|
stroke: { thickness: 1, fill: solid("#454545") },
|
||||||
effects: [{ type: "shadow", offsetX: 0, offsetY: 2, blur: 8, spread: 0, color: "rgba(0,0,0,0.36)" }],
|
effects: [{ type: "shadow", offsetX: 0, offsetY: 2, blur: 8, spread: 0, color: "rgba(0,0,0,0.36)" }],
|
||||||
});
|
}));
|
||||||
text(tip, label, { size: 11, color: C.fg, lh: 14 });
|
text(tip, label, { size: 11, color: C.fg, lh: 14 });
|
||||||
return bw;
|
return bw;
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-31
@@ -1,6 +1,6 @@
|
|||||||
// 代码阅读 React 状态卡片:useState 不再写成「钩子 useState」,而是一张「状态」卡片,讲清楚五件事:
|
// 代码阅读 React 状态卡片:useState 不再写成「钩子 useState」,而是一张「状态」卡片,讲清楚五件事:
|
||||||
// 它是普通变量也是 React 状态(左边变量、状态两个图标);它是数组解构(名称照源码写出解构,展开是每一项);
|
// 它是普通变量也是 React 状态(左边变量、状态两个图标);它是数组解构(名称照源码写出解构,展开是每一项);
|
||||||
// 它来自 React 内置的 useState(悬停说明);状态类型;默认值。修改函数在哪调用,用右上角的调用树查。
|
// 它来自 React 内置的 useState(悬停说明);状态类型;默认值。解构出的每一项各有一个调用树按钮:当前值查谁用了它,修改函数查谁改了它。
|
||||||
// 画板一照抄 luffy-agent-platform 的 frontend/client-nextjs/src/app/desktop/login/page.tsx 第 1–37 行,
|
// 画板一照抄 luffy-agent-platform 的 frontend/client-nextjs/src/app/desktop/login/page.tsx 第 1–37 行,
|
||||||
// 画板二是手写的三行,讲没标类型、函数形式的默认值、只取当前值三种写法。
|
// 画板二是手写的三行,讲没标类型、函数形式的默认值、只取当前值三种写法。
|
||||||
// 窗口、对齐、别的卡片的画法在同目录 react-shared.js,接在它后面跑:`cat react-shared.js react-state.js | op design - --script`。
|
// 窗口、对齐、别的卡片的画法在同目录 react-shared.js,接在它后面跑:`cat react-shared.js react-state.js | op design - --script`。
|
||||||
@@ -84,26 +84,36 @@ const VARIANTS = sampleFile({
|
|||||||
|
|
||||||
// 种类「状态」:描边、淡底、两个图标都用变量色;左边先是变量卡片的图标,再是状态图标,悬停才出名字
|
// 种类「状态」:描边、淡底、两个图标都用变量色;左边先是变量卡片的图标,再是状态图标,悬停才出名字
|
||||||
const STATE = { label: "状态", color: "#9cdcfe", icon: "variable" };
|
const STATE = { label: "状态", color: "#9cdcfe", icon: "variable" };
|
||||||
const FROM_TIP = "React 内置钩子";
|
|
||||||
|
// 解构出的每一项:是什么、它那一行调用树按钮查什么(悬停文字)
|
||||||
|
const ITEMS = [
|
||||||
|
{ what: "当前值", tip: "谁用了这个状态" },
|
||||||
|
{ what: "修改函数", tip: "谁改了这个状态" },
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 「来自」那一行悬停时的气泡在卡片里的位置:在 `useState` 几个字正上方居中、隔 6px。
|
* 「是什么」一格:说法后面跟这一项自己的调用树按钮,`calls` 是它在样例里出现几次;
|
||||||
* 内容区从卡片内边距(上 8、左 8)、折叠箭头一列(14)、两个图标(42)各隔 10px 往后排;
|
* `bubble` 时按钮右边画出它的悬停气泡(上方是上一行的按钮,放上面会挡住它)。
|
||||||
* 「来自」前面是名称一行和解构表(表头一行、每项一行,各带 1px 分隔线、上下各 2px 留白)。
|
|
||||||
*/
|
*/
|
||||||
function fromBubble(labelW, items) {
|
function whatCell(item, calls, bubble) {
|
||||||
const tableH = 2 + 24 + items * (24 + 1) + 2;
|
return {
|
||||||
const rowTop = 8 + 20 + 3 + tableH + 3;
|
est: item.what,
|
||||||
const valueX = 8 + 14 + 10 + 42 + 10 + labelW + 12;
|
extra: 6 + 37,
|
||||||
const center = valueX + widthOf("useState", 13, true) / 1.04 / 2;
|
draw: (frame) => {
|
||||||
return { x: center - tooltipWidth(FROM_TIP) / 2, y: rowTop - 6 - 22 };
|
const line = row(frame, "What", { gap: 6, height: 20, width: "fit_content" });
|
||||||
|
text(line, item.what, { size: 12.5, lh: 20 });
|
||||||
|
callTreeButton(line, calls, item.tip);
|
||||||
|
if (bubble)
|
||||||
|
tooltip(line, item.tip);
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一处 useState:名称照源码写出解构(后面灰字「数组解构」,展开是「第几项 | 名称 | 是什么」)/ 来自 / 状态类型 / 默认值。
|
* 一处 useState:名称照源码写出解构(后面灰字「数组解构」,展开是「第几项 | 名称 | 是什么」,每一项一个调用树按钮)/
|
||||||
* `names` 是解构出的名字,第二个是修改函数;有修改函数时右上角有调用树,`calls` 是它被调用几次。
|
* 来自(悬停说是 React 内置钩子)/ 状态类型 / 默认值。
|
||||||
* `type` 是状态类型(没写时「未标注」),`initial` 是默认值的着色段。
|
* `names` 是解构出的名字,第二个是修改函数;`calls` 是各项在样例里出现几次。
|
||||||
* `bubble` 画出「来自」的悬停气泡:卡片和气泡放进一个自由摆放的框,气泡在前,才画在卡片上层。
|
* `type` 是状态类型(没写时「未标注」),`initial` 是默认值的着色段;`bubble` 画出修改函数那一行调用树按钮的悬停气泡。
|
||||||
*/
|
*/
|
||||||
function stateCard(s) {
|
function stateCard(s) {
|
||||||
return (parent, w) => {
|
return (parent, w) => {
|
||||||
@@ -111,15 +121,7 @@ function stateCard(s) {
|
|||||||
{ name: "variable", color: STATE.color, tip: "变量" },
|
{ name: "variable", color: STATE.color, tip: "变量" },
|
||||||
{ name: "toggle-right", color: STATE.color, tip: "React 状态" },
|
{ name: "toggle-right", color: STATE.color, tip: "React 状态" },
|
||||||
];
|
];
|
||||||
let holder = parent;
|
const ctx = card(parent, STATE, w, { name: s.names[0], icons });
|
||||||
let place = {};
|
|
||||||
if (s.bubble) {
|
|
||||||
holder = box(parent, "Hover / 来自", { layout: "none", width: w, height: "fit_content" });
|
|
||||||
const spot = fromBubble(76, s.names.length);
|
|
||||||
tooltip(holder, FROM_TIP, spot.x, spot.y);
|
|
||||||
place = { x: 0, y: 0 };
|
|
||||||
}
|
|
||||||
const ctx = card(holder, STATE, w, Object.assign({ name: s.names[0], icons, calls: s.names.length > 1 ? s.calls : null }, place));
|
|
||||||
prop(ctx, "名称", (frame) => {
|
prop(ctx, "名称", (frame) => {
|
||||||
const line = row(frame, "Destructure", { gap: 8, height: 20, width: "fit_content" });
|
const line = row(frame, "Destructure", { gap: 8, height: 20, width: "fit_content" });
|
||||||
const parts = [seg("[", C.fg)];
|
const parts = [seg("[", C.fg)];
|
||||||
@@ -133,8 +135,8 @@ function stateCard(s) {
|
|||||||
text(line, "数组解构", { size: 12.5, color: C.desc });
|
text(line, "数组解构", { size: 12.5, color: C.desc });
|
||||||
}, { fold: "open" });
|
}, { fold: "open" });
|
||||||
table(ctx, [{ name: "第几项", mono: false }, { name: "名称", color: C.variable }, { name: "是什么", mono: false }],
|
table(ctx, [{ name: "第几项", mono: false }, { name: "名称", color: C.variable }, { name: "是什么", mono: false }],
|
||||||
s.names.map((name, i) => [indexCell(i + 1), px(name, i === 0 ? C.variable : C.fn), i === 0 ? "当前值" : "修改函数"]));
|
s.names.map((name, i) => [indexCell(i + 1), px(name, i === 0 ? C.variable : C.fn), whatCell(ITEMS[i], s.calls[i], s.bubble && i === 1)]));
|
||||||
prop(ctx, "来自", px("useState", C.fn));
|
prop(ctx, "来自", (frame) => text(frame, px("useState", C.fn), { mono: true, name: "useState(悬停:React 内置钩子)" }));
|
||||||
prop(ctx, "状态类型", s.type || px("未标注", C.desc), { mono: Boolean(s.type) });
|
prop(ctx, "状态类型", s.type || px("未标注", C.desc), { mono: Boolean(s.type) });
|
||||||
prop(ctx, "默认值", s.initial);
|
prop(ctx, "默认值", s.initial);
|
||||||
};
|
};
|
||||||
@@ -175,7 +177,7 @@ function searchParamsHook(parent, w) {
|
|||||||
|
|
||||||
const ACTIVE_TAB = stateCard({
|
const ACTIVE_TAB = stateCard({
|
||||||
names: ["activeTab", "setActiveTab"],
|
names: ["activeTab", "setActiveTab"],
|
||||||
calls: 1,
|
calls: [1, 1],
|
||||||
type: [link("LoginTab", C.type)],
|
type: [link("LoginTab", C.type)],
|
||||||
initial: px("\"sms\"", C.string),
|
initial: px("\"sms\"", C.string),
|
||||||
bubble: true,
|
bubble: true,
|
||||||
@@ -183,7 +185,7 @@ const ACTIVE_TAB = stateCard({
|
|||||||
|
|
||||||
const PASSWORD_VIEW = stateCard({
|
const PASSWORD_VIEW = stateCard({
|
||||||
names: ["passwordView", "setPasswordView"],
|
names: ["passwordView", "setPasswordView"],
|
||||||
calls: 1,
|
calls: [1, 1],
|
||||||
type: [link("PasswordView", C.type)],
|
type: [link("PasswordView", C.type)],
|
||||||
initial: px("\"login\"", C.string),
|
initial: px("\"login\"", C.string),
|
||||||
});
|
});
|
||||||
@@ -235,16 +237,16 @@ windowOf("Prototype / React 状态 · 其他写法", WIN_W + BOARD_GAP, VARIANTS
|
|||||||
{
|
{
|
||||||
from: 1,
|
from: 1,
|
||||||
to: 1,
|
to: 1,
|
||||||
draw: stateCard({ names: ["open", "setOpen"], calls: 0, initial: cx("假") }),
|
draw: stateCard({ names: ["open", "setOpen"], calls: [0, 0], initial: cx("假") }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 2,
|
from: 2,
|
||||||
to: 2,
|
to: 2,
|
||||||
draw: stateCard({ names: ["items", "setItems"], calls: 0, type: [link("Item", C.type), seg("[]", C.fg)], initial: [seg("首次渲染时调用 ", C.fg)].concat(cx("loadItems()")) }),
|
draw: stateCard({ names: ["items", "setItems"], calls: [0, 0], type: [link("Item", C.type), seg("[]", C.fg)], initial: [seg("首次渲染时调用 ", C.fg)].concat(cx("loadItems()")) }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: 3,
|
from: 3,
|
||||||
to: 3,
|
to: 3,
|
||||||
draw: stateCard({ names: ["count"], initial: cx("0") }),
|
draw: stateCard({ names: ["count"], calls: [0], initial: cx("0") }),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user