@godxjp/ui 31.14.0 → 31.16.0

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.
Files changed (36) hide show
  1. package/agent/START-HERE.md +5 -5
  2. package/agent/components/TextDiff.json +86 -0
  3. package/agent/components/Timeline.json +4 -3
  4. package/agent/components/Tree.json +22 -1
  5. package/agent/components-index.json +7 -2
  6. package/agent/components.json +112 -4
  7. package/agent/index.json +6 -6
  8. package/agent/llms.txt +6 -6
  9. package/agent/tokens.json +42 -0
  10. package/dist/components/data-display/index.d.ts +3 -1
  11. package/dist/components/data-display/index.js +4 -0
  12. package/dist/components/data-display/text-diff.d.ts +45 -0
  13. package/dist/components/data-display/text-diff.js +240 -0
  14. package/dist/components/data-display/timeline.d.ts +12 -2
  15. package/dist/components/data-display/timeline.js +6 -2
  16. package/dist/components/data-display/tree.d.ts +1 -1
  17. package/dist/components/data-display/tree.js +147 -0
  18. package/dist/contracts/measurement.json +1 -1
  19. package/dist/i18n/messages/en.json +18 -1
  20. package/dist/i18n/messages/ja.json +15 -1
  21. package/dist/i18n/messages/vi.json +15 -1
  22. package/dist/props/components/data-display.prop.d.ts +83 -1
  23. package/dist/props/registry.d.ts +88 -0
  24. package/dist/props/registry.js +109 -0
  25. package/dist/styles/data-display-layout.css +92 -1
  26. package/dist/styles/layers.json +6 -1
  27. package/dist/tokens/components/data-display.css +8 -0
  28. package/dist/tokens/components/tree.css +3 -0
  29. package/docs/FRAME-COVERAGE-REPORT.md +3 -2
  30. package/docs/data-display/text-diff.tsx +89 -0
  31. package/docs/data-display/timeline.tsx +28 -0
  32. package/docs/data-display/tree.tsx +82 -0
  33. package/docs/i18n/messages/en.json +41 -0
  34. package/docs/i18n/messages/ja.json +41 -0
  35. package/docs/i18n/messages/vi.json +41 -0
  36. package/package.json +2 -2
@@ -711,6 +711,29 @@ export type TreeNodeProp = TreeOptionProp & {
711
711
  * `checkedValues`/`defaultCheckedValues`/`onCheckedValuesChange`.
712
712
  * @see docs/roadmap/tree-components.md §3
713
713
  */
714
+ /**
715
+ * Where a drop lands relative to `node` (gh#1093): `-1` in the gap BEFORE it, `0` INSIDE it (the
716
+ * dragged node becomes its child), `1` in the gap AFTER it. antd's `onDrop` reports an absolute
717
+ * index and every antd demo subtracts the node's position back out to get exactly this number, so
718
+ * the relative form is what a consumer needs.
719
+ */
720
+ export type TreeDropPositionProp = -1 | 0 | 1;
721
+ /** `allowDrop` argument (antd `allowDrop({ dragNode, dropNode, dropPosition })`). */
722
+ export type TreeAllowDropInfoProp = {
723
+ dragNode: TreeNodeProp;
724
+ dropNode: TreeNodeProp;
725
+ dropPosition: TreeDropPositionProp;
726
+ };
727
+ /** `onDrop` argument (antd `onDrop({ dragNode, node, dropPosition, dropToGap })`). */
728
+ export type TreeDropInfoProp = {
729
+ /** The node being moved. */
730
+ dragNode: TreeNodeProp;
731
+ /** The node it was dropped on or beside. */
732
+ node: TreeNodeProp;
733
+ dropPosition: TreeDropPositionProp;
734
+ /** `true` when it lands in a gap (becomes a SIBLING of `node`), `false` when it lands inside. */
735
+ dropToGap: boolean;
736
+ };
714
737
  export type TreeProp = {
715
738
  /** The hierarchy (antd `treeData`). Use `fieldNames` to remap keys off an API response. */
716
739
  treeData: readonly TreeNodeProp[];
@@ -781,6 +804,21 @@ export type TreeProp = {
781
804
  * capability; see docs/DESIGN-AUTHORITY.md.
782
805
  */
783
806
  divided?: boolean;
807
+ /**
808
+ * Let the reader move nodes by dragging (antd `draggable`, gh#1093): drop on the upper or lower
809
+ * quarter of a row to land in the gap before / after it, on the middle to file it inside. The
810
+ * tree never mutates `treeData` — it reports the move through `onDrop` and the consumer persists
811
+ * it. Keyboard equivalent (WCAG 2.5.7): Alt+↑ / Alt+↓ move past the previous / next sibling,
812
+ * Alt+← outdents (after its parent), Alt+→ indents (into the previous sibling); each move is
813
+ * announced through a live region. A node is never dropped into itself or its own subtree.
814
+ */
815
+ draggable?: boolean;
816
+ /** Per-node drag gate (antd `draggable.nodeDraggable`). Disabled nodes never drag. */
817
+ allowDrag?: (node: TreeNodeProp) => boolean;
818
+ /** Per-target drop gate (antd `allowDrop`). Asked for pointer AND keyboard moves. */
819
+ allowDrop?: (info: TreeAllowDropInfoProp) => boolean;
820
+ /** A move the reader committed (antd `onDrop`). Update `treeData` here. */
821
+ onDrop?: (info: TreeDropInfoProp) => void;
784
822
  /** Draw each node's `icon` (antd `showIcon`). `variant="directory"` supplies folder/file glyphs. */
785
823
  showIcon?: boolean;
786
824
  /** `directory` is antd's `<DirectoryTree>`: folder/file glyphs and a full-row selected band. */
@@ -793,7 +831,8 @@ export type TreeProp = {
793
831
  id?: IdProp;
794
832
  /** Accessible name of the `role="tree"` container. Required unless `aria-labelledby` is given. */
795
833
  "aria-label"?: string;
796
- "aria-labelledby"?: string;
834
+ "aria-labelledby"?: string; /** Extra description ids; a `draggable` tree appends its own keyboard-move hint. */
835
+ "aria-describedby"?: string;
797
836
  };
798
837
  /**
799
838
  * @see ChatBubble — which side of the conversation the message sits on. LOGICAL, not physical:
@@ -1295,3 +1334,46 @@ export type ImagePreviewGroupProp = {
1295
1334
  preview?: boolean | ImagePreviewGroupConfigProp;
1296
1335
  children?: React.ReactNode;
1297
1336
  };
1337
+ /** @see diffText — one run of a diff: kept, taken out of `before`, or put into `after`. */
1338
+ export type TextDiffSegment = {
1339
+ kind: "same" | "removed" | "added";
1340
+ text: string;
1341
+ };
1342
+ /**
1343
+ * @see TextDiff — what changed between two versions of a text (gh#1096).
1344
+ *
1345
+ * antd ships no diff, so the shape follows GitHub/GitLab: `<del>`/`<ins>` with a spoken start/end
1346
+ * label (never colour alone), inline ("unified") or split, unchanged runs folded around the
1347
+ * changes. Text keeps its newlines (`pre-wrap`) and a long token breaks anywhere.
1348
+ */
1349
+ export type TextDiffProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children"> & {
1350
+ /** The earlier text — what a translation was written against, the saved version. */
1351
+ before: string;
1352
+ /** The current text. */
1353
+ after: string;
1354
+ /** `inline` (default) — one flow; `split` — two columns, before | after, hunks aligned. */
1355
+ mode?: "inline" | "split";
1356
+ /**
1357
+ * The unit a change is measured in. `auto` (default) is words for alphabetic text and one
1358
+ * character per token for Han / Hiragana / Katakana / Hangul runs, which have no spaces to split
1359
+ * on. `word` is `Intl.Segmenter` word segmentation in `lang` (dictionary words for Japanese),
1360
+ * `char` is one grapheme cluster per token, `line` compares whole lines.
1361
+ */
1362
+ granularity?: "auto" | "word" | "char" | "line";
1363
+ /** Fold long unchanged runs of lines behind a "show N unchanged lines" button. Default `true`. */
1364
+ collapseUnchanged?: boolean;
1365
+ /** Unchanged lines kept visible next to each change when folding. Default `2`. */
1366
+ contextLines?: number;
1367
+ /**
1368
+ * The LCS cost cap (tokens before × tokens after). Past it the changed middle is shown as one
1369
+ * removal and one addition instead of freezing the tab. Default `4_000_000`.
1370
+ */
1371
+ maxCells?: number;
1372
+ /** Heading of the before column in `split` mode. Default: the localized "Before". */
1373
+ beforeLabel?: React.ReactNode;
1374
+ /** Heading of the after column in `split` mode. Default: the localized "After". */
1375
+ afterLabel?: React.ReactNode;
1376
+ /** BCP-47 language of the text — set on the element and used for `Intl.Segmenter`. */
1377
+ lang?: string;
1378
+ className?: ClassNameProp;
1379
+ };
@@ -2881,6 +2881,51 @@ export declare const COMPONENT_PROP_REGISTRY: {
2881
2881
  readonly reason: "The sample's accessible NAME, which is what lets a read-only colour be shown with no visible label. Absent, the mark is aria-hidden (Legend's rule) — so colour is never the sole carrier either way.";
2882
2882
  }];
2883
2883
  };
2884
+ readonly TextDiffProp: {
2885
+ readonly group: "data-display";
2886
+ readonly file: "components/data-display.prop.ts";
2887
+ readonly vocabulary: readonly ["ClassNameProp", {
2888
+ readonly field: "before";
2889
+ readonly local: true;
2890
+ readonly reason: "The earlier version of the text being compared — DATA, the left side of a diff (gh#1096).";
2891
+ }, {
2892
+ readonly field: "after";
2893
+ readonly local: true;
2894
+ readonly reason: "The current version of the text being compared — DATA, the right side of a diff (gh#1096).";
2895
+ }, {
2896
+ readonly field: "mode";
2897
+ readonly local: true;
2898
+ readonly reason: "inline | split — GitHub's unified vs side-by-side diff layout; not a size or tone axis.";
2899
+ }, {
2900
+ readonly field: "granularity";
2901
+ readonly local: true;
2902
+ readonly reason: "auto | word | char | line — the token unit, the same word Intl.Segmenter uses for it.";
2903
+ }, {
2904
+ readonly field: "collapseUnchanged";
2905
+ readonly local: true;
2906
+ readonly reason: "Positive boolean: fold long unchanged runs of lines behind a reveal button.";
2907
+ }, {
2908
+ readonly field: "contextLines";
2909
+ readonly local: true;
2910
+ readonly reason: "Unchanged lines kept beside each change when folding — git's -U<n> context.";
2911
+ }, {
2912
+ readonly field: "maxCells";
2913
+ readonly local: true;
2914
+ readonly reason: "Cost cap of the LCS table; past it the changed middle is one removal plus one addition.";
2915
+ }, {
2916
+ readonly field: "beforeLabel";
2917
+ readonly local: true;
2918
+ readonly reason: "Heading of the before column in split mode; defaults to the localized word.";
2919
+ }, {
2920
+ readonly field: "afterLabel";
2921
+ readonly local: true;
2922
+ readonly reason: "Heading of the after column in split mode; defaults to the localized word.";
2923
+ }, {
2924
+ readonly field: "lang";
2925
+ readonly local: true;
2926
+ readonly reason: "The HTML lang attribute, also the Intl.Segmenter locale for word/char segmentation.";
2927
+ }];
2928
+ };
2884
2929
  readonly FeatureStateProp: {
2885
2930
  readonly group: "data-display";
2886
2931
  readonly file: "components/data-display.prop.ts";
@@ -3165,6 +3210,49 @@ export declare const COMPONENT_PROP_REGISTRY: {
3165
3210
  readonly reason: "The node's own child nodes — the hierarchy itself, not a slot.";
3166
3211
  }];
3167
3212
  };
3213
+ readonly TreeDropPositionProp: {
3214
+ readonly group: "data-display";
3215
+ readonly file: "components/data-display.prop.ts";
3216
+ readonly vocabulary: readonly [];
3217
+ };
3218
+ readonly TreeAllowDropInfoProp: {
3219
+ readonly group: "data-display";
3220
+ readonly file: "components/data-display.prop.ts";
3221
+ readonly vocabulary: readonly [{
3222
+ readonly field: "dragNode";
3223
+ readonly local: true;
3224
+ readonly reason: "antd `allowDrop` argument: the node being moved (gh#1093).";
3225
+ }, {
3226
+ readonly field: "dropNode";
3227
+ readonly local: true;
3228
+ readonly reason: "antd `allowDrop` argument: the node it would land on or beside.";
3229
+ }, {
3230
+ readonly field: "dropPosition";
3231
+ readonly local: true;
3232
+ readonly reason: "antd `allowDrop` argument: -1 before, 0 inside, 1 after.";
3233
+ }];
3234
+ };
3235
+ readonly TreeDropInfoProp: {
3236
+ readonly group: "data-display";
3237
+ readonly file: "components/data-display.prop.ts";
3238
+ readonly vocabulary: readonly [{
3239
+ readonly field: "dragNode";
3240
+ readonly local: true;
3241
+ readonly reason: "antd `onDrop` info: the node being moved (gh#1093).";
3242
+ }, {
3243
+ readonly field: "node";
3244
+ readonly local: true;
3245
+ readonly reason: "antd `onDrop` info: the node it landed on or beside.";
3246
+ }, {
3247
+ readonly field: "dropPosition";
3248
+ readonly local: true;
3249
+ readonly reason: "antd `onDrop` info, relative: -1 before, 0 inside, 1 after.";
3250
+ }, {
3251
+ readonly field: "dropToGap";
3252
+ readonly local: true;
3253
+ readonly reason: "antd `onDrop` info: true when it lands as a sibling, false inside.";
3254
+ }];
3255
+ };
3168
3256
  readonly TreeProp: {
3169
3257
  readonly group: "data-display";
3170
3258
  readonly file: "components/data-display.prop.ts";
@@ -3398,6 +3398,63 @@ const COMPONENT_PROP_REGISTRY = {
3398
3398
  }
3399
3399
  ]
3400
3400
  },
3401
+ TextDiffProp: {
3402
+ group: "data-display",
3403
+ file: "components/data-display.prop.ts",
3404
+ vocabulary: [
3405
+ "ClassNameProp",
3406
+ {
3407
+ field: "before",
3408
+ local: true,
3409
+ reason: "The earlier version of the text being compared \u2014 DATA, the left side of a diff (gh#1096)."
3410
+ },
3411
+ {
3412
+ field: "after",
3413
+ local: true,
3414
+ reason: "The current version of the text being compared \u2014 DATA, the right side of a diff (gh#1096)."
3415
+ },
3416
+ {
3417
+ field: "mode",
3418
+ local: true,
3419
+ reason: "inline | split \u2014 GitHub's unified vs side-by-side diff layout; not a size or tone axis."
3420
+ },
3421
+ {
3422
+ field: "granularity",
3423
+ local: true,
3424
+ reason: "auto | word | char | line \u2014 the token unit, the same word Intl.Segmenter uses for it."
3425
+ },
3426
+ {
3427
+ field: "collapseUnchanged",
3428
+ local: true,
3429
+ reason: "Positive boolean: fold long unchanged runs of lines behind a reveal button."
3430
+ },
3431
+ {
3432
+ field: "contextLines",
3433
+ local: true,
3434
+ reason: "Unchanged lines kept beside each change when folding \u2014 git's -U<n> context."
3435
+ },
3436
+ {
3437
+ field: "maxCells",
3438
+ local: true,
3439
+ reason: "Cost cap of the LCS table; past it the changed middle is one removal plus one addition."
3440
+ },
3441
+ {
3442
+ field: "beforeLabel",
3443
+ local: true,
3444
+ reason: "Heading of the before column in split mode; defaults to the localized word."
3445
+ },
3446
+ {
3447
+ field: "afterLabel",
3448
+ local: true,
3449
+ reason: "Heading of the after column in split mode; defaults to the localized word."
3450
+ },
3451
+ {
3452
+ field: "lang",
3453
+ local: true,
3454
+ reason: "The HTML lang attribute, also the Intl.Segmenter locale for word/char segmentation."
3455
+ }
3456
+ ]
3457
+ },
3401
3458
  FeatureStateProp: {
3402
3459
  group: "data-display",
3403
3460
  file: "components/data-display.prop.ts",
@@ -3751,6 +3808,58 @@ const COMPONENT_PROP_REGISTRY = {
3751
3808
  }
3752
3809
  ]
3753
3810
  },
3811
+ TreeDropPositionProp: {
3812
+ group: "data-display",
3813
+ file: "components/data-display.prop.ts",
3814
+ vocabulary: []
3815
+ },
3816
+ TreeAllowDropInfoProp: {
3817
+ group: "data-display",
3818
+ file: "components/data-display.prop.ts",
3819
+ vocabulary: [
3820
+ {
3821
+ field: "dragNode",
3822
+ local: true,
3823
+ reason: "antd `allowDrop` argument: the node being moved (gh#1093)."
3824
+ },
3825
+ {
3826
+ field: "dropNode",
3827
+ local: true,
3828
+ reason: "antd `allowDrop` argument: the node it would land on or beside."
3829
+ },
3830
+ {
3831
+ field: "dropPosition",
3832
+ local: true,
3833
+ reason: "antd `allowDrop` argument: -1 before, 0 inside, 1 after."
3834
+ }
3835
+ ]
3836
+ },
3837
+ TreeDropInfoProp: {
3838
+ group: "data-display",
3839
+ file: "components/data-display.prop.ts",
3840
+ vocabulary: [
3841
+ {
3842
+ field: "dragNode",
3843
+ local: true,
3844
+ reason: "antd `onDrop` info: the node being moved (gh#1093)."
3845
+ },
3846
+ {
3847
+ field: "node",
3848
+ local: true,
3849
+ reason: "antd `onDrop` info: the node it landed on or beside."
3850
+ },
3851
+ {
3852
+ field: "dropPosition",
3853
+ local: true,
3854
+ reason: "antd `onDrop` info, relative: -1 before, 0 inside, 1 after."
3855
+ },
3856
+ {
3857
+ field: "dropToGap",
3858
+ local: true,
3859
+ reason: "antd `onDrop` info: true when it lands as a sibling, false inside."
3860
+ }
3861
+ ]
3862
+ },
3754
3863
  TreeProp: {
3755
3864
  group: "data-display",
3756
3865
  file: "components/data-display.prop.ts",
@@ -467,6 +467,58 @@
467
467
  block-size: var(--thumbnail-block-size-lg);
468
468
  }
469
469
 
470
+ .ui-text-diff {
471
+ min-inline-size: 0;
472
+ }
473
+
474
+ :is(.ui-text-diff-body, .ui-text-diff-cell) {
475
+ white-space: pre-wrap;
476
+ overflow-wrap: anywhere;
477
+ min-inline-size: 0;
478
+ }
479
+
480
+ .ui-text-diff-removed {
481
+ background: var(
482
+ --surface-destructive,
483
+ hsl(var(--destructive) / var(--text-diff-removed-background-alpha))
484
+ );
485
+ color: inherit;
486
+ text-decoration-line: line-through;
487
+ text-decoration-thickness: var(--text-diff-mark-line-width);
488
+ }
489
+
490
+ .ui-text-diff-added {
491
+ background: var(
492
+ --surface-success,
493
+ hsl(var(--success) / var(--text-diff-added-background-alpha))
494
+ );
495
+ color: inherit;
496
+ text-decoration-line: underline;
497
+ text-decoration-thickness: var(--text-diff-mark-line-width);
498
+ }
499
+
500
+ .ui-text-diff-gap {
501
+ display: block;
502
+ padding-block: var(--text-diff-space-inset);
503
+ }
504
+
505
+ .ui-text-diff-split {
506
+ display: grid;
507
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
508
+ column-gap: var(--text-diff-space-gap);
509
+ }
510
+
511
+ .ui-text-diff-split > .ui-text-diff-gap {
512
+ grid-column: 1 / -1;
513
+ }
514
+
515
+ .ui-text-diff-heading {
516
+ padding-block-end: var(--text-diff-space-inset);
517
+ color: hsl(var(--muted-foreground));
518
+ font-size: var(--font-size-sm, var(--font-size-base));
519
+ font-weight: var(--font-weight-medium);
520
+ }
521
+
470
522
  .ui-timeline {
471
523
  display: flex;
472
524
  flex-direction: column;
@@ -523,7 +575,8 @@
523
575
  hsl(var(--primary) / 0.2);
524
576
  }
525
577
 
526
- .ui-timeline-dot[data-status="pending"] {
578
+ .ui-timeline-dot[data-status="pending"],
579
+ .ui-timeline-dot[data-status="log"] {
527
580
  border-color: hsl(var(--border));
528
581
  background: hsl(var(--background));
529
582
  color: hsl(var(--muted-foreground));
@@ -2126,6 +2179,44 @@
2126
2179
  opacity: var(--disabled-opacity);
2127
2180
  }
2128
2181
 
2182
+ .ui-tree-node[draggable="true"] {
2183
+ cursor: grab;
2184
+ }
2185
+
2186
+ .ui-tree-node[data-dragging="true"] {
2187
+ opacity: var(--disabled-opacity);
2188
+ }
2189
+
2190
+ .ui-tree-node[data-drop-position] {
2191
+ position: relative;
2192
+ }
2193
+
2194
+ .ui-tree-node[data-drop-position="before"]::after,
2195
+ .ui-tree-node[data-drop-position="after"]::after {
2196
+ content: "";
2197
+ position: absolute;
2198
+ inset-inline: calc(
2199
+ var(--tree-node-padding-inline) + var(--tree-node-level, 0) * var(--tree-indent-width)
2200
+ )
2201
+ 0;
2202
+ block-size: var(--tree-drop-indicator-width, var(--stroke-md));
2203
+ background: var(--tree-drop-indicator-color, hsl(var(--primary)));
2204
+ pointer-events: none;
2205
+ }
2206
+
2207
+ .ui-tree-node[data-drop-position="before"]::after {
2208
+ inset-block-start: 0;
2209
+ }
2210
+
2211
+ .ui-tree-node[data-drop-position="after"]::after {
2212
+ inset-block-end: 0;
2213
+ }
2214
+
2215
+ .ui-tree-node[data-drop-position="inside"] {
2216
+ box-shadow: inset 0 0 0 var(--tree-drop-indicator-width, var(--stroke-md))
2217
+ var(--tree-drop-indicator-color, hsl(var(--primary)));
2218
+ }
2219
+
2129
2220
  .ui-tree-switcher {
2130
2221
  display: inline-flex;
2131
2222
  flex: none;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.14.0",
2
+ "version": "31.16.0",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
@@ -1053,6 +1053,11 @@
1053
1053
  "dialog-layout.css"
1054
1054
  ]
1055
1055
  },
1056
+ "TextDiff": {
1057
+ "layers": [
1058
+ "control.css"
1059
+ ]
1060
+ },
1056
1061
  "Textarea": {
1057
1062
  "layers": [
1058
1063
  "control.css"
@@ -173,6 +173,14 @@
173
173
  --thumbnail-border-width: initial;
174
174
  --thumbnail-radius: initial;
175
175
 
176
+ --text-diff-removed-background-alpha: 0.12;
177
+ --text-diff-added-background-alpha: 0.16;
178
+
179
+ --text-diff-mark-line-width: var(--stroke-hairline);
180
+
181
+ --text-diff-space-gap: var(--space-3);
182
+ --text-diff-space-inset: var(--space-2);
183
+
176
184
  --timeline-dot-done-background: initial;
177
185
  --timeline-dot-done-foreground: initial;
178
186
  --timeline-dot-current-background: initial;
@@ -29,5 +29,8 @@
29
29
 
30
30
  --tree-node-filter-foreground: initial;
31
31
 
32
+ --tree-drop-indicator-color: initial;
33
+ --tree-drop-indicator-width: initial;
34
+
32
35
  --tree-loading-bar-inline-size: 40%;
33
36
  }
@@ -3,8 +3,8 @@
3
3
  > Generated by `scripts/frame-coverage.mjs` (issue #163). Do not edit by hand — run `pnpm check:frame-coverage`.
4
4
  > Standard: [FRAME-COVERAGE-STANDARD.md](./FRAME-COVERAGE-STANDARD.md). A missing dimension is **UNTESTED**, never a pass.
5
5
 
6
- - Public components: **178**
7
- - With ≥1 frame: **154** · zero-frame: **24**
6
+ - Public components: **179**
7
+ - With ≥1 frame: **155** · zero-frame: **24**
8
8
  - Fully covered (every contract axis declared): **0**
9
9
 
10
10
  ## UNTESTED — no `/frame/**` route
@@ -88,6 +88,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
88
88
  | Badge | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
89
89
  | Legend | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
90
90
  | Swatch | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
91
+ | TextDiff | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
91
92
  | FeatureList | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
92
93
  | Thumbnail | ✓ | · | N/A | N/A | · | · | · | · | · | · |
93
94
  | ListRow | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
@@ -0,0 +1,89 @@
1
+ import {
2
+ Card,
3
+ CardContent,
4
+ CardDescription,
5
+ CardHeader,
6
+ CardTitle,
7
+ TextDiff,
8
+ } from "@godxjp/ui/data-display";
9
+ import { useTranslation } from "@godxjp/ui/i18n";
10
+ import { Flex, PageContainer } from "@godxjp/ui/layout";
11
+
12
+ const PREVIOUS_ORIGINAL = "来週の定例会議までに、見積書を承認してください。";
13
+ const CURRENT_ORIGINAL = "今週金曜までに、見積書と請求書を却下してください。";
14
+
15
+ const PREVIOUS_POLICY = [
16
+ "1. 申請は月末までに提出する。",
17
+ "2. 領収書の原本を添付する。",
18
+ "3. 上長の承認を得る。",
19
+ "4. 経理部が内容を確認する。",
20
+ "5. 承認後、翌月の給与と合わせて支払う。",
21
+ "6. 不備がある場合は差し戻す。",
22
+ "7. 差し戻しから五営業日以内に再提出する。",
23
+ "8. 期限を過ぎた申請は受け付けない。",
24
+ ].join("\n");
25
+
26
+ const CURRENT_POLICY = [
27
+ "1. 申請は毎月二十日までに提出する。",
28
+ "2. 領収書の原本を添付する。",
29
+ "3. 上長の承認を得る。",
30
+ "4. 経理部が内容を確認する。",
31
+ "5. 承認後、翌月の給与と合わせて支払う。",
32
+ "6. 不備がある場合は差し戻す。",
33
+ "7. 差し戻しから五営業日以内に再提出する。",
34
+ "8. 期限を過ぎた申請は、部長の承認があれば受け付ける。",
35
+ ].join("\n");
36
+
37
+ /**
38
+ * TextDiff — what changed between two versions of a text (gh#1096).
39
+ * Composed only from real @godxjp/ui components.
40
+ */
41
+ export default function Demo() {
42
+ const { t } = useTranslation();
43
+ return (
44
+ <PageContainer title="TextDiff" subtitle={t("textDiffDocs.subtitle")}>
45
+ <Flex direction="col" gap="lg">
46
+ <Card>
47
+ <CardHeader>
48
+ <CardTitle level={2}>{t("textDiffDocs.source.title")}</CardTitle>
49
+ <CardDescription>{t("textDiffDocs.source.description")}</CardDescription>
50
+ </CardHeader>
51
+ <CardContent>
52
+ <TextDiff before={PREVIOUS_ORIGINAL} after={CURRENT_ORIGINAL} lang="ja" />
53
+ </CardContent>
54
+ </Card>
55
+
56
+ <Card>
57
+ <CardHeader>
58
+ <CardTitle level={2}>{t("textDiffDocs.policy.title")}</CardTitle>
59
+ <CardDescription>{t("textDiffDocs.policy.description")}</CardDescription>
60
+ </CardHeader>
61
+ <CardContent>
62
+ <TextDiff
63
+ mode="split"
64
+ before={PREVIOUS_POLICY}
65
+ after={CURRENT_POLICY}
66
+ lang="ja"
67
+ beforeLabel={t("textDiffDocs.policy.before")}
68
+ afterLabel={t("textDiffDocs.policy.after")}
69
+ />
70
+ </CardContent>
71
+ </Card>
72
+
73
+ <Card>
74
+ <CardHeader>
75
+ <CardTitle level={2}>{t("textDiffDocs.words.title")}</CardTitle>
76
+ <CardDescription>{t("textDiffDocs.words.description")}</CardDescription>
77
+ </CardHeader>
78
+ <CardContent>
79
+ <TextDiff
80
+ before="Please approve the quotation before next week's meeting."
81
+ after="Please reject the quotation and the invoice by Friday."
82
+ lang="en"
83
+ />
84
+ </CardContent>
85
+ </Card>
86
+ </Flex>
87
+ </PageContainer>
88
+ );
89
+ }
@@ -13,6 +13,7 @@ import {
13
13
  } from "@godxjp/ui/data-display";
14
14
  import { Text } from "@godxjp/ui/general";
15
15
  import { Flex, PageContainer } from "@godxjp/ui/layout";
16
+ import { ArrowRightLeft, MessageSquare } from "lucide-react";
16
17
 
17
18
  /**
18
19
  * Timeline — vertical event list with an icon rail; the current item gets a
@@ -89,6 +90,23 @@ const richItems: TimelineItem[] = [
89
90
 
90
91
  export default function Demo() {
91
92
  const { t, locale } = useTranslation();
93
+ // An activity log (gh#1092): every row already happened, so none is "done" or "upcoming" —
94
+ // `status: "log"` keeps the neutral dot and adds no screen-reader status prefix.
95
+ const logItems: TimelineItem[] = [
96
+ {
97
+ title: t("timelineDocs.log.status"),
98
+ time: "10/01 08:12",
99
+ status: "log",
100
+ icon: ArrowRightLeft,
101
+ },
102
+ {
103
+ title: t("timelineDocs.log.comment"),
104
+ time: "10/01 09:30",
105
+ status: "log",
106
+ icon: MessageSquare,
107
+ },
108
+ { title: t("timelineDocs.log.assignee"), time: "10/01 10:05", status: "log" },
109
+ ];
92
110
  const dayLabel = (day: number) =>
93
111
  new Intl.DateTimeFormat(locale, { month: "short", day: "numeric", timeZone: "UTC" }).format(
94
112
  Date.UTC(2026, 8, day),
@@ -139,6 +157,16 @@ export default function Demo() {
139
157
  </CardContent>
140
158
  </Card>
141
159
 
160
+ <Card>
161
+ <CardHeader>
162
+ <CardTitle level={2}>{t("timelineDocs.log.title")}</CardTitle>
163
+ <CardDescription>{t("timelineDocs.log.description")}</CardDescription>
164
+ </CardHeader>
165
+ <CardContent>
166
+ <Timeline density="compact" items={logItems} />
167
+ </CardContent>
168
+ </Card>
169
+
142
170
  <Card>
143
171
  <CardHeader>
144
172
  <CardTitle level={2}>配送ルート進捗</CardTitle>