@morlay/dsh-client-ui-primitives 0.0.6 → 0.0.8
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 +31 -6
- package/dist/client.d.mts +186 -11175
- package/dist/client.mjs +3730 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +23 -34
- package/src/client/controls/Bubble.module.css +17 -0
- package/src/client/controls/Bubble.tsx +17 -0
- package/src/client/controls/Button.module.css +10 -0
- package/src/client/controls/Button.tsx +29 -0
- package/src/client/controls/DisclosureRow.module.css +22 -0
- package/src/client/controls/DisclosureRow.tsx +20 -0
- package/src/client/controls/IconButton.module.css +50 -0
- package/src/client/controls/IconButton.tsx +45 -0
- package/src/client/controls/ModelRouteList.module.css +100 -0
- package/src/client/controls/ModelRouteList.tsx +118 -0
- package/src/client/controls/MultilineField.module.css +26 -0
- package/src/client/controls/MultilineField.tsx +19 -0
- package/src/client/controls/SearchInput.module.css +11 -0
- package/src/client/controls/SearchInput.tsx +22 -0
- package/src/client/controls/SearchSelect.module.css +34 -0
- package/src/client/controls/SearchSelect.tsx +138 -0
- package/src/client/controls/SeatButton.module.css +53 -0
- package/src/client/controls/SeatButton.tsx +40 -0
- package/src/client/controls/Spinner.module.css +18 -0
- package/src/client/controls/Spinner.tsx +14 -0
- package/src/client/controls/Tabs.module.css +48 -0
- package/src/client/controls/Tabs.tsx +57 -0
- package/src/client/controls/TagInput.module.css +105 -0
- package/src/client/controls/TagInput.tsx +152 -0
- package/src/client/controls/TimeLabel.module.css +16 -0
- package/src/client/controls/TimeLabel.tsx +15 -0
- package/src/client/controls/VisuallyHidden.module.css +9 -0
- package/src/client/controls/VisuallyHidden.tsx +16 -0
- package/src/client/controls/classes.ts +7 -0
- package/src/client/controls/index.ts +30 -0
- package/src/client/controls/tags.ts +24 -0
- package/src/client/index.ts +17 -9
- package/src/client/layout/Row.tsx +86 -0
- package/src/client/layout/Stack.tsx +79 -0
- package/src/client/layout/Text.tsx +54 -0
- package/src/client/layout/index.ts +16 -0
- package/src/client/layout/layout.module.css +217 -0
- package/src/client/layout/scales.ts +91 -0
- package/src/client/schema-form/Editor.tsx +61 -70
- package/src/client/schema-form/SchemaForm.tsx +4 -4
- package/src/client/schema-form/TruncatedTooltip.tsx +60 -0
- package/src/client/schema-form/controller.ts +5 -2
- package/src/client/schema-form/geometry.ts +3 -0
- package/src/client/schema-form/index.ts +11 -0
- package/src/client/schema-form/schema-form.module.css +335 -0
- package/src/client/schema-form/testing/resize-observer-stub.ts +63 -0
- package/src/client/schema-form/value.tsx +18 -15
- package/src/client/settings-form/SettingsFieldRow.module.css +93 -0
- package/src/client/settings-form/SettingsFieldRow.tsx +103 -0
- package/src/client/settings-form/index.ts +5 -18
- package/src/css-modules.d.ts +6 -0
- package/dist/client.cjs +0 -17075
- package/dist/client.d.cts +0 -11656
- package/dist/index.d.cts +0 -5
- package/dist/reference-BX54bj6_.d.cts +0 -30
- package/src/client/schema-form/styles.ts +0 -298
- package/src/client/settings-form/SettingsForm.styles.ts +0 -58
- package/src/client/settings-form/SettingsForm.tsx +0 -68
- package/src/client/settings-form/fields.styles.ts +0 -158
- package/src/client/settings-form/fields.tsx +0 -184
- package/src/client/settings-form/form-model.ts +0 -339
- package/src/client/styling/css.ts +0 -27
- package/src/client/styling/focus.ts +0 -9
- package/src/client/styling/index.ts +0 -6
- package/src/client/styling/styled.tsx +0 -54
- package/src/client/styling/styling.ts +0 -137
- package/src/client/styling/token.ts +0 -275
- package/src/client/styling/toolkit.ts +0 -37
- package/src/client/theme.generated.ts +0 -833
- package/src/client/theme.ts +0 -5
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// 与设置页其余部分同一套视觉与键盘行为。
|
|
6
6
|
//
|
|
7
7
|
// 字段槽仍然生效:命中时该字段**值**的位置换成注册方的组件,键名、注释、行号与行为按钮仍由这里画。
|
|
8
|
+
//
|
|
9
|
+
// 被 `ellipsis` 截断的文本(注释行、值)由官方 `Tooltip` 承载全文——**只有真的截断了才挂**(`TruncatedTooltip`)。
|
|
8
10
|
|
|
9
11
|
import { useMemo, useState, type ReactNode } from "react";
|
|
10
12
|
import {
|
|
@@ -29,27 +31,12 @@ import {
|
|
|
29
31
|
} from "./lines.ts";
|
|
30
32
|
import { parseFor } from "./fields.tsx";
|
|
31
33
|
import type { ResolveText } from "./labels.ts";
|
|
34
|
+
import { TruncatedTooltip } from "./TruncatedTooltip.tsx";
|
|
32
35
|
import { isMultiline, valueText } from "./value.tsx";
|
|
33
36
|
import type { SchemaFieldOwnerProps, SchemaFormTranslate } from "./slot-contract.ts";
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
CompactTextField,
|
|
38
|
-
EditorRoot,
|
|
39
|
-
HoverActions,
|
|
40
|
-
ICON_SIZE,
|
|
41
|
-
LineBody,
|
|
42
|
-
LineComment,
|
|
43
|
-
LineFold,
|
|
44
|
-
LineFoldSpacer,
|
|
45
|
-
LineInvalid,
|
|
46
|
-
LineKey,
|
|
47
|
-
LineNumber,
|
|
48
|
-
LineRow,
|
|
49
|
-
LineToken,
|
|
50
|
-
LineValue,
|
|
51
|
-
ValueTrigger,
|
|
52
|
-
} from "./styles.ts";
|
|
37
|
+
import { classes } from "../controls/classes.ts";
|
|
38
|
+
import { ICON_SIZE } from "./geometry.ts";
|
|
39
|
+
import css from "./schema-form.module.css";
|
|
53
40
|
|
|
54
41
|
// 编辑器要的一切。
|
|
55
42
|
export interface EditorProps {
|
|
@@ -81,7 +68,7 @@ export function Editor(props: EditorProps): ReactNode {
|
|
|
81
68
|
);
|
|
82
69
|
const disabled = !state.writable || !state.available;
|
|
83
70
|
return (
|
|
84
|
-
<
|
|
71
|
+
<div className={css.editorRoot} data-editor="schema-form">
|
|
85
72
|
{lines.map((line, index) => (
|
|
86
73
|
<Row
|
|
87
74
|
key={`${line.kind}:${fieldKey(line.path)}:${String(index)}`}
|
|
@@ -120,7 +107,7 @@ export function Editor(props: EditorProps): ReactNode {
|
|
|
120
107
|
renderField={renderField}
|
|
121
108
|
/>
|
|
122
109
|
))}
|
|
123
|
-
</
|
|
110
|
+
</div>
|
|
124
111
|
);
|
|
125
112
|
}
|
|
126
113
|
|
|
@@ -201,17 +188,20 @@ function Row({
|
|
|
201
188
|
const body =
|
|
202
189
|
line.kind === "comment" ? (
|
|
203
190
|
<>
|
|
204
|
-
<
|
|
205
|
-
{/* 这一行有问题时注释位装的就是那条消息:红字,别整行铺红。
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
191
|
+
<span className={css.lineFoldSpacer} data-role="fold" />
|
|
192
|
+
{/* 这一行有问题时注释位装的就是那条消息:红字,别整行铺红。
|
|
193
|
+
注释被 `ellipsis` 截断时全文交给官方 `Tooltip`——**只有真的截断才挂**,放得下就原样画。 */}
|
|
194
|
+
<TruncatedTooltip label={line.text}>
|
|
195
|
+
{line.invalid ? (
|
|
196
|
+
<span className={css.lineInvalid} data-role="comment">{`// ${line.text}`}</span>
|
|
197
|
+
) : (
|
|
198
|
+
<span className={css.lineComment} data-role="comment">{`// ${line.text}`}</span>
|
|
199
|
+
)}
|
|
200
|
+
</TruncatedTooltip>
|
|
211
201
|
</>
|
|
212
202
|
) : line.kind === "open" ? (
|
|
213
203
|
<>
|
|
214
|
-
<
|
|
204
|
+
<button className={css.lineFold}
|
|
215
205
|
data-role="fold"
|
|
216
206
|
type="button"
|
|
217
207
|
aria-expanded={!line.collapsed}
|
|
@@ -225,11 +215,11 @@ function Row({
|
|
|
225
215
|
) : (
|
|
226
216
|
<IconChevronDownOutlineRegular size={ICON_SIZE} />
|
|
227
217
|
)}
|
|
228
|
-
</
|
|
218
|
+
</button>
|
|
229
219
|
<LinePrefix line={line} face={face} t={t} disabled={disabled} />
|
|
230
|
-
<
|
|
220
|
+
<span className={css.lineToken}>
|
|
231
221
|
{line.shape === "object" ? (line.collapsed ? "{…}" : "{") : line.collapsed ? "[…]" : "["}
|
|
232
|
-
</
|
|
222
|
+
</span>
|
|
233
223
|
<VariantSelect
|
|
234
224
|
control={line.variants}
|
|
235
225
|
path={line.path}
|
|
@@ -240,8 +230,8 @@ function Row({
|
|
|
240
230
|
</>
|
|
241
231
|
) : line.kind === "close" ? (
|
|
242
232
|
<>
|
|
243
|
-
<
|
|
244
|
-
<
|
|
233
|
+
<span className={css.lineFoldSpacer} data-role="fold" />
|
|
234
|
+
<span className={css.lineToken}>{line.shape === "object" ? "}" : "]"}</span>
|
|
245
235
|
<AddLine
|
|
246
236
|
line={line}
|
|
247
237
|
state={state}
|
|
@@ -268,7 +258,7 @@ function Row({
|
|
|
268
258
|
/>
|
|
269
259
|
);
|
|
270
260
|
return (
|
|
271
|
-
<
|
|
261
|
+
<div className={css.lineRow}
|
|
272
262
|
data-line={line.kind}
|
|
273
263
|
data-field-path={line.path.join(".")}
|
|
274
264
|
data-selected={selected === key ? "true" : undefined}
|
|
@@ -282,18 +272,18 @@ function Row({
|
|
|
282
272
|
: undefined
|
|
283
273
|
}
|
|
284
274
|
>
|
|
285
|
-
<
|
|
275
|
+
<span className={css.lineNumber}
|
|
286
276
|
data-role="number"
|
|
287
277
|
onClick={() => {
|
|
288
278
|
onSelect(line.path);
|
|
289
279
|
}}
|
|
290
280
|
>
|
|
291
281
|
{n}
|
|
292
|
-
</
|
|
293
|
-
<
|
|
282
|
+
</span>
|
|
283
|
+
<div className={css.lineBody} data-role="body" style={indent}>
|
|
294
284
|
{body}
|
|
295
|
-
</
|
|
296
|
-
</
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
297
287
|
);
|
|
298
288
|
}
|
|
299
289
|
|
|
@@ -371,19 +361,19 @@ function FieldLine({
|
|
|
371
361
|
const multiline = isMultiline(line.field.value) || /[\r\n]/.test(text);
|
|
372
362
|
return (
|
|
373
363
|
<>
|
|
374
|
-
<
|
|
375
|
-
<
|
|
364
|
+
<span className={css.lineFoldSpacer} data-role="fold" />
|
|
365
|
+
<span className={css.lineKey}
|
|
376
366
|
data-role="key"
|
|
377
367
|
data-index={member?.index === undefined ? undefined : "true"}
|
|
378
368
|
title={member?.key ?? line.node.key}
|
|
379
369
|
>
|
|
380
370
|
{member?.key ?? line.node.key}
|
|
381
|
-
</
|
|
382
|
-
<
|
|
371
|
+
</span>
|
|
372
|
+
<span className={css.lineToken}>{member?.index === undefined ? ": " : " "}</span>
|
|
383
373
|
{isEditing ? (
|
|
384
374
|
// 带换行的值给多行输入(Enter 换行、Esc 撤销),其余是一行的官方输入框。
|
|
385
375
|
multiline ? (
|
|
386
|
-
<
|
|
376
|
+
<span className={css.compactTextField}>
|
|
387
377
|
<textarea
|
|
388
378
|
autoFocus={!missing}
|
|
389
379
|
rows={1}
|
|
@@ -399,9 +389,9 @@ function FieldLine({
|
|
|
399
389
|
}
|
|
400
390
|
}}
|
|
401
391
|
/>
|
|
402
|
-
</
|
|
392
|
+
</span>
|
|
403
393
|
) : (
|
|
404
|
-
<
|
|
394
|
+
<span className={css.compactInput}>
|
|
405
395
|
<Input
|
|
406
396
|
autoFocus={!missing}
|
|
407
397
|
type="text"
|
|
@@ -422,12 +412,13 @@ function FieldLine({
|
|
|
422
412
|
}
|
|
423
413
|
}}
|
|
424
414
|
/>
|
|
425
|
-
</
|
|
415
|
+
</span>
|
|
426
416
|
)
|
|
427
417
|
) : null}
|
|
428
418
|
{/* 编辑态只留输入框:原值不再在它旁边画一遍。 */}
|
|
429
419
|
{isEditing || line.variantsStandIn ? null : (
|
|
430
|
-
<span
|
|
420
|
+
<span className={css.valueSlot}
|
|
421
|
+
data-role="value"
|
|
431
422
|
onClick={() => {
|
|
432
423
|
if (editable) onEdit(key);
|
|
433
424
|
}}
|
|
@@ -444,7 +435,7 @@ function FieldLine({
|
|
|
444
435
|
/>
|
|
445
436
|
{isEditing ? (
|
|
446
437
|
// 编辑态:确认收起这一格的编辑、取消把值退回去——都在输入框旁边,不用记快捷键。
|
|
447
|
-
<
|
|
438
|
+
<span className={classes(css.lineActions, css.hoverActions)} data-role="actions" data-editing="true">
|
|
448
439
|
<button
|
|
449
440
|
type="button"
|
|
450
441
|
aria-label={t("confirmEdit")}
|
|
@@ -468,9 +459,9 @@ function FieldLine({
|
|
|
468
459
|
>
|
|
469
460
|
<IconCloseOutlineRegular size={ICON_SIZE} />
|
|
470
461
|
</button>
|
|
471
|
-
</
|
|
462
|
+
</span>
|
|
472
463
|
) : null}
|
|
473
|
-
<
|
|
464
|
+
<span className={classes(css.lineActions, css.hoverActions)} data-role="actions">
|
|
474
465
|
{line.field.staged && !disabled ? (
|
|
475
466
|
<button
|
|
476
467
|
type="button"
|
|
@@ -510,7 +501,7 @@ function FieldLine({
|
|
|
510
501
|
{member === undefined ? null : (
|
|
511
502
|
<RemoveLineButton member={member} face={face} t={t} disabled={disabled} />
|
|
512
503
|
)}
|
|
513
|
-
</
|
|
504
|
+
</span>
|
|
514
505
|
</>
|
|
515
506
|
);
|
|
516
507
|
}
|
|
@@ -558,7 +549,7 @@ function VariantSelect({
|
|
|
558
549
|
setOpen(false);
|
|
559
550
|
}}
|
|
560
551
|
anchor={
|
|
561
|
-
<
|
|
552
|
+
<button className={css.valueTrigger}
|
|
562
553
|
type="button"
|
|
563
554
|
data-role="variant"
|
|
564
555
|
disabled={disabled}
|
|
@@ -567,9 +558,9 @@ function VariantSelect({
|
|
|
567
558
|
setOpen(true);
|
|
568
559
|
}}
|
|
569
560
|
>
|
|
570
|
-
<
|
|
561
|
+
<span className={css.lineValue} data-tone="empty">{current?.label ?? ""}</span>
|
|
571
562
|
<IconChevronDownOutlineRegular size={ICON_SIZE} />
|
|
572
|
-
</
|
|
563
|
+
</button>
|
|
573
564
|
}
|
|
574
565
|
/>
|
|
575
566
|
);
|
|
@@ -594,15 +585,15 @@ function LinePrefix({
|
|
|
594
585
|
const indexed = member !== undefined && member.index !== undefined;
|
|
595
586
|
return (
|
|
596
587
|
<>
|
|
597
|
-
<
|
|
588
|
+
<span className={css.lineKey} data-role="key" data-index={indexed ? "true" : undefined} title={key}>
|
|
598
589
|
{key}
|
|
599
|
-
</
|
|
600
|
-
<
|
|
590
|
+
</span>
|
|
591
|
+
<span className={css.lineToken}>{indexed ? " " : ": "}</span>
|
|
601
592
|
{/* 值本身是容器(对象/数组)的成员:它也要能移除,否则这一项删不掉。 */}
|
|
602
593
|
{member === undefined ? null : (
|
|
603
|
-
<
|
|
594
|
+
<span className={classes(css.lineActions, css.hoverActions)} data-role="actions">
|
|
604
595
|
<RemoveLineButton member={member} face={face} t={t} disabled={disabled} />
|
|
605
|
-
</
|
|
596
|
+
</span>
|
|
606
597
|
)}
|
|
607
598
|
</>
|
|
608
599
|
);
|
|
@@ -705,7 +696,7 @@ function AddLine({
|
|
|
705
696
|
setOpen(false);
|
|
706
697
|
};
|
|
707
698
|
return (
|
|
708
|
-
<
|
|
699
|
+
<span className={css.addWrap} data-add={spec.kind} data-invalid={invalid ? "true" : undefined}>
|
|
709
700
|
<Menu
|
|
710
701
|
open={open && spec.kind !== "item" && matched.length > 0}
|
|
711
702
|
dense
|
|
@@ -720,7 +711,7 @@ function AddLine({
|
|
|
720
711
|
setOpen(false);
|
|
721
712
|
}}
|
|
722
713
|
anchor={
|
|
723
|
-
<
|
|
714
|
+
<span className={css.compactInput}>
|
|
724
715
|
<Input
|
|
725
716
|
value={text}
|
|
726
717
|
placeholder={label}
|
|
@@ -749,16 +740,16 @@ function AddLine({
|
|
|
749
740
|
}
|
|
750
741
|
}}
|
|
751
742
|
/>
|
|
752
|
-
</
|
|
743
|
+
</span>
|
|
753
744
|
}
|
|
754
745
|
/>
|
|
755
|
-
{invalid ? <
|
|
756
|
-
{/* 候选的说明:选中之前先看清"要加的是什么"
|
|
746
|
+
{invalid ? <span className={css.lineInvalid}>{t("unknownProperty")}</span> : null}
|
|
747
|
+
{/* 候选的说明:选中之前先看清"要加的是什么"(长了同样由 `Tooltip` 给全文,只有截断时才挂)。 */}
|
|
757
748
|
{!invalid && matched.length === 1 && matched[0]?.description !== undefined ? (
|
|
758
|
-
<
|
|
759
|
-
{resolveText(matched[0].description)}
|
|
760
|
-
</
|
|
749
|
+
<TruncatedTooltip label={resolveText(matched[0].description)}>
|
|
750
|
+
<span className={css.lineComment}>{resolveText(matched[0].description)}</span>
|
|
751
|
+
</TruncatedTooltip>
|
|
761
752
|
) : null}
|
|
762
|
-
</
|
|
753
|
+
</span>
|
|
763
754
|
);
|
|
764
755
|
}
|
|
@@ -8,7 +8,7 @@ import { SettingsForm, type SettingsFormLabels } from "@deepseek-ai/dsh-client-u
|
|
|
8
8
|
import { Editor } from "./Editor.tsx";
|
|
9
9
|
import { parseFor } from "./fields.tsx";
|
|
10
10
|
import type { SchemaFieldOwnerProps, SchemaFormComponentProps } from "./slot-contract.ts";
|
|
11
|
-
import
|
|
11
|
+
import css from "./schema-form.module.css";
|
|
12
12
|
import { SchemaFieldDefault } from "./value.tsx";
|
|
13
13
|
|
|
14
14
|
// 渲染一行的配置页。
|
|
@@ -26,7 +26,7 @@ export function SchemaForm(props: SchemaFormComponentProps): ReactNode {
|
|
|
26
26
|
save: t("save"),
|
|
27
27
|
saving: t("saving"),
|
|
28
28
|
};
|
|
29
|
-
if (!state.configured) return <
|
|
29
|
+
if (!state.configured) return <p className={css.hint} role="status">{t("noSchema")}</p>;
|
|
30
30
|
return (
|
|
31
31
|
<SettingsForm
|
|
32
32
|
labels={labels}
|
|
@@ -52,9 +52,9 @@ export function SchemaForm(props: SchemaFormComponentProps): ReactNode {
|
|
|
52
52
|
/>
|
|
53
53
|
{/* 本地整段校验挡下的保存要说出来:否则点保存"没有反应"看起来像按钮坏了。 */}
|
|
54
54
|
{state.violation === undefined ? null : (
|
|
55
|
-
<
|
|
55
|
+
<p className={css.hint} role="alert" data-violation="true">
|
|
56
56
|
{t("notSaved", { message: state.violation.message })}
|
|
57
|
-
</
|
|
57
|
+
</p>
|
|
58
58
|
)}
|
|
59
59
|
</SettingsForm>
|
|
60
60
|
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// 行内文本的全文浮层:**只有确实被截断**时才给官方 `Tooltip`,放得下就一个浮层也不挂。
|
|
2
|
+
//
|
|
3
|
+
// 判据是元素自己的几何——`scrollWidth > clientWidth` 就是「这一格正被 `ellipsis` 截断」,不用字符数之类的估计。
|
|
4
|
+
// 宽度变化(窗口、面板、字段槽)由 `ResizeObserver` 盯着这个元素重测;盒子没变、只是换了内容(值改了、注释换
|
|
5
|
+
// 了)不会被它通知,所以每次提交再重量一次。
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
cloneElement,
|
|
9
|
+
useCallback,
|
|
10
|
+
useLayoutEffect,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
type ReactElement,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
type Ref,
|
|
16
|
+
} from "react";
|
|
17
|
+
import { Tooltip } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
18
|
+
|
|
19
|
+
export function TruncatedTooltip({
|
|
20
|
+
label,
|
|
21
|
+
children,
|
|
22
|
+
}: {
|
|
23
|
+
label: string;
|
|
24
|
+
children: ReactElement<{ ref?: Ref<HTMLElement> }>;
|
|
25
|
+
}): ReactNode {
|
|
26
|
+
const node = useRef<HTMLElement | null>(null);
|
|
27
|
+
const watch = useRef<ResizeObserver | null>(null);
|
|
28
|
+
const [truncated, setTruncated] = useState(false);
|
|
29
|
+
|
|
30
|
+
const measure = useCallback(() => {
|
|
31
|
+
const el = node.current;
|
|
32
|
+
if (el === null) return;
|
|
33
|
+
setTruncated(el.scrollWidth > el.clientWidth);
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
// ref 一直挂在子元素上(没截断时也挂着):挂上就盯着它的宽度,卸载时退订。
|
|
37
|
+
const anchor = useCallback(
|
|
38
|
+
(el: HTMLElement | null) => {
|
|
39
|
+
watch.current?.disconnect();
|
|
40
|
+
watch.current = null;
|
|
41
|
+
node.current = el;
|
|
42
|
+
if (el === null) return;
|
|
43
|
+
const observer = new ResizeObserver(measure);
|
|
44
|
+
observer.observe(el);
|
|
45
|
+
watch.current = observer;
|
|
46
|
+
},
|
|
47
|
+
[measure],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// 每次提交都重量一次:`ResizeObserver` 只对盒子尺寸发通知,换了文本的同尺寸盒子要靠这里。
|
|
51
|
+
useLayoutEffect(measure);
|
|
52
|
+
|
|
53
|
+
// 没截断时走 `Tooltip` 自己的 `disabled`:气泡不渲染、hover 也不弹,锚元素同时不重挂
|
|
54
|
+
//(换成「有截断才包 `Tooltip`」会在截断状态翻转时把锚换掉,白丢一次 DOM)。
|
|
55
|
+
return (
|
|
56
|
+
<Tooltip label={label} side="bottom" portal disabled={!truncated}>
|
|
57
|
+
{cloneElement(children, { ref: anchor })}
|
|
58
|
+
</Tooltip>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -304,9 +304,12 @@ export class SchemaFormController {
|
|
|
304
304
|
return this.#ns;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
//
|
|
307
|
+
// 外部读数变了(提示面的候选、或 describe 里的 schema 本身):**重读一次 describe** 再投影。
|
|
308
|
+
//
|
|
309
|
+
// 只重投影是不够的:行页在 describe 变化时会重建控制器,而"这一行常驻、只订阅 describe"的消费方(bundle 配置页)
|
|
310
|
+
// 依赖这次重读——否则视图晚到、通知没落上时,字段树会停在"还没有"的那一次投影上。
|
|
308
311
|
refresh(): void {
|
|
309
|
-
this.#
|
|
312
|
+
this.#sync();
|
|
310
313
|
}
|
|
311
314
|
|
|
312
315
|
// 释放订阅。
|
|
@@ -74,7 +74,18 @@ export type { SuggestedKeys } from "./schema-node.ts";
|
|
|
74
74
|
type RowConfigEntryProps = PropsRuntime<"plugins.row.config"> & PropsRenderFactories;
|
|
75
75
|
|
|
76
76
|
// 装上本包:字典、Factory(字段槽的声明者)与按行自动注册。
|
|
77
|
+
//
|
|
78
|
+
// 调用方是**内联了本包的 client 行**(本包不再是装配行):inline 后每个消费行的产物里各带一份这份代码、
|
|
79
|
+
// 都会调到这里,所以先等依赖服务到齐(调用方不必自己排顺序),并保证同一运行时只装一次。
|
|
77
80
|
export function apply(ctx: Context): void {
|
|
81
|
+
ctx.inject([...inject], (scope) => {
|
|
82
|
+
// 提示面已在场 = 另一份副本装过了:字典 / 槽 / 行入口都不再重复注册。
|
|
83
|
+
if (scope.get("schemaFormHints") !== undefined) return;
|
|
84
|
+
install(scope);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function install(ctx: Context): void {
|
|
78
89
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "ui-schema-form: dictionaries");
|
|
79
90
|
// 提示面(dict 的候选键):同一次 apply 里提供,业务插件注册读数,本包的行投影读它。
|
|
80
91
|
const hints = new SchemaFormHints(ctx);
|