@ramme-io/ui 4.2.2 → 4.2.4

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.
@@ -1,6 +1,10 @@
1
- import { jsx as r } from "react/jsx-runtime";
2
- const s = ({ children: o, onClick: t }) => /* @__PURE__ */ r("button", { onClick: t, className: "px-3 py-1.5 bg-card-alt hover:bg-bg-hover border border-border rounded-full text-sm text-muted-foreground transition-colors", children: o }), a = ({ children: o }) => /* @__PURE__ */ r("div", { className: "flex flex-wrap gap-2 mt-3", children: o });
1
+ import { jsx as t } from "react/jsx-runtime";
2
+ const n = ({ children: e, onClick: r }) => (
3
+ // min-h-11 is the 44px tap target (AGENTS.md Zero Jank rule 8) — these sat at
4
+ // 38px with the open chat panel measured at 360px wide.
5
+ /* @__PURE__ */ t("button", { onClick: r, className: "inline-flex min-h-11 items-center justify-center px-3 bg-card-alt hover:bg-bg-hover border border-border rounded-full text-sm text-muted-foreground transition-colors", children: e })
6
+ ), s = ({ children: e }) => /* @__PURE__ */ t("div", { className: "flex flex-wrap gap-2 mt-3", children: e });
3
7
  export {
4
- a as Actions,
5
- s as Suggestion
8
+ s as Actions,
9
+ n as Suggestion
6
10
  };
@@ -1,18 +1,26 @@
1
1
  import { jsxs as s, jsx as e } from "react/jsx-runtime";
2
2
  import { Icon as m } from "../../components/ui/Icon.js";
3
- const i = ({ value: r, onChange: t, onSubmit: o, placeholder: n = "Type your message..." }) => /* @__PURE__ */ s("form", { onSubmit: o, className: "flex items-center p-2 border-t border-border", children: [
3
+ const l = ({ value: r, onChange: t, onSubmit: n, placeholder: o = "Type your message..." }) => /* @__PURE__ */ s("form", { onSubmit: n, className: "flex items-center p-2 border-t border-border", children: [
4
4
  /* @__PURE__ */ e(
5
5
  "input",
6
6
  {
7
7
  type: "text",
8
- placeholder: n,
9
- className: "flex-1 p-2 bg-transparent focus:outline-none text-foreground",
8
+ placeholder: o,
9
+ className: "flex-1 min-h-11 p-2 bg-transparent focus:outline-none text-foreground",
10
10
  value: r,
11
11
  onChange: t
12
12
  }
13
13
  ),
14
- /* @__PURE__ */ e("button", { type: "submit", className: "p-2 text-primary hover:bg-primary/10 rounded-full transition-colors", children: /* @__PURE__ */ e(m, { name: "send" }) })
14
+ /* @__PURE__ */ e(
15
+ "button",
16
+ {
17
+ type: "submit",
18
+ "aria-label": "Send message",
19
+ className: "-mr-2 inline-flex min-h-11 min-w-11 items-center justify-center text-primary hover:bg-primary/10 rounded-full transition-colors",
20
+ children: /* @__PURE__ */ e(m, { name: "send" })
21
+ }
22
+ )
15
23
  ] });
16
24
  export {
17
- i as PromptInput
25
+ l as PromptInput
18
26
  };
@@ -4,10 +4,15 @@ import { type DataFormat } from '../../lib/formatters';
4
4
  /**
5
5
  * @wizard
6
6
  * @name DataTable
7
- * @description A "Smart" data grid that renders data based on a simple JSON schema.
8
- * @tags data-display, grid, table, smart-component
7
+ * @description A "Smart" data grid that renders data based on a simple JSON schema. Below 768px it renders the same rows as cards instead, derived from the column definitions — no second layout to write. Set mobileCards={false} to keep the grid at every width.
8
+ * @tags data-display, grid, table, smart-component, responsive, mobile
9
9
  * @category data-display
10
10
  * @id data-table
11
+ * @param {ColumnSchema[]} schema - Columns as a JSON schema; an alternative to passing columnDefs.
12
+ * @param {TableAction[]} actions - Per-row icon buttons, pinned right in the grid and shown in the card footer.
13
+ * @param {boolean} mobileCards - Render rows as cards below 768px. Default true.
14
+ * @param {ReactNode} emptyState - Shown instead of the grid or the cards when there are no rows.
15
+ * @param {boolean} loading - Shows a loading overlay in the grid, skeleton cards on a phone.
11
16
  */
12
17
  export interface ColumnSchema {
13
18
  key: string;
@@ -29,5 +34,15 @@ export interface DataTableProps extends AgGridReactProps {
29
34
  actions?: TableAction[];
30
35
  emptyState?: React.ReactNode;
31
36
  loading?: boolean;
37
+ /**
38
+ * Render rows as cards below 768px instead of a horizontally scrolling grid.
39
+ *
40
+ * Defaults to ON. A grid is a horizontal instrument and a phone is not: the
41
+ * risk watchlist declares eight columns with one pinned each side, so at
42
+ * 360px a reader got the two pinned columns and a sliver of the six carrying
43
+ * the actual data. Opting out is for a table that genuinely needs to stay a
44
+ * table — a wide matrix read by comparing rows down a column.
45
+ */
46
+ mobileCards?: boolean;
32
47
  }
33
48
  export declare const DataTable: React.FC<DataTableProps>;
@@ -1,92 +1,104 @@
1
- import { jsx as a } from "react/jsx-runtime";
2
- import { useMemo as c } from "react";
3
- import { AgGridReact as v } from "ag-grid-react";
4
- import { formatValue as N } from "../../lib/formatters.js";
5
- import { Button as w } from "../ui/Button.js";
6
- import { Icon as C } from "../ui/Icon.js";
7
- const j = ({
8
- height: g = "500px",
9
- defaultColDef: d,
10
- schema: n,
11
- actions: r,
12
- columnDefs: o,
13
- emptyState: p,
14
- rowData: s,
15
- loading: m = !1,
16
- ...i
1
+ import { jsx as l } from "react/jsx-runtime";
2
+ import { useMemo as d } from "react";
3
+ import { AgGridReact as w } from "ag-grid-react";
4
+ import { formatValue as x } from "../../lib/formatters.js";
5
+ import { Button as R } from "../ui/Button.js";
6
+ import { Icon as z } from "../ui/Icon.js";
7
+ import { useIsMobile as T } from "../../lib/hooks/useMediaQuery.js";
8
+ import { DataTableCards as k } from "./_DataTableCards.js";
9
+ const O = ({
10
+ height: f = "500px",
11
+ defaultColDef: p,
12
+ schema: m,
13
+ actions: n,
14
+ columnDefs: c,
15
+ emptyState: s,
16
+ rowData: i,
17
+ loading: o = !1,
18
+ mobileCards: h = !0,
19
+ ...t
17
20
  }) => {
18
- const h = c(() => ({
21
+ const b = T(), v = d(() => ({
19
22
  flex: 1,
20
23
  minWidth: 120,
21
24
  resizable: !0,
22
25
  sortable: !0,
23
26
  filter: !0,
24
27
  menuTabs: ["filterMenuTab"],
25
- ...d
26
- }), [d]), f = c(() => {
27
- if (o) return o;
28
- if (!n) return [];
29
- const l = n.map((e) => {
30
- const t = e.type === "number" || e.type === "currency" || e.type === "percent" || e.type === "delta";
28
+ ...p
29
+ }), [p]), g = d(() => {
30
+ if (c) return c;
31
+ if (!m) return [];
32
+ const r = m.map((e) => {
33
+ const a = e.type === "number" || e.type === "currency" || e.type === "percent" || e.type === "delta";
31
34
  return {
32
35
  field: e.key,
33
36
  headerName: e.label,
34
37
  hide: e.hide,
35
38
  width: e.width,
36
- cellClass: t ? "text-right tabular-nums" : void 0,
37
- headerClass: t ? "ag-right-aligned-header" : void 0,
38
- cellRenderer: (u) => N(u.value, e.type),
39
- filter: e.type === "date" ? "agDateColumnFilter" : t ? "agNumberColumnFilter" : "agTextColumnFilter"
39
+ cellClass: a ? "text-right tabular-nums" : void 0,
40
+ headerClass: a ? "ag-right-aligned-header" : void 0,
41
+ cellRenderer: (u) => x(u.value, e.type),
42
+ filter: e.type === "date" ? "agDateColumnFilter" : a ? "agNumberColumnFilter" : "agTextColumnFilter"
40
43
  };
41
44
  });
42
- return r && r.length > 0 && l.push({
45
+ return n && n.length > 0 && r.push({
43
46
  headerName: "",
44
47
  field: "actions",
45
48
  pinned: "right",
46
- width: 80 + r.length * 40,
49
+ width: 80 + n.length * 40,
47
50
  sortable: !1,
48
51
  filter: !1,
49
52
  resizable: !1,
50
- cellRenderer: (e) => /* @__PURE__ */ a("div", { className: "flex items-center justify-end gap-1 h-full", children: r.map((t) => /* @__PURE__ */ a(
51
- w,
53
+ cellRenderer: (e) => /* @__PURE__ */ l("div", { className: "flex items-center justify-end gap-1 h-full", children: n.map((a) => /* @__PURE__ */ l(
54
+ R,
52
55
  {
53
- variant: t.variant || "ghost",
56
+ variant: a.variant || "ghost",
54
57
  size: "icon",
55
58
  className: "h-7 w-7",
56
- title: t.label,
59
+ title: a.label,
57
60
  onClick: (u) => {
58
- u.stopPropagation(), t.onClick(e.data);
61
+ u.stopPropagation(), a.onClick(e.data);
59
62
  },
60
- children: /* @__PURE__ */ a(C, { name: t.icon, className: "h-4 w-4" })
63
+ children: /* @__PURE__ */ l(z, { name: a.icon, className: "h-4 w-4" })
61
64
  },
62
- t.id
65
+ a.id
63
66
  )) })
64
- }), l;
65
- }, [n, o, r]), b = "ag-theme-quartz ag-theme-ramme", y = c(() => ({
67
+ }), r;
68
+ }, [m, c, n]), y = "ag-theme-quartz ag-theme-ramme", C = d(() => ({
66
69
  overlayLoadingTemplate: '<span class="ag-overlay-loading-center">Loading data...</span>',
67
70
  overlayNoRowsTemplate: '<span class="ag-overlay-no-rows-center">No data available</span>'
68
- }), []);
69
- return /* @__PURE__ */ a("div", { className: `relative ${b}`, style: { height: g, width: "100%" }, children: (!s || s.length === 0) && p && !m ? /* @__PURE__ */ a("div", { className: "absolute inset-0 z-10 bg-background/50 flex items-center justify-center", children: p }) : /* @__PURE__ */ a(
70
- v,
71
+ }), []), N = (!i || i.length === 0) && !o;
72
+ return h && b ? /* @__PURE__ */ l("div", { className: "w-full", children: N && s ? /* @__PURE__ */ l("div", { className: "flex items-center justify-center py-fluid-lg", children: s }) : /* @__PURE__ */ l(
73
+ k,
71
74
  {
72
- rowData: m ? [] : s,
73
- columnDefs: f,
74
- defaultColDef: h,
75
+ rowData: i,
76
+ columnDefs: g,
77
+ loading: o,
78
+ pageSize: t.pagination === !1 ? !1 : t.paginationPageSize ?? 20,
79
+ onRowClick: t.onRowClicked ? (r) => t.onRowClicked({ data: r }) : void 0
80
+ }
81
+ ) }) : /* @__PURE__ */ l("div", { className: `relative ${y}`, style: { height: f, width: "100%" }, children: (!i || i.length === 0) && s && !o ? /* @__PURE__ */ l("div", { className: "absolute inset-0 z-10 bg-background/50 flex items-center justify-center", children: s }) : /* @__PURE__ */ l(
82
+ w,
83
+ {
84
+ rowData: o ? [] : i,
85
+ columnDefs: g,
86
+ defaultColDef: v,
75
87
  rowSelection: "multiple",
76
88
  animateRows: !0,
77
89
  pagination: !0,
78
90
  paginationPageSize: 20,
79
91
  enableCellTextSelection: !0,
80
92
  suppressCellFocus: !0,
81
- gridOptions: y,
82
- onGridReady: (l) => {
93
+ gridOptions: C,
94
+ onGridReady: (r) => {
83
95
  var e;
84
- m && l.api.showLoadingOverlay(), (e = i.onGridReady) == null || e.call(i, l);
96
+ o && r.api.showLoadingOverlay(), (e = t.onGridReady) == null || e.call(t, r);
85
97
  },
86
- ...i
98
+ ...t
87
99
  }
88
100
  ) });
89
101
  };
90
102
  export {
91
- j as DataTable
103
+ O as DataTable
92
104
  };
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { type ColDef } from 'ag-grid-community';
3
+ export interface DataTableCardsProps {
4
+ rowData: any[] | null | undefined;
5
+ columnDefs: ColDef[];
6
+ loading?: boolean;
7
+ /** 0 or false disables card pagination; the grid's page size otherwise. */
8
+ pageSize?: number | false;
9
+ onRowClick?: (row: any) => void;
10
+ }
11
+ interface Slots {
12
+ heading: ColDef[];
13
+ body: ColDef[];
14
+ footer: ColDef[];
15
+ }
16
+ /**
17
+ * Split the columns into the card's three zones.
18
+ *
19
+ * Exported for the test, which is the only way to assert the mapping without
20
+ * mounting AG Grid.
21
+ */
22
+ export declare function splitColumns(columnDefs: ColDef[]): Slots;
23
+ /**
24
+ * Apply the grid's declared initial sort.
25
+ *
26
+ * Order is information here: the watchlist opens on risk ascending so the
27
+ * zombies lead, and a phone showing the same rows in insertion order would be
28
+ * a different screen, not a narrower one. `sortIndex` orders a multi-column
29
+ * sort; AG Grid treats a missing one as "after those that have it".
30
+ */
31
+ export declare function applyDeclaredSort(rows: any[], columnDefs: ColDef[]): any[];
32
+ export declare const DataTableCards: React.FC<DataTableCardsProps>;
33
+ export {};
@@ -0,0 +1,153 @@
1
+ import { jsx as o, jsxs as g } from "react/jsx-runtime";
2
+ import f from "react";
3
+ import { Card as N, CardHeader as E, CardContent as F, CardFooter as I } from "../layout/Card.js";
4
+ import { Pagination as M } from "../navigation/Pagination.js";
5
+ import { Skeleton as x } from "../feedback/Skeleton.js";
6
+ function y(n, r) {
7
+ if (!(!n || !r))
8
+ return r.includes(".") ? r.split(".").reduce((t, e) => t == null ? t : t[e], n) : n[r];
9
+ }
10
+ function P(n) {
11
+ return (n.colId ?? n.field ?? "").toLowerCase() === "actions";
12
+ }
13
+ function j(n) {
14
+ const r = n.filter((e) => !e.hide), t = { heading: [], body: [], footer: [] };
15
+ for (const e of r)
16
+ e.pinned === "left" || e.pinned === !0 ? t.heading.push(e) : e.pinned === "right" || P(e) ? t.footer.push(e) : t.body.push(e);
17
+ return t.heading.length === 0 && t.body.length > 0 && t.heading.push(t.body.shift()), t;
18
+ }
19
+ function k(n, r) {
20
+ const t = r.filter((s) => s.sort === "asc" || s.sort === "desc").sort((s, l) => (s.sortIndex ?? Number.MAX_SAFE_INTEGER) - (l.sortIndex ?? Number.MAX_SAFE_INTEGER));
21
+ if (t.length === 0) return n;
22
+ const e = [...n];
23
+ return e.sort((s, l) => {
24
+ for (const d of t) {
25
+ const c = y(s, d.field), u = y(l, d.field);
26
+ let i;
27
+ if (typeof d.comparator == "function")
28
+ i = d.comparator(c, u, null, null, !1) ?? 0;
29
+ else if (c == null && u == null) i = 0;
30
+ else {
31
+ if (c == null) return 1;
32
+ if (u == null) return -1;
33
+ typeof c == "number" && typeof u == "number" ? i = c - u : i = String(c).localeCompare(String(u), void 0, { numeric: !0 });
34
+ }
35
+ if (i !== 0) return d.sort === "desc" ? -i : i;
36
+ }
37
+ return 0;
38
+ }), e;
39
+ }
40
+ function C(n, r) {
41
+ const t = y(r, n.field);
42
+ let e = null;
43
+ try {
44
+ e = n.valueFormatter ? n.valueFormatter({ value: t, data: r, colDef: n, node: null }) : null;
45
+ } catch {
46
+ e = null;
47
+ }
48
+ return {
49
+ value: t,
50
+ valueFormatted: e,
51
+ data: r,
52
+ colDef: n,
53
+ node: null
54
+ };
55
+ }
56
+ function b(n, r) {
57
+ const t = C(n, r), e = t.valueFormatted ?? (t.value == null ? "—" : String(t.value)), s = n.cellRenderer;
58
+ if (typeof s == "function")
59
+ try {
60
+ const l = s(t);
61
+ if (l === null || typeof l == "string" || typeof l == "number" || f.isValidElement(l)) return l;
62
+ } catch {
63
+ }
64
+ return e;
65
+ }
66
+ function _(n, r) {
67
+ const t = n.cellClass;
68
+ let e;
69
+ if (typeof t == "function")
70
+ try {
71
+ e = t(C(n, r));
72
+ } catch {
73
+ e = void 0;
74
+ }
75
+ else
76
+ e = t;
77
+ const s = (Array.isArray(e) ? e : e ? [e] : []).flatMap((d) => String(d).split(/\s+/)), l = /* @__PURE__ */ new Set(["text-right", "ag-right-aligned-cell"]);
78
+ return s.filter((d) => d && !l.has(d)).join(" ");
79
+ }
80
+ const R = ({ count: n }) => /* @__PURE__ */ o("div", { className: "flex flex-col gap-3", children: Array.from({ length: n }, (r, t) => /* @__PURE__ */ g(N, { className: "p-4", children: [
81
+ /* @__PURE__ */ o(x, { className: "h-4 w-24" }),
82
+ /* @__PURE__ */ o("div", { className: "mt-3 grid grid-cols-2 gap-2", children: Array.from({ length: 4 }, (e, s) => /* @__PURE__ */ o(x, { className: "h-3 w-full" }, s)) })
83
+ ] }, t)) }), D = ({
84
+ rowData: n,
85
+ columnDefs: r,
86
+ loading: t = !1,
87
+ pageSize: e = !1,
88
+ onRowClick: s
89
+ }) => {
90
+ const [l, d] = f.useState(1), c = f.useMemo(() => j(r), [r]), u = f.useMemo(
91
+ () => k(n ?? [], r),
92
+ [n, r]
93
+ ), i = typeof e == "number" && e > 0 ? e : 0, m = i ? Math.max(1, Math.ceil(u.length / i)) : 1;
94
+ f.useEffect(() => {
95
+ l > m && d(m);
96
+ }, [l, m]);
97
+ const A = i ? u.slice((l - 1) * i, l * i) : u;
98
+ return t ? /* @__PURE__ */ o(R, { count: Math.min(i || 3, 5) }) : /* @__PURE__ */ g("div", { className: "flex flex-col gap-3", children: [
99
+ A.map((p, S) => {
100
+ const v = !!s;
101
+ return /* @__PURE__ */ g(
102
+ N,
103
+ {
104
+ className: [
105
+ "overflow-hidden",
106
+ v ? "cursor-pointer active:bg-surface-muted" : ""
107
+ ].filter(Boolean).join(" "),
108
+ onClick: v ? () => s(p) : void 0,
109
+ children: [
110
+ c.heading.length > 0 && /* @__PURE__ */ o(E, { className: "flex-row items-center gap-3 p-4 pb-0", children: c.heading.map((a, h) => /* @__PURE__ */ o("div", { className: "min-w-0 flex-1 truncate", children: b(a, p) }, a.colId ?? a.field ?? h)) }),
111
+ c.body.length > 0 && /* @__PURE__ */ o(F, { className: "p-4 pt-3", children: /* @__PURE__ */ o("dl", { className: "grid grid-cols-2 gap-x-4 gap-y-2", children: c.body.map((a, h) => /* @__PURE__ */ g(
112
+ "div",
113
+ {
114
+ className: "flex min-w-0 items-baseline justify-between gap-2",
115
+ children: [
116
+ /* @__PURE__ */ o("dt", { className: "shrink-0 text-xs uppercase tracking-wider text-muted-foreground", children: a.headerName ?? a.field }),
117
+ /* @__PURE__ */ o(
118
+ "dd",
119
+ {
120
+ className: [
121
+ "min-w-0 truncate text-sm tabular-nums",
122
+ _(a, p)
123
+ ].filter(Boolean).join(" "),
124
+ children: b(a, p)
125
+ }
126
+ )
127
+ ]
128
+ },
129
+ a.colId ?? a.field ?? h
130
+ )) }) }),
131
+ c.footer.length > 0 && // min-h-11 is the 44px AAA tap target from AGENTS.md: the footer
132
+ // is where the controls live and a phone is what taps them.
133
+ /* @__PURE__ */ o(
134
+ I,
135
+ {
136
+ className: "flex-row items-center justify-end gap-1 border-t border-border p-2 min-h-11",
137
+ onClick: (a) => a.stopPropagation(),
138
+ children: c.footer.map((a, h) => /* @__PURE__ */ o(f.Fragment, { children: b(a, p) }, a.colId ?? a.field ?? h))
139
+ }
140
+ )
141
+ ]
142
+ },
143
+ S
144
+ );
145
+ }),
146
+ m > 1 && /* @__PURE__ */ o(M, { currentPage: l, totalPages: m, onPageChange: d })
147
+ ] });
148
+ };
149
+ export {
150
+ D as DataTableCards,
151
+ k as applyDeclaredSort,
152
+ j as splitColumns
153
+ };
@@ -50,7 +50,7 @@ const S = ({ status: a }) => {
50
50
  className: l("w-full flex items-center gap-3 p-4 text-left focus:outline-none", !e.description && "cursor-default"),
51
51
  children: [
52
52
  /* @__PURE__ */ r("div", { className: "shrink-0 pt-0.5", children: /* @__PURE__ */ r(S, { status: e.status }) }),
53
- /* @__PURE__ */ r("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ r("p", { className: l("font-medium truncate", s && "text-muted-foreground line-through"), children: e.label }) }),
53
+ /* @__PURE__ */ r("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ r("p", { className: l("font-medium", s && "text-muted-foreground line-through"), children: e.label }) }),
54
54
  e.description && /* @__PURE__ */ r(x, { className: l("w-4 h-4 text-muted-foreground transition-transform", m[e.id] && "rotate-180") })
55
55
  ]
56
56
  }
@@ -1,10 +1,10 @@
1
1
  import { jsxs as d, jsx as r } from "react/jsx-runtime";
2
- import s from "react";
2
+ import m from "react";
3
3
  import b from "react-datepicker";
4
4
  import { cn as e } from "../../utils/cn.js";
5
5
  import { Icon as u } from "../ui/Icon.js";
6
6
  import "react-datepicker/dist/react-datepicker.css";
7
- const f = `
7
+ const g = `
8
8
  .react-datepicker {
9
9
  font-family: inherit !important;
10
10
  background-color: rgb(var(--app-card-bg-color)) !important;
@@ -38,9 +38,9 @@ const f = `
38
38
  helperText: a,
39
39
  ...c
40
40
  }) => {
41
- const m = s.useId(), i = c.id || m;
41
+ const s = m.useId(), i = c.id || s;
42
42
  return /* @__PURE__ */ d("div", { className: e("w-full flex flex-col", p), children: [
43
- /* @__PURE__ */ r("style", { children: f }),
43
+ /* @__PURE__ */ r("style", { children: g }),
44
44
  o && /* @__PURE__ */ r("label", { htmlFor: i, className: "block text-sm font-medium text-foreground mb-1.5", children: o }),
45
45
  /* @__PURE__ */ d("div", { className: "relative", children: [
46
46
  /* @__PURE__ */ r(
@@ -50,7 +50,21 @@ const f = `
50
50
  selected: l,
51
51
  onChange: n,
52
52
  className: e(
53
- "flex h-10 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
53
+ // No text size on the control. react-datepicker forwards this
54
+ // className straight to its own <input>, so a `text-sm` here sits
55
+ // on a real focus target and out-ranks the floor in index.css
56
+ // exactly as one written inline would — 14.5px, and iOS Safari
57
+ // zooms in on focus and never zooms back out.
58
+ //
59
+ // It was copied from Input.tsx before Input had its own `text-sm`
60
+ // removed, so that fix never reached here. The `file:` rules came
61
+ // along in the same copy and mean nothing on a date field; they are
62
+ // dropped so a future search for `file:text-sm` finds only the one
63
+ // place it is deliberate.
64
+ //
65
+ // A source scan cannot catch this one — there is no <input> tag to
66
+ // find — which is why formFieldFloor.test.tsx also renders the atoms.
67
+ "flex h-10 w-full rounded-md border bg-background px-3 py-2 ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
54
68
  t ? "border-destructive" : "border-input"
55
69
  ),
56
70
  wrapperClassName: "w-full",
@@ -77,7 +77,7 @@ const C = {
77
77
  placeholder: "Search components...",
78
78
  value: l,
79
79
  onChange: (e) => u(e.target.value),
80
- className: "w-full rounded-md bg-muted/50 border border-border pl-7 pr-3 py-1.5 text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary/60 transition-shadow"
80
+ className: "w-full rounded-md bg-muted/50 border border-border pl-7 pr-3 py-1.5 placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary/60 transition-shadow"
81
81
  }
82
82
  ),
83
83
  l && /* @__PURE__ */ o(
@@ -6,8 +6,8 @@ import { Button as v } from "../ui/Button.js";
6
6
  import { Icon as l } from "../ui/Icon.js";
7
7
  const S = ({
8
8
  headingFont: i,
9
- setHeadingFont: t,
10
- bodyFont: c,
9
+ setHeadingFont: c,
10
+ bodyFont: t,
11
11
  setBodyFont: s,
12
12
  borderRadius: d,
13
13
  setBorderRadius: u,
@@ -33,8 +33,8 @@ const S = ({
33
33
  "select",
34
34
  {
35
35
  value: i,
36
- onChange: (n) => t(n.target.value),
37
- className: "w-full border border-input bg-background rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-primary focus:outline-none",
36
+ onChange: (n) => c(n.target.value),
37
+ className: "w-full border border-input bg-background rounded-md px-3 py-2 focus:ring-2 focus:ring-primary focus:outline-none",
38
38
  children: [
39
39
  /* @__PURE__ */ e("option", { value: "Inter", children: "Inter" }),
40
40
  /* @__PURE__ */ e("option", { value: "Orbitron", children: "Orbitron" }),
@@ -53,9 +53,9 @@ const S = ({
53
53
  /* @__PURE__ */ r(
54
54
  "select",
55
55
  {
56
- value: c,
56
+ value: t,
57
57
  onChange: (n) => s(n.target.value),
58
- className: "w-full border border-input bg-background rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-primary focus:outline-none",
58
+ className: "w-full border border-input bg-background rounded-md px-3 py-2 focus:ring-2 focus:ring-primary focus:outline-none",
59
59
  children: [
60
60
  /* @__PURE__ */ e("option", { value: "Inter", children: "Inter" }),
61
61
  /* @__PURE__ */ e("option", { value: "Rajdhani", children: "Rajdhani" }),
@@ -73,7 +73,7 @@ const S = ({
73
73
  {
74
74
  value: d,
75
75
  onChange: (n) => u(n.target.value),
76
- className: "w-full border border-input bg-background rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-primary focus:outline-none",
76
+ className: "w-full border border-input bg-background rounded-md px-3 py-2 focus:ring-2 focus:ring-primary focus:outline-none",
77
77
  children: [
78
78
  /* @__PURE__ */ e("option", { value: "0rem", children: "0rem (Sharp)" }),
79
79
  /* @__PURE__ */ e("option", { value: "0.25rem", children: "0.25rem (Sm)" }),
@@ -92,7 +92,7 @@ const S = ({
92
92
  {
93
93
  value: m,
94
94
  onChange: (n) => p(n.target.value),
95
- className: "w-full border border-input bg-background rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-primary focus:outline-none",
95
+ className: "w-full border border-input bg-background rounded-md px-3 py-2 focus:ring-2 focus:ring-primary focus:outline-none",
96
96
  children: [
97
97
  /* @__PURE__ */ e("option", { value: "0.875rem", children: "0.875rem (Small)" }),
98
98
  /* @__PURE__ */ e("option", { value: "1rem", children: "1rem (Base)" }),