@particle-academy/react-fancy 3.2.1 → 3.3.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.
- package/README.md +14 -7
- package/dist/index.cjs +122 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +122 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @particle-academy/react-fancy
|
|
2
2
|
|
|
3
|
-
React UI component library —
|
|
3
|
+
React UI component library for **Human+ UX** — controlled, agent-bridgeable primitives that humans and agents share. Every component exposes `value` / `onChange`, stable handles, and JSON-friendly props so MCP bridges can drive the UI without DOM scraping.
|
|
4
4
|
|
|
5
5
|
## Inertia.js integration
|
|
6
6
|
|
|
@@ -240,7 +240,7 @@ src/
|
|
|
240
240
|
|
|
241
241
|
- `Size` — `"xs" | "sm" | "md" | "lg" | "xl"`
|
|
242
242
|
- `Color` — Full Tailwind color palette (17 colors)
|
|
243
|
-
- `ActionColor` — Subset of 10 standalone colors
|
|
243
|
+
- `ActionColor` — Subset of 10 standalone colors used by Action and friends
|
|
244
244
|
- `Variant` — `"solid" | "outline" | "ghost" | "soft"`
|
|
245
245
|
- `Placement` — `"top" | "bottom" | "left" | "right"` + start/end variants
|
|
246
246
|
|
|
@@ -268,16 +268,23 @@ Every component follows this structure:
|
|
|
268
268
|
|
|
269
269
|
Use `lucide-react` as the default icon library. It is a dependency of this package and marked as external in tsup. Components should import icons directly (e.g., `import { X, ChevronDown } from "lucide-react"`).
|
|
270
270
|
|
|
271
|
-
###
|
|
271
|
+
### Human+ UX contract
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
-
|
|
273
|
+
Every interactive component must:
|
|
274
|
+
|
|
275
|
+
- Expose **`value` + `onChange`** so an agent can read and write state. No internal-only state for anything an agent might want to inspect or change.
|
|
276
|
+
- Carry **stable handles** — `id`, `data-react-fancy-*` attributes, or a selector prop — so MCP bridges can address elements without guessing DOM structure.
|
|
277
|
+
- Accept **JSON-friendly props** — arrays of objects, primitives, simple discriminated unions. Avoid forcing React children for things the agent needs to populate.
|
|
278
|
+
- Support a **bridgeable surface**: a `register<Surface>Bridge(server, { adapter })` (in `agent-integrations`) should be sketchable in one sitting.
|
|
279
|
+
- Broadcast **`AgentActivity`** events for mutations so presence, undo, and coaching layers can compose.
|
|
280
|
+
- Provide **trust-but-verify** hooks for destructive actions — agents propose, humans confirm via `pendingMode` / staged-write affordances.
|
|
281
|
+
|
|
282
|
+
Purely visual primitives (labels, dividers, layout shells) only owe the first bullet.
|
|
276
283
|
|
|
277
284
|
### Styling
|
|
278
285
|
|
|
279
286
|
- **Tailwind v4** — CSS-first config. Use `@import "tailwindcss"` not `@tailwind` directives.
|
|
280
|
-
- **Dark mode** — Every color variant must include `dark:` equivalents.
|
|
287
|
+
- **Dark mode** — Every color variant must include `dark:` equivalents. Portal components get dark mode automatically via the Portal wrapper.
|
|
281
288
|
- **No component library deps** — Only `clsx`, `tailwind-merge`, and `lucide-react`. Don't add Radix, Headless UI, or similar.
|
|
282
289
|
- Class maps should be `Record<Size, string>` (or similar) constants outside the component function, not inline.
|
|
283
290
|
|
package/dist/index.cjs
CHANGED
|
@@ -6673,6 +6673,127 @@ var Timeline = Object.assign(TimelineRoot, {
|
|
|
6673
6673
|
Item: TimelineItem,
|
|
6674
6674
|
Block: TimelineBlock
|
|
6675
6675
|
});
|
|
6676
|
+
var TONE_CLASSES = {
|
|
6677
|
+
violet: "bg-violet-500 hover:bg-violet-600",
|
|
6678
|
+
emerald: "bg-emerald-500 hover:bg-emerald-600",
|
|
6679
|
+
sky: "bg-sky-500 hover:bg-sky-600",
|
|
6680
|
+
rose: "bg-rose-500 hover:bg-rose-600",
|
|
6681
|
+
amber: "bg-amber-500 hover:bg-amber-600",
|
|
6682
|
+
indigo: "bg-indigo-500 hover:bg-indigo-600",
|
|
6683
|
+
blue: "bg-blue-500 hover:bg-blue-600",
|
|
6684
|
+
zinc: "bg-zinc-500 hover:bg-zinc-600"
|
|
6685
|
+
};
|
|
6686
|
+
var OFF_CLASSES = "bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700";
|
|
6687
|
+
var TimeGrid = react.forwardRef(
|
|
6688
|
+
({
|
|
6689
|
+
rows,
|
|
6690
|
+
cols,
|
|
6691
|
+
value,
|
|
6692
|
+
onChange,
|
|
6693
|
+
toneOn = "violet",
|
|
6694
|
+
cellWidth = 20,
|
|
6695
|
+
cellHeight = 16,
|
|
6696
|
+
sparseColLabels = true,
|
|
6697
|
+
toggleStripsOnHeaderClick = true,
|
|
6698
|
+
ariaCell,
|
|
6699
|
+
cellId,
|
|
6700
|
+
className
|
|
6701
|
+
}, ref) => {
|
|
6702
|
+
const [drag, setDrag] = react.useState(null);
|
|
6703
|
+
const setCell = (r, c, v) => {
|
|
6704
|
+
if (value[r]?.[c] === v) return;
|
|
6705
|
+
const next = value.map((row) => row.slice());
|
|
6706
|
+
next[r][c] = v;
|
|
6707
|
+
onChange(next);
|
|
6708
|
+
};
|
|
6709
|
+
const toggleRow = (r) => {
|
|
6710
|
+
if (!toggleStripsOnHeaderClick) return;
|
|
6711
|
+
const all = value[r].every(Boolean);
|
|
6712
|
+
const next = value.map((row) => row.slice());
|
|
6713
|
+
for (let c = 0; c < cols.length; c++) next[r][c] = !all;
|
|
6714
|
+
onChange(next);
|
|
6715
|
+
};
|
|
6716
|
+
const toggleCol = (c) => {
|
|
6717
|
+
if (!toggleStripsOnHeaderClick) return;
|
|
6718
|
+
const all = value.every((row) => row[c]);
|
|
6719
|
+
const next = value.map((row) => row.slice());
|
|
6720
|
+
for (let r = 0; r < rows.length; r++) next[r][c] = !all;
|
|
6721
|
+
onChange(next);
|
|
6722
|
+
};
|
|
6723
|
+
const colStep = Math.max(1, Math.floor(cols.length / 6));
|
|
6724
|
+
const onTone = TONE_CLASSES[toneOn];
|
|
6725
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6726
|
+
"div",
|
|
6727
|
+
{
|
|
6728
|
+
ref,
|
|
6729
|
+
"data-react-fancy-timegrid": "",
|
|
6730
|
+
className: cn(
|
|
6731
|
+
"select-none overflow-x-auto",
|
|
6732
|
+
className
|
|
6733
|
+
),
|
|
6734
|
+
onMouseLeave: () => setDrag(null),
|
|
6735
|
+
onMouseUp: () => setDrag(null),
|
|
6736
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "text-[10px]", children: [
|
|
6737
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
6738
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", {}),
|
|
6739
|
+
cols.map((label, c) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6740
|
+
"th",
|
|
6741
|
+
{
|
|
6742
|
+
onClick: () => toggleCol(c),
|
|
6743
|
+
style: { width: cellWidth },
|
|
6744
|
+
"data-react-fancy-timegrid-col": c,
|
|
6745
|
+
className: cn(
|
|
6746
|
+
"text-zinc-400",
|
|
6747
|
+
toggleStripsOnHeaderClick && "cursor-pointer hover:text-zinc-700 dark:hover:text-zinc-200"
|
|
6748
|
+
),
|
|
6749
|
+
children: sparseColLabels ? c % colStep === 0 ? label : "" : label
|
|
6750
|
+
},
|
|
6751
|
+
c
|
|
6752
|
+
))
|
|
6753
|
+
] }) }),
|
|
6754
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((label, r) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
6755
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6756
|
+
"th",
|
|
6757
|
+
{
|
|
6758
|
+
onClick: () => toggleRow(r),
|
|
6759
|
+
"data-react-fancy-timegrid-row": r,
|
|
6760
|
+
className: cn(
|
|
6761
|
+
"pr-1 text-right font-medium text-zinc-500",
|
|
6762
|
+
toggleStripsOnHeaderClick && "cursor-pointer hover:text-zinc-800 dark:hover:text-zinc-200"
|
|
6763
|
+
),
|
|
6764
|
+
children: label
|
|
6765
|
+
}
|
|
6766
|
+
),
|
|
6767
|
+
cols.map((_, c) => {
|
|
6768
|
+
const on = value[r]?.[c] ?? false;
|
|
6769
|
+
return /* @__PURE__ */ jsxRuntime.jsx("td", { className: "p-px", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6770
|
+
"button",
|
|
6771
|
+
{
|
|
6772
|
+
type: "button",
|
|
6773
|
+
onMouseDown: () => {
|
|
6774
|
+
const v = !on;
|
|
6775
|
+
setDrag(v);
|
|
6776
|
+
setCell(r, c, v);
|
|
6777
|
+
},
|
|
6778
|
+
onMouseEnter: () => drag !== null && setCell(r, c, drag),
|
|
6779
|
+
style: { width: cellWidth, height: cellHeight },
|
|
6780
|
+
"data-react-fancy-timegrid-cell": cellId ? cellId(r, c) : `${r}:${c}`,
|
|
6781
|
+
"aria-pressed": on,
|
|
6782
|
+
"aria-label": ariaCell ? ariaCell(r, c, on) : `${rows[r]} ${cols[c]} ${on ? "on" : "off"}`,
|
|
6783
|
+
className: cn(
|
|
6784
|
+
"block rounded-sm transition",
|
|
6785
|
+
on ? onTone : OFF_CLASSES
|
|
6786
|
+
)
|
|
6787
|
+
}
|
|
6788
|
+
) }, c);
|
|
6789
|
+
})
|
|
6790
|
+
] }, r)) })
|
|
6791
|
+
] })
|
|
6792
|
+
}
|
|
6793
|
+
);
|
|
6794
|
+
}
|
|
6795
|
+
);
|
|
6796
|
+
TimeGrid.displayName = "TimeGrid";
|
|
6676
6797
|
var Tooltip = react.forwardRef(
|
|
6677
6798
|
function Tooltip2({ children, content, placement = "top", delay = 200, offset = 8, className }, _ref) {
|
|
6678
6799
|
const [open, setOpen] = react.useState(false);
|
|
@@ -13179,6 +13300,7 @@ exports.Table = Table;
|
|
|
13179
13300
|
exports.Tabs = Tabs;
|
|
13180
13301
|
exports.Text = Text;
|
|
13181
13302
|
exports.Textarea = Textarea;
|
|
13303
|
+
exports.TimeGrid = TimeGrid;
|
|
13182
13304
|
exports.TimePicker = TimePicker;
|
|
13183
13305
|
exports.Timeline = Timeline;
|
|
13184
13306
|
exports.Toast = Toast;
|