@tangle-network/ui 11.30.0 → 11.30.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.30.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 0b6d379: `Toolbar` wraps its filters below `lg` instead of scrolling them as one line, so a phone shows every filter: at 390px the second select had started at 403px, off screen. From `lg` up the filters still share one scrollable line. `SidebarDropZone` sets its title on the label size (`--font-size-label`, 14px) and its description on the help size (`--font-size-help`, 12px) instead of 12px and 10px literals, and `UploadProgress` sets its size and error lines on the help size.
8
+
3
9
  ## 11.30.0
4
10
 
5
11
  ### Minor Changes
@@ -73,11 +73,11 @@ function SidebarDropZone({ onDrop, accept, disabled, title = "Drop files here",
73
73
  children: icon ?? /* @__PURE__ */ jsx(Upload, { className: "h-4 w-4" })
74
74
  }),
75
75
  /* @__PURE__ */ jsx("p", {
76
- className: cn("text-xs font-medium", dragOver ? "text-[var(--text-primary,hsl(var(--foreground)))]" : "text-[var(--text-muted,hsl(var(--muted-foreground)))]"),
76
+ className: cn("text-[length:var(--font-size-label,0.875rem)] font-medium", dragOver ? "text-[var(--text-primary,hsl(var(--foreground)))]" : "text-[var(--text-muted,hsl(var(--muted-foreground)))]"),
77
77
  children: title
78
78
  }),
79
79
  description && /* @__PURE__ */ jsx("p", {
80
- className: "text-[10px] text-[var(--text-muted,hsl(var(--muted-foreground)))]",
80
+ className: "text-[length:var(--font-size-help,0.75rem)] text-[var(--text-muted,hsl(var(--muted-foreground)))]",
81
81
  children: description
82
82
  })
83
83
  ]
@@ -21,9 +21,11 @@ import * as React from "react";
21
21
  * case — and search stays usable however many filters a page adds. `max-w-sm`
22
22
  * then stops search claiming more than it can use when there are few filters.
23
23
  *
24
- * On a narrow viewport the row becomes a column and the filters scroll
25
- * horizontally as a group rather than wrapping into a tall stack that pushes
26
- * the table itself below the fold.
24
+ * Below `lg` the row becomes a column and the filters wrap onto as many lines
25
+ * as they need. Scrolling them as one line hid every filter past the first
26
+ * behind the screen edge on a phone: at 390px the second select started at
27
+ * 403px and a third at 652px, with nothing to say more were there. From `lg`
28
+ * up they share one line and scroll within their half, as above.
27
29
  */
28
30
  /**
29
31
  * Slots only — `children` is omitted deliberately. A free child would render as
@@ -34,7 +36,7 @@ import * as React from "react";
34
36
  export interface ToolbarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
35
37
  /** The one control that expands to fill the row. */
36
38
  search?: React.ReactNode;
37
- /** Filters, in reading order. Kept on one scrollable line when space runs out. */
39
+ /** Filters, in reading order. Wrapped below `lg`; one scrollable line from `lg` up. */
38
40
  filters?: React.ReactNode;
39
41
  /** View switches and exports, pinned to the end. */
40
42
  actions?: React.ReactNode;
@@ -12,7 +12,7 @@ const Toolbar = React.forwardRef(({ className, search, filters, actions, ...prop
12
12
  children: search
13
13
  }),
14
14
  filters && /* @__PURE__ */ jsx("div", {
15
- className: "flex min-w-0 items-center gap-2 overflow-x-auto py-1 lg:flex-1",
15
+ className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2 py-1 lg:flex-1 lg:flex-nowrap lg:gap-2 lg:overflow-x-auto",
16
16
  children: filters
17
17
  }),
18
18
  actions && /* @__PURE__ */ jsx("div", {
@@ -33,7 +33,7 @@ function UploadProgress({ files, onRemove, onRetry, className }) {
33
33
  className: "truncate font-medium text-foreground",
34
34
  children: file.name
35
35
  }), /* @__PURE__ */ jsx("span", {
36
- className: "shrink-0 text-xs text-muted-foreground",
36
+ className: "shrink-0 text-[length:var(--font-size-help,0.75rem)] text-muted-foreground",
37
37
  children: formatSize(file.size)
38
38
  })]
39
39
  }),
@@ -45,7 +45,7 @@ function UploadProgress({ files, onRemove, onRetry, className }) {
45
45
  })
46
46
  }),
47
47
  file.status === "error" && file.error && /* @__PURE__ */ jsx("p", {
48
- className: "mt-0.5 text-xs text-[var(--surface-danger-text)]",
48
+ className: "mt-0.5 text-[length:var(--font-size-help,0.75rem)] text-[var(--surface-danger-text)]",
49
49
  children: file.error
50
50
  })
51
51
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.30.0",
3
+ "version": "11.30.1",
4
4
  "description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -150,7 +150,7 @@
150
150
  "peerDependencies": {
151
151
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
152
152
  "@nanostores/react": ">=0.8.4 <3.0.0",
153
- "@tangle-network/brand": "^1.19.2",
153
+ "@tangle-network/brand": "^1.19.3",
154
154
  "@tiptap/core": ">=3.20.4 <4.0.0",
155
155
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
156
156
  "@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
@@ -130,7 +130,7 @@ export function SidebarDropZone({
130
130
  {icon ?? <Upload className="h-4 w-4" />}
131
131
  </div>
132
132
  <p className={cn(
133
- "text-xs font-medium",
133
+ "text-[length:var(--font-size-label,0.875rem)] font-medium",
134
134
  dragOver
135
135
  ? "text-[var(--text-primary,hsl(var(--foreground)))]"
136
136
  : "text-[var(--text-muted,hsl(var(--muted-foreground)))]",
@@ -138,7 +138,7 @@ export function SidebarDropZone({
138
138
  {title}
139
139
  </p>
140
140
  {description && (
141
- <p className="text-[10px] text-[var(--text-muted,hsl(var(--muted-foreground)))]">
141
+ <p className="text-[length:var(--font-size-help,0.75rem)] text-[var(--text-muted,hsl(var(--muted-foreground)))]">
142
142
  {description}
143
143
  </p>
144
144
  )}
@@ -48,12 +48,16 @@ describe("Toolbar", () => {
48
48
  expect(actionsSlot.className).not.toContain("flex-1");
49
49
  });
50
50
 
51
- it("scrolls the filter row instead of wrapping it", () => {
51
+ it("wraps the filters on a phone and scrolls one line from lg up", () => {
52
52
  const { container } = render(<Toolbar filters={<span>Product</span>} />);
53
53
  const filterSlot = (container.firstElementChild as HTMLElement)
54
54
  .children[0] as HTMLElement;
55
- expect(filterSlot.className).toContain("overflow-x-auto");
56
- expect(filterSlot.className).toContain("min-w-0");
55
+ const classes = filterSlot.className.split(" ");
56
+ expect(classes).toContain("flex-wrap");
57
+ expect(classes).not.toContain("overflow-x-auto");
58
+ expect(classes).toContain("lg:flex-nowrap");
59
+ expect(classes).toContain("lg:overflow-x-auto");
60
+ expect(classes).toContain("min-w-0");
57
61
  });
58
62
 
59
63
  // A free child would render as a bare flex item with none of the slots'
@@ -22,9 +22,11 @@ import { cn } from "../lib/utils";
22
22
  * case — and search stays usable however many filters a page adds. `max-w-sm`
23
23
  * then stops search claiming more than it can use when there are few filters.
24
24
  *
25
- * On a narrow viewport the row becomes a column and the filters scroll
26
- * horizontally as a group rather than wrapping into a tall stack that pushes
27
- * the table itself below the fold.
25
+ * Below `lg` the row becomes a column and the filters wrap onto as many lines
26
+ * as they need. Scrolling them as one line hid every filter past the first
27
+ * behind the screen edge on a phone: at 390px the second select started at
28
+ * 403px and a third at 652px, with nothing to say more were there. From `lg`
29
+ * up they share one line and scroll within their half, as above.
28
30
  */
29
31
  /**
30
32
  * Slots only — `children` is omitted deliberately. A free child would render as
@@ -36,7 +38,7 @@ export interface ToolbarProps
36
38
  extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
37
39
  /** The one control that expands to fill the row. */
38
40
  search?: React.ReactNode;
39
- /** Filters, in reading order. Kept on one scrollable line when space runs out. */
41
+ /** Filters, in reading order. Wrapped below `lg`; one scrollable line from `lg` up. */
40
42
  filters?: React.ReactNode;
41
43
  /** View switches and exports, pinned to the end. */
42
44
  actions?: React.ReactNode;
@@ -56,11 +58,12 @@ const Toolbar = React.forwardRef<HTMLDivElement, ToolbarProps>(
56
58
  >
57
59
  {search && <div className="min-w-0 lg:max-w-sm lg:flex-1">{search}</div>}
58
60
  {filters && (
59
- // `py-1` leaves room for a focus ring above and below; horizontally the
60
- // scroll container clips it, which is the trade a single-line filter row
61
- // makes. No negative margin — pulling the row wider than its parent is
62
- // what made every page report 4px of horizontal overflow.
63
- <div className="flex min-w-0 items-center gap-2 overflow-x-auto py-1 lg:flex-1">
61
+ // `py-1` leaves room for a focus ring above and below; on the single
62
+ // `lg` line the scroll container clips it horizontally, which is the
63
+ // trade a one-line filter row makes. No negative margin — pulling the
64
+ // row wider than its parent is what made every page report 4px of
65
+ // horizontal overflow.
66
+ <div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2 py-1 lg:flex-1 lg:flex-nowrap lg:gap-2 lg:overflow-x-auto">
64
67
  {filters}
65
68
  </div>
66
69
  )}
@@ -65,7 +65,7 @@ export function UploadProgress({ files, onRemove, onRetry, className }: UploadPr
65
65
  <div className="min-w-0 flex-1">
66
66
  <div className="flex items-center gap-2">
67
67
  <span className="truncate font-medium text-foreground">{file.name}</span>
68
- <span className="shrink-0 text-xs text-muted-foreground">{formatSize(file.size)}</span>
68
+ <span className="shrink-0 text-[length:var(--font-size-help,0.75rem)] text-muted-foreground">{formatSize(file.size)}</span>
69
69
  </div>
70
70
  {/* Progress bar */}
71
71
  {file.status === "uploading" && file.progress !== undefined && (
@@ -78,7 +78,7 @@ export function UploadProgress({ files, onRemove, onRetry, className }: UploadPr
78
78
  )}
79
79
  {/* Error message */}
80
80
  {file.status === "error" && file.error && (
81
- <p className="mt-0.5 text-xs text-[var(--surface-danger-text)]">{file.error}</p>
81
+ <p className="mt-0.5 text-[length:var(--font-size-help,0.75rem)] text-[var(--surface-danger-text)]">{file.error}</p>
82
82
  )}
83
83
  </div>
84
84