@godxjp/ui 33.10.1 → 33.11.2

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 (38) hide show
  1. package/DESIGN.md +1 -0
  2. package/README.md +2 -2
  3. package/agent/DESIGN.md +1 -0
  4. package/agent/START-HERE.md +4 -4
  5. package/agent/components/Cascader.json +1 -0
  6. package/agent/components/DatePicker.json +1 -0
  7. package/agent/components/Input.json +1 -0
  8. package/agent/components/NumberInput.json +1 -0
  9. package/agent/components/PasswordInput.json +1 -0
  10. package/agent/components/SearchInput.json +1 -0
  11. package/agent/components/Select.json +1 -0
  12. package/agent/components/TimePicker.json +1 -0
  13. package/agent/components/Topbar.json +8 -2
  14. package/agent/components/TopbarItem.json +7 -1
  15. package/agent/components/TreeSelect.json +1 -0
  16. package/agent/components.json +24 -3
  17. package/agent/index.json +4 -4
  18. package/agent/llms.txt +5 -5
  19. package/agent/tokens.json +19 -1
  20. package/dist/components/data-entry/input.js +16 -1
  21. package/dist/components/data-entry/search-input.js +1 -1
  22. package/dist/components/layout/topbar-item.d.ts +1 -0
  23. package/dist/components/layout/topbar-item.js +9 -4
  24. package/dist/components/layout/topbar.d.ts +1 -1
  25. package/dist/components/layout/topbar.js +25 -3
  26. package/dist/contracts/measurement.json +1 -1
  27. package/dist/props/components/layout.prop.d.ts +13 -0
  28. package/dist/props/registry.d.ts +9 -1
  29. package/dist/props/registry.js +10 -0
  30. package/dist/styles/control.css +131 -3
  31. package/dist/styles/layers.json +1 -1
  32. package/dist/styles/shell-layout.css +38 -0
  33. package/dist/styles.css +1 -1
  34. package/dist/tokens/components/control.css +4 -1
  35. package/dist/tokens/components/shell.css +3 -0
  36. package/docs/TOKENS.md +11 -0
  37. package/package.json +2 -2
  38. package/scripts/visual-audit.mjs +33 -3
@@ -11,6 +11,7 @@ function Topbar({
11
11
  start,
12
12
  center,
13
13
  end,
14
+ identity,
14
15
  className,
15
16
  children,
16
17
  height,
@@ -20,8 +21,10 @@ function Topbar({
20
21
  ...props
21
22
  }) {
22
23
  const barRef = React.useRef(null);
24
+ const controlsRef = React.useRef(null);
25
+ const hasIdentity = identity != null && children == null;
23
26
  const foldable = overflow === "menu" && children == null;
24
- const collapsed = useCollapsesToMenu(barRef, foldable);
27
+ const collapsed = useCollapsesToMenu(hasIdentity ? controlsRef : barRef, foldable);
25
28
  const clusters = /* @__PURE__ */ jsxs(Fragment, { children: [
26
29
  start != null ? /* @__PURE__ */ jsx("div", { "data-slot": "topbar-start", className: "ui-topbar-start", children: start }) : null,
27
30
  center != null ? /* @__PURE__ */ jsx("div", { "data-slot": "topbar-center", className: "ui-topbar-center", children: center }) : null,
@@ -32,13 +35,26 @@ function Topbar({
32
35
  {
33
36
  ref: barRef,
34
37
  "data-height": height,
38
+ "data-identity": hasIdentity ? "true" : void 0,
35
39
  "data-overflow": overflow,
36
40
  "data-collapsed": foldable ? String(collapsed) : void 0,
37
41
  style: { ...style, ...padStyle(pad, void 0) },
38
42
  "data-slot": "topbar",
39
43
  className: cn("ui-topbar", className),
40
44
  ...props,
41
- children: children ?? (collapsed ? /* @__PURE__ */ jsx(TopbarOverflowMenu, { children: clusters }) : clusters)
45
+ children: children ?? (hasIdentity ? /* @__PURE__ */ jsxs(Fragment, { children: [
46
+ /* @__PURE__ */ jsx(
47
+ "div",
48
+ {
49
+ ref: controlsRef,
50
+ "data-slot": "topbar-controls",
51
+ "data-overflow": overflow,
52
+ className: "ui-topbar ui-topbar-controls",
53
+ children: collapsed ? /* @__PURE__ */ jsx(TopbarOverflowMenu, { children: clusters }) : clusters
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx("div", { "data-slot": "topbar-identity", className: "ui-topbar-identity", children: identity })
57
+ ] }) : collapsed ? /* @__PURE__ */ jsx(TopbarOverflowMenu, { children: clusters }) : clusters)
42
58
  }
43
59
  );
44
60
  }
@@ -69,7 +85,13 @@ function useCollapsesToMenu(ref, enabled) {
69
85
  if (typeof ResizeObserver === "undefined") return void 0;
70
86
  const observer = new ResizeObserver(update);
71
87
  observer.observe(bar);
72
- if (!collapsed) for (const cluster of bar.children) observer.observe(cluster);
88
+ if (!collapsed)
89
+ for (const cluster of bar.children) {
90
+ observer.observe(cluster);
91
+ if (cluster.getAttribute("data-slot") === "topbar-start") {
92
+ for (const cell of cluster.children) observer.observe(cell);
93
+ }
94
+ }
73
95
  return () => observer.disconnect();
74
96
  }, [ref, enabled, collapsed]);
75
97
  return enabled && collapsed;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "33.10.1",
3
+ "version": "33.11.2",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -1545,6 +1545,14 @@ export type TopbarProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children">
1545
1545
  center?: ReactNode;
1546
1546
  /** Inline-end cluster — settings pickers, notifications, the user menu. */
1547
1547
  end?: ReactNode;
1548
+ /**
1549
+ * Opt-in shared-device identity, pinned outside scrolling/overflow menus. Pass one
1550
+ * TopbarItem with `truncate` and an Avatar in `icon`; use a plain string name so its full
1551
+ * title is automatic. Never combine with hideBelow/labelHideBelow/iconHideFrom.
1552
+ * Capped by --topbar-identity-max-inline-size (40%); primary controls retain their floors.
1553
+ * At extreme widths only the other clusters scroll or move to the menu. Ignored with children.
1554
+ */
1555
+ identity?: ReactNode;
1548
1556
  /**
1549
1557
  * What a bar that does not fit does with the cells that do not fit (gh#728).
1550
1558
  *
@@ -1643,6 +1651,11 @@ export type TopbarItemProp = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>,
1643
1651
  * accessible name. Under `asChild` the child element's own children are the label.
1644
1652
  */
1645
1653
  labelHideBelow?: BreakpointProp;
1654
+ /** Ellipse the label in the available width, keeping the icon and control minimum.
1655
+ * Same boolean vocabulary as Text.truncate. Overrides labelHideBelow; the complete text
1656
+ * remains the accessible name. String children automatically supply title (asChild too).
1657
+ */
1658
+ truncate?: boolean;
1646
1659
  /**
1647
1660
  * Unread count OVERLAID on the cell's glyph — the notification-bell affordance the cell's own use
1648
1661
  * cases name (gh#398). Pass the CONTENT ONLY, exactly like `SidebarItemProp.badge`: a number, a
@@ -1433,7 +1433,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
1433
1433
  readonly TopbarProp: {
1434
1434
  readonly group: "layout";
1435
1435
  readonly file: "components/layout.prop.ts";
1436
- readonly vocabulary: readonly ["ChildrenProp", {
1436
+ readonly vocabulary: readonly [{
1437
+ readonly field: "identity";
1438
+ readonly local: true;
1439
+ readonly reason: "Reserved shared-device account slot; bounded and outside the overflow policy.";
1440
+ }, "ChildrenProp", {
1437
1441
  readonly field: "overflow";
1438
1442
  readonly local: true;
1439
1443
  readonly reason: "The bar's overflow contract — `scroll` (default) keeps every cell reachable, `clip` is the pre-gh#728 slicing.";
@@ -1443,6 +1447,10 @@ export declare const COMPONENT_PROP_REGISTRY: {
1443
1447
  readonly group: "layout";
1444
1448
  readonly file: "components/layout.prop.ts";
1445
1449
  readonly vocabulary: readonly ["ChildrenProp", "ClassNameProp", {
1450
+ readonly field: "truncate";
1451
+ readonly local: true;
1452
+ readonly reason: "Text.truncate boolean vocabulary applied to the bar cell label.";
1453
+ }, {
1446
1454
  readonly field: "asChild";
1447
1455
  readonly local: true;
1448
1456
  readonly reason: "Radix Slot passthrough — merges onto the child";
@@ -1551,6 +1551,11 @@ const COMPONENT_PROP_REGISTRY = {
1551
1551
  group: "layout",
1552
1552
  file: "components/layout.prop.ts",
1553
1553
  vocabulary: [
1554
+ {
1555
+ field: "identity",
1556
+ local: true,
1557
+ reason: "Reserved shared-device account slot; bounded and outside the overflow policy."
1558
+ },
1554
1559
  "ChildrenProp",
1555
1560
  {
1556
1561
  field: "overflow",
@@ -1565,6 +1570,11 @@ const COMPONENT_PROP_REGISTRY = {
1565
1570
  vocabulary: [
1566
1571
  "ChildrenProp",
1567
1572
  "ClassNameProp",
1573
+ {
1574
+ field: "truncate",
1575
+ local: true,
1576
+ reason: "Text.truncate boolean vocabulary applied to the bar cell label."
1577
+ },
1568
1578
  { field: "asChild", local: true, reason: "Radix Slot passthrough \u2014 merges onto the child" },
1569
1579
  "BreakpointProp",
1570
1580
  {
@@ -1232,7 +1232,10 @@
1232
1232
 
1233
1233
  .ui-search-input-control {
1234
1234
  padding-inline-start: var(--search-input-start-padding);
1235
- padding-inline-end: var(--search-input-end-padding);
1235
+ padding-inline-end: max(
1236
+ var(--search-input-end-padding),
1237
+ var(--search-input-affix-min-padding, 0px)
1238
+ );
1236
1239
  }
1237
1240
 
1238
1241
  .ui-search-input-clear {
@@ -1263,8 +1266,8 @@
1263
1266
  .ui-search-input-clear::after {
1264
1267
  content: "";
1265
1268
  position: absolute;
1266
- inset-block: min(0px, calc((100% - var(--control-height-default)) / 2));
1267
- inset-inline: min(0px, calc((100% - var(--control-height-default)) / 2));
1269
+ inset-block: min(0px, calc((100% - var(--control-affix-touch-target-size)) / 2));
1270
+ inset-inline: min(0px, calc((100% - var(--control-affix-touch-target-size)) / 2));
1268
1271
  }
1269
1272
  }
1270
1273
 
@@ -3767,3 +3770,128 @@
3767
3770
  min-inline-size: var(--control-height-touch);
3768
3771
  }
3769
3772
  }
3773
+
3774
+ @layer components {
3775
+ @media (pointer: coarse) {
3776
+ .ui-control-inline-affix-action:not(.ui-badge-remove),
3777
+ .ui-control-affix-action,
3778
+ .ui-tree-select-clear,
3779
+ .ui-password-input-toggle,
3780
+ .ui-search-input-clear,
3781
+ .ui-number-input-step {
3782
+ min-inline-size: var(--control-affix-touch-target-size);
3783
+ min-block-size: var(--control-affix-touch-target-size);
3784
+ flex-shrink: 0;
3785
+ }
3786
+
3787
+ .ui-control-inline-affix-action:not(.ui-badge-remove)::after {
3788
+ inset: 0;
3789
+ translate: none;
3790
+ }
3791
+
3792
+ .ui-input-affix-wrapper:has(.ui-control-inline-affix-action) > .ui-input {
3793
+ min-block-size: var(--control-affix-touch-target-size);
3794
+ }
3795
+
3796
+ .ui-input--trailing-affix {
3797
+ padding-inline-end: max(
3798
+ var(--control-inline-affix-space-inline-end),
3799
+ calc(
3800
+ var(--input-trailing-measured-size, var(--control-affix-touch-target-size)) +
3801
+ var(--control-inline-affix-inset-inline) + var(--control-inline-affix-space-gap)
3802
+ )
3803
+ );
3804
+ }
3805
+
3806
+ .ui-control-trigger-affixed {
3807
+ min-block-size: var(--control-affix-touch-target-size);
3808
+ padding-inline-end: max(
3809
+ var(--control-trigger-space-inline-end),
3810
+ calc(
3811
+ var(--control-affix-touch-target-size) + var(--control-affix-inset-inline-end) +
3812
+ var(--control-inline-affix-space-gap)
3813
+ )
3814
+ );
3815
+ }
3816
+
3817
+ .ui-tree-select-trigger-affixed {
3818
+ min-block-size: var(--control-affix-touch-target-size);
3819
+ padding-inline-end: calc(
3820
+ var(--control-affix-touch-target-size) + var(--space-3) +
3821
+ var(--control-inline-affix-space-gap)
3822
+ );
3823
+ }
3824
+
3825
+ .ui-password-input-field {
3826
+ min-block-size: var(--control-affix-touch-target-size);
3827
+ padding-inline-end: calc(
3828
+ var(--control-affix-touch-target-size) + var(--control-inline-affix-space-gap)
3829
+ );
3830
+ }
3831
+
3832
+ .ui-number-input-steppers {
3833
+ flex-direction: row;
3834
+ align-items: center;
3835
+ inset-block: var(--control-border-width, var(--stroke-hairline));
3836
+ }
3837
+
3838
+ .ui-number-input-step {
3839
+ border: 0;
3840
+ background: transparent;
3841
+ box-shadow: none;
3842
+ }
3843
+ .ui-number-input-step:hover {
3844
+ background: hsl(var(--accent));
3845
+ color: hsl(var(--accent-foreground));
3846
+ }
3847
+ .ui-number-input-step::after {
3848
+ inset-block: calc((100% - var(--control-affix-touch-target-size)) / 2) auto;
3849
+ inset-inline: calc((100% - var(--control-affix-touch-target-size)) / 2) auto;
3850
+ inline-size: var(--control-affix-touch-target-size);
3851
+ block-size: var(--control-affix-touch-target-size);
3852
+ translate: none;
3853
+ }
3854
+ .ui-search-input-control {
3855
+ min-block-size: var(--control-affix-touch-target-size);
3856
+
3857
+ --search-input-affix-min-padding: max(
3858
+ var(--control-affix-touch-target-size),
3859
+ calc(
3860
+ var(--search-input-edge-inset) + var(--touch-target-min) / 2 +
3861
+ var(--control-affix-touch-target-size) / 2
3862
+ )
3863
+ );
3864
+ }
3865
+ [data-slot="textarea-affix-wrapper"]:has(.ui-textarea-clear) > textarea {
3866
+ min-block-size: calc(
3867
+ var(--control-affix-touch-target-size) + 2 * var(--textarea-clear-inset-block-start)
3868
+ );
3869
+ }
3870
+ .ui-search-input-clear {
3871
+ inset-inline-end: max(
3872
+ 0px,
3873
+ calc(
3874
+ var(--search-input-edge-inset) + var(--touch-target-min) / 2 -
3875
+ var(--control-affix-touch-target-size) / 2
3876
+ )
3877
+ );
3878
+ }
3879
+ .ui-number-input-field {
3880
+ min-block-size: calc(
3881
+ var(--control-affix-touch-target-size) + 2 *
3882
+ var(--control-border-width, var(--stroke-hairline))
3883
+ );
3884
+ padding-inline-end: calc(2 * var(--control-affix-touch-target-size) + var(--space-2));
3885
+ }
3886
+ .ui-number-input:not([data-controls="off"]) [data-slot="number-input-suffix"] {
3887
+ inset-inline-end: calc(2 * var(--control-affix-touch-target-size) + var(--space-2));
3888
+ }
3889
+ .ui-number-input:not([data-controls="off"]):has([data-slot="number-input-suffix"])
3890
+ .ui-number-input-field {
3891
+ padding-inline-end: calc(
3892
+ 2 * var(--control-affix-touch-target-size) + var(--space-2) +
3893
+ var(--control-inline-affix-size)
3894
+ );
3895
+ }
3896
+ }
3897
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "33.10.1",
2
+ "version": "33.11.2",
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",
@@ -2272,6 +2272,44 @@
2272
2272
  overflow: visible;
2273
2273
  }
2274
2274
 
2275
+ .ui-topbar[data-identity="true"] {
2276
+ overflow: clip;
2277
+ }
2278
+ .ui-topbar-controls {
2279
+ padding-inline: 0;
2280
+ height: auto;
2281
+ background: none;
2282
+ backdrop-filter: none;
2283
+ }
2284
+
2285
+ .ui-topbar-controls > .ui-topbar-start {
2286
+ display: contents;
2287
+ }
2288
+ .ui-topbar-identity {
2289
+ display: flex;
2290
+ align-self: stretch;
2291
+ flex: 0 0 auto;
2292
+ min-inline-size: 0;
2293
+ max-inline-size: var(--topbar-identity-max-inline-size);
2294
+ margin-inline-start: auto;
2295
+ }
2296
+ .ui-topbar-item[data-truncate="true"] {
2297
+ flex: 0 1 auto;
2298
+ min-inline-size: var(--topbar-item-min-width);
2299
+ max-inline-size: 100%;
2300
+ }
2301
+ .ui-topbar-item[data-truncate="true"] > .ui-topbar-item-label {
2302
+ min-inline-size: 0;
2303
+
2304
+ line-height: var(--line-height-normal);
2305
+ overflow: hidden;
2306
+ text-overflow: ellipsis;
2307
+ white-space: nowrap;
2308
+ }
2309
+ .ui-topbar-identity .ui-topbar-item[data-truncate="true"] > .ui-topbar-item-label {
2310
+ min-inline-size: var(--topbar-identity-min-text-inline-size);
2311
+ }
2312
+
2275
2313
  .ui-topbar-item {
2276
2314
  display: inline-flex;
2277
2315
  min-width: var(--topbar-item-min-width);