@godxjp/ui 28.10.0 → 28.12.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.
@@ -3170,7 +3170,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
3170
3170
  readonly ServiceLauncherCardProp: {
3171
3171
  readonly group: "data-display";
3172
3172
  readonly file: "components/data-display/service-launcher-card.tsx";
3173
- readonly vocabulary: readonly ["TitleProp", "ToneProp", "IconProp", "ClassNameProp", "DisabledProp"];
3173
+ readonly vocabulary: readonly ["TitleProp", "ToneProp", "IconProp", "ClassNameProp", "DisabledProp", {
3174
+ readonly field: "logo";
3175
+ readonly local: true;
3176
+ readonly reason: "URL of the service's OWN uploaded mark, distinct from `icon` (the LucideIcon for its kind) and deliberately NOT folded into it: `icon` is the fallback the tile needs when the upload 404s or does not exist yet, so the two cannot be one prop. A `string` URL rather than a ReactNode because the medallion owns the box, the object-fit and the decode hint (gh#850).";
3177
+ }];
3174
3178
  };
3175
3179
  readonly ServiceCatalogCtaProp: {
3176
3180
  readonly group: "data-display";
@@ -3763,7 +3763,18 @@ const COMPONENT_PROP_REGISTRY = {
3763
3763
  ServiceLauncherCardProp: {
3764
3764
  group: "data-display",
3765
3765
  file: "components/data-display/service-launcher-card.tsx",
3766
- vocabulary: ["TitleProp", "ToneProp", "IconProp", "ClassNameProp", "DisabledProp"]
3766
+ vocabulary: [
3767
+ "TitleProp",
3768
+ "ToneProp",
3769
+ "IconProp",
3770
+ "ClassNameProp",
3771
+ "DisabledProp",
3772
+ {
3773
+ field: "logo",
3774
+ local: true,
3775
+ reason: "URL of the service's OWN uploaded mark, distinct from `icon` (the LucideIcon for its kind) and deliberately NOT folded into it: `icon` is the fallback the tile needs when the upload 404s or does not exist yet, so the two cannot be one prop. A `string` URL rather than a ReactNode because the medallion owns the box, the object-fit and the decode hint (gh#850)."
3776
+ }
3777
+ ]
3767
3778
  },
3768
3779
  ServiceCatalogCtaProp: {
3769
3780
  group: "data-display",
@@ -574,6 +574,12 @@
574
574
  height: var(--card-service-launcher-icon-glyph-size);
575
575
  }
576
576
 
577
+ [data-slot="service-launcher-logo"] {
578
+ width: var(--card-service-launcher-icon-glyph-size);
579
+ height: var(--card-service-launcher-icon-glyph-size);
580
+ object-fit: contain;
581
+ }
582
+
577
583
  [data-slot="service-launcher-identity"] {
578
584
  display: flex;
579
585
  min-width: 0;
@@ -1160,7 +1160,7 @@
1160
1160
  align-items: stretch;
1161
1161
  padding: var(--segmented-track-padding);
1162
1162
  border-radius: var(--segmented-track-radius);
1163
- background: hsl(var(--segmented-track-background));
1163
+ background: hsl(var(--segmented-track-background, var(--muted)));
1164
1164
  color: hsl(var(--segmented-item-color));
1165
1165
  font-size: var(--font-size-base);
1166
1166
  }
@@ -1196,7 +1196,7 @@
1196
1196
  }
1197
1197
 
1198
1198
  .ui-segmented-item[data-state="checked"] {
1199
- background: hsl(var(--segmented-item-selected-background));
1199
+ background: hsl(var(--segmented-item-selected-background, var(--background)));
1200
1200
 
1201
1201
  box-shadow:
1202
1202
  var(--segmented-item-selected-shadow),
@@ -1,7 +1,250 @@
1
1
  @layer components {
2
2
 
3
- .ui-attachments-input {
4
- min-block-size: var(--touch-target-min);
3
+ .ui-attachments {
4
+ position: relative;
5
+ inline-size: 100%;
6
+ }
7
+
8
+ .ui-attachments-list {
9
+ display: flex;
10
+ align-items: stretch;
11
+ gap: var(--attachments-gap);
12
+ margin: 0;
13
+ padding: 0;
14
+ list-style: none;
15
+ }
16
+
17
+ .ui-attachments-list > li {
18
+ position: relative;
19
+ display: flex;
20
+ flex: none;
21
+ min-inline-size: 0;
22
+ }
23
+
24
+ .ui-attachments-list[data-overflow="wrap"] {
25
+ flex-wrap: wrap;
26
+ }
27
+
28
+ .ui-attachments-list[data-overflow="scrollX"] {
29
+ flex-wrap: nowrap;
30
+ overflow-x: auto;
31
+ overflow-y: hidden;
32
+ }
33
+
34
+ .ui-attachments-list[data-overflow="scrollY"] {
35
+ flex-wrap: wrap;
36
+ overflow-x: hidden;
37
+ overflow-y: auto;
38
+ max-block-size: var(--attachments-card-block-size);
39
+ }
40
+
41
+ .ui-attachments-card {
42
+ position: relative;
43
+ display: flex;
44
+ align-items: center;
45
+ gap: var(--attachments-card-gap);
46
+ inline-size: var(--attachments-card-size);
47
+
48
+ max-inline-size: 100%;
49
+ block-size: var(--attachments-card-block-size);
50
+ padding-block: var(--space-3);
51
+ padding-inline: var(--space-4);
52
+ border: var(--stroke-hairline) solid transparent;
53
+ border-radius: var(--attachments-card-radius);
54
+ background-color: hsl(var(--muted));
55
+ overflow: hidden;
56
+ }
57
+
58
+ .ui-attachments-card-preview,
59
+ .ui-attachments-card-placeholder {
60
+ flex: none;
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ inline-size: var(--icon-size-2xl);
65
+ block-size: var(--icon-size-2xl);
66
+ }
67
+
68
+ .ui-attachments-card-preview {
69
+ border-radius: var(--radius-md);
70
+ object-fit: cover;
71
+ }
72
+
73
+ .ui-attachments-card-placeholder {
74
+ color: hsl(var(--muted-foreground));
75
+ }
76
+
77
+ .ui-attachments-card-placeholder > svg {
78
+ inline-size: var(--icon-size-2xl);
79
+ block-size: var(--icon-size-2xl);
80
+ }
81
+
82
+ .ui-attachments-card-content {
83
+ display: flex;
84
+ flex: 1 1 auto;
85
+ min-inline-size: 0;
86
+ flex-direction: column;
87
+ justify-content: center;
88
+ }
89
+
90
+ .ui-attachments-card-name {
91
+ display: flex;
92
+ max-inline-size: 100%;
93
+ font-size: var(--font-size-base);
94
+ color: hsl(var(--foreground));
95
+ }
96
+
97
+ .ui-attachments-card-name-prefix {
98
+ flex: 0 1 auto;
99
+ min-inline-size: 0;
100
+ overflow: hidden;
101
+ text-overflow: ellipsis;
102
+ white-space: nowrap;
103
+ }
104
+
105
+ .ui-attachments-card-name-suffix {
106
+ flex: none;
107
+ }
108
+
109
+ .ui-attachments-card-meta {
110
+ overflow: hidden;
111
+ font-size: var(--font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)));
112
+ color: hsl(var(--muted-foreground));
113
+ text-overflow: ellipsis;
114
+ white-space: nowrap;
115
+ }
116
+
117
+ .ui-attachments-card[data-status="error"] {
118
+ border-color: hsl(var(--text-error));
119
+ }
120
+
121
+ .ui-attachments-card[data-status="error"] .ui-attachments-card-meta {
122
+ color: hsl(var(--text-error));
123
+ }
124
+
125
+ .ui-attachments-card-progress {
126
+ position: absolute;
127
+ inset-inline: 0;
128
+ inset-block-end: 0;
129
+ display: block;
130
+ }
131
+
132
+ .ui-attachments-card-remove {
133
+ flex: none;
134
+ }
135
+
136
+ .ui-attachments-card-remove > svg {
137
+ inline-size: var(--attachments-remove-icon-size);
138
+ block-size: var(--attachments-remove-icon-size);
139
+ }
140
+
141
+ .ui-attachments-upload-btn {
142
+ display: flex;
143
+ align-items: center;
144
+ justify-content: center;
145
+
146
+ inline-size: var(--attachments-card-block-size);
147
+ block-size: var(--attachments-card-block-size);
148
+ border: var(--stroke-hairline) dashed hsl(var(--border));
149
+ border-radius: var(--attachments-card-radius);
150
+ background-color: transparent;
151
+ color: hsl(var(--muted-foreground));
152
+ cursor: pointer;
153
+ transition:
154
+ border-color 150ms ease,
155
+ color 150ms ease;
156
+ }
157
+
158
+ .ui-attachments-upload-btn:hover:not(:disabled) {
159
+ border-color: hsl(var(--primary));
160
+ color: hsl(var(--primary));
161
+ }
162
+
163
+ .ui-attachments-upload-btn:disabled {
164
+ cursor: not-allowed;
165
+ opacity: 0.5;
166
+ }
167
+
168
+ .ui-attachments-upload-btn > svg {
169
+ inline-size: var(--icon-size-xl);
170
+ block-size: var(--icon-size-xl);
171
+ }
172
+
173
+ .ui-attachments-placeholder {
174
+ display: flex;
175
+ flex-direction: column;
176
+ align-items: center;
177
+ justify-content: center;
178
+ gap: var(--attachments-placeholder-gap);
179
+ padding: var(--attachments-placeholder-space-inset);
180
+ border: var(--stroke-md) dashed hsl(var(--border));
181
+ border-radius: var(--attachments-placeholder-radius);
182
+ background-color: hsl(var(--muted) / 0.4);
183
+ text-align: center;
184
+ cursor: pointer;
185
+ transition:
186
+ border-color 150ms ease,
187
+ background-color 150ms ease;
188
+ }
189
+
190
+ .ui-attachments-placeholder[data-drag-active="true"] {
191
+ border-color: hsl(var(--primary));
192
+ background-color: hsl(var(--primary) / 0.05);
193
+ }
194
+
195
+ .ui-attachments-placeholder:not([data-drag-active="true"]):hover {
196
+ border-color: hsl(var(--primary) / 0.5);
197
+ }
198
+
199
+ .ui-attachments-placeholder[data-disabled="true"] {
200
+ cursor: not-allowed;
201
+ opacity: 0.25;
202
+ pointer-events: none;
203
+ }
204
+
205
+ .ui-attachments-placeholder-icon {
206
+ display: flex;
207
+ color: hsl(var(--muted-foreground));
208
+ }
209
+
210
+ .ui-attachments-placeholder-icon > svg {
211
+ inline-size: var(--attachments-placeholder-icon-size);
212
+ block-size: var(--attachments-placeholder-icon-size);
213
+ }
214
+
215
+ .ui-attachments-placeholder-title {
216
+ font-size: var(--attachments-placeholder-title-font-size);
217
+ font-weight: var(--font-weight-medium);
218
+ color: hsl(var(--foreground));
219
+ }
220
+
221
+ .ui-attachments-placeholder-description {
222
+ font-size: var(--attachments-placeholder-description-font-size);
223
+ color: hsl(var(--muted-foreground));
224
+ }
225
+
226
+ .ui-attachments-drop-layer {
227
+ position: absolute;
228
+ inset: 0;
229
+ z-index: 1;
230
+ border: var(--stroke-md) dashed hsl(var(--primary));
231
+ border-radius: var(--attachments-placeholder-radius);
232
+ background: var(
233
+ --attachments-drop-overlay-background,
234
+ color-mix(in srgb, hsl(var(--background)) 85%, transparent)
235
+ );
236
+ opacity: 0;
237
+ pointer-events: none;
238
+ transition: opacity 150ms ease;
239
+ }
240
+
241
+ .ui-attachments-drop-layer[data-active="true"] {
242
+ opacity: 1;
243
+ backdrop-filter: blur(10px);
244
+ }
245
+
246
+ .ui-attachments-drop-layer[data-scope="viewport"] {
247
+ position: fixed;
5
248
  }
6
249
 
7
250
  .ui-combobox-content {
@@ -1,15 +1,24 @@
1
1
 
2
2
  :root {
3
+
3
4
  --attachments-gap: var(--space-2);
4
- --attachments-card-size: var(--upload-tile-size);
5
- --attachments-card-radius: var(--upload-tile-radius);
6
- --attachments-card-gap: var(--upload-tile-space-gap);
7
- --attachments-placeholder-space-inset: var(--upload-dropzone-space-inset);
8
- --attachments-placeholder-radius: var(--upload-dropzone-radius);
9
- --attachments-placeholder-icon-size: var(--upload-dropzone-icon-size);
10
- --attachments-placeholder-title-font-size: var(--font-size-sm, var(--font-size-base));
11
- --attachments-placeholder-description-font-size: var(--upload-dropzone-meta-font-size);
12
- --attachments-placeholder-gap: var(--space-2);
5
+
6
+ --attachments-card-size: 16.75rem;
7
+ --attachments-card-block-size: 4.25rem;
8
+ --attachments-card-radius: var(--radius-lg);
9
+ --attachments-card-gap: var(--space-3);
10
+
11
+ --attachments-placeholder-space-inset: var(--space-4);
12
+ --attachments-placeholder-radius: var(
13
+ --radius-lg
14
+ );
15
+
16
+ --attachments-placeholder-icon-size: var(--icon-size-3xl);
17
+ --attachments-placeholder-title-font-size: var(--font-size-base);
18
+ --attachments-placeholder-description-font-size: var(--font-size-sm, var(--font-size-base));
19
+ --attachments-placeholder-gap: var(--space-1);
20
+
13
21
  --attachments-drop-overlay-background: initial;
22
+
14
23
  --attachments-remove-icon-size: var(--upload-remove-icon-size);
15
24
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  --segmented-track-padding: calc(var(--space-1) / 2);
6
6
 
7
- --segmented-track-background: var(--muted);
7
+ --segmented-track-background: initial;
8
8
 
9
9
  --segmented-track-radius: var(--radius);
10
10
  --segmented-item-radius: var(--radius-md);
@@ -27,7 +27,7 @@
27
27
  --segmented-item-hover-background: initial;
28
28
  --segmented-item-active-background: var(--secondary);
29
29
 
30
- --segmented-item-selected-background: var(--background);
30
+ --segmented-item-selected-background: initial;
31
31
  --segmented-item-selected-shadow: var(--shadow-md);
32
32
 
33
33
  --segmented-item-selected-font-weight: var(--font-weight-medium);
@@ -463,6 +463,44 @@ expandable.expandedRowKeys` / `onExpandedRowsChange`. This library already mappe
463
463
  (`src/props/components/data-entry.prop.ts`) and in the MCP catalog. Revisit if a `Space`/compact
464
464
  primitive is ever ported — then antd's steer is followable and this becomes a migration.
465
465
 
466
+ - **`Attachments` is Ant Design X's card and tray, with five measured departures (gh#855).** The
467
+ component had shipped with NO stylesheet at all — fifteen slot classes, one rule, twelve tokens
468
+ read by nothing — so the port below is the first time it followed anything. Geometry was taken
469
+ from `@ant-design/x@2.9.0` (`es/attachments/style/index.js` for root/placeholder/drop-area,
470
+ `es/file-card/style/index.js` for list/card/overflow/remove), not from a screenshot. What was
471
+ NOT followed, and why:
472
+
473
+ 1. **The card's 268px is a CEILING, not a fixed width.** Ant X writes `width: 268` flat. At the
474
+ 320px viewport this repo sweeps, 268 plus the page gutter spills, and `check:frame-overflow`
475
+ is the gate that would have to be baselined around it. `inline-size` + `max-inline-size: 100%`
476
+ keeps Ant's box everywhere it fits and gives ground only where the alternative is a clip.
477
+ 2. **One leading box for a thumbnail AND a glyph.** Ant X has two card types — a 268x68 overview
478
+ card and a separate 68x68 image card — so a tray mixing a `.png` with a `.pdf` gets two
479
+ shapes. This library already decided that question for `Upload` (gh#720: "the image rows draw
480
+ their previewUrl as a thumbnail and every other row draws the glyph for its kind, on one row
481
+ height"), and a second answer in the same group would be the defect.
482
+ 3. **The remove ✕ is always visible, and in flow.** Ant X pins it absolutely outside the card
483
+ corner at `opacity: 0` until the card is hovered. A hover-only control has no touch
484
+ equivalent — worse, an `opacity: 0` button is still hit-testable, so a tablet user taps a
485
+ control they cannot see. `.ui-upload-tile-remove` already made this call. Always-visible in
486
+ turn removes the reason to position it outside the box, so it is the row's trailing item.
487
+ 4. **The placeholder mark is the icon scale's 40px step, not `fontSizeHeading2` (30px).** 30 is
488
+ not one of the nine steps gh#326 fixed, and its neighbours are 24 and 36 — equidistant. The
489
+ token keeps the value the frozen table in `src/tokens/__tests__/icon-size-scale.test.ts`
490
+ already records for it, which is `--upload-dropzone-icon-size`: the mark a reader meets this
491
+ surface beside. Same reasoning holds `--attachments-remove-icon-size` at 14px against Ant's
492
+ `fontSizeLG` = 16.
493
+ 5. **The placeholder carries a dashed border at rest; Ant X's is `transparent`.** Ant X's
494
+ placeholder sits INSIDE a `Sender`, which already draws the boundary. Here `Attachments` is
495
+ also used standalone, where a target with no edge is not a target — so it takes
496
+ `.ui-upload-dropzone`'s dashed edge.
497
+
498
+ Not ported, and named here so the gap is a decision rather than an oversight: Ant X's 8px
499
+ `::before`/`::after` scroll-edge gradients and their `ping-start`/`ping-end` state (they need a
500
+ scroll listener, and the clipped card already reads as "more this way"), and its full-screen
501
+ drop area — `getDropContainer` positions the layer `fixed`, but activation is still the
502
+ control's own dragenter, not the document's.
503
+
466
504
  **A knob that only a fork could reach is not parity either.** antd's `components`,
467
505
  `filterDropdown`, `classNames`/`styles` semantic maps and `prefixCls` all exist to let a consumer
468
506
  replace the rendered markup. This library answers that layer with tokens (cardinal rule #45), so
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="240" height="96" viewBox="0 0 240 96" role="img">
2
+ <rect width="240" height="96" rx="12" fill="hsl(348 32% 42%)"/>
3
+ <rect x="24" y="30" width="36" height="36" rx="8" fill="hsl(348 32% 88%)"/>
4
+ <rect x="76" y="36" width="140" height="10" rx="5" fill="hsl(348 32% 88%)"/>
5
+ <rect x="76" y="54" width="96" height="10" rx="5" fill="hsl(348 32% 74%)"/>
6
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img">
2
+ <rect width="128" height="128" rx="28" fill="hsl(178 38% 34%)"/>
3
+ <path d="M40 78 L64 34 L88 78 Z" fill="hsl(178 38% 88%)"/>
4
+ <circle cx="64" cy="90" r="9" fill="hsl(178 38% 88%)"/>
5
+ </svg>