Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions frontend/apps/studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"@rivet-gg/icons": "workspace:*",
"@sentry/react": "^8.26.0",
"@sentry/vite-plugin": "^2.22.2",
"@tanstack/react-query": "^5.76.0",
"@tanstack/react-query-devtools": "^5.76.0",
"@tanstack/react-router": "^1.114.25",
"@tanstack/react-table": "^8.20.6",
"@tanstack/router-devtools": "^1.114.25",
Expand All @@ -43,6 +45,7 @@
"jotai": "^2.12.2",
"jotai-devtools": "^0.11.0",
"jotai-effect": "^2.0.2",
"jotai-tanstack-query": "^0.9.0",
"postcss": "^8.4.38",
"posthog-js": "^1.144.2",
"react": "^19.0.0",
Expand Down
12 changes: 10 additions & 2 deletions frontend/apps/studio/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 26 additions & 10 deletions frontend/apps/studio/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import {
currentActorIdAtom,
pickActorListFilters,
} from "@rivet-gg/components/actors";
import { useAtom } from "jotai";
import { Provider, useAtom } from "jotai";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { queryClient } from "./queries/global";
import { useHydrateAtoms } from "jotai/utils";
import { queryClientAtom } from "jotai-tanstack-query";

declare module "@tanstack/react-router" {
interface Register {
Expand Down Expand Up @@ -60,18 +65,29 @@ function InnerApp() {
return <RouterProvider router={router} />;
}

const HydrateAtoms = ({ children }) => {
useHydrateAtoms(new Map([[queryClientAtom, queryClient]]));
return children;
};

export function App() {
return (
<ConfigProvider value={getConfig()}>
<ThirdPartyProviders>
<Suspense fallback={<FullscreenLoading />}>
<TooltipProvider>
<InnerApp />
</TooltipProvider>
</Suspense>
</ThirdPartyProviders>

<Toaster />
<QueryClientProvider client={queryClient}>
<Provider>
<HydrateAtoms>
<ThirdPartyProviders>
<Suspense fallback={<FullscreenLoading />}>
<TooltipProvider>
<InnerApp />
</TooltipProvider>
</Suspense>
</ThirdPartyProviders>
</HydrateAtoms>
<Toaster />
</Provider>
{/* <ReactQueryDevtools /> */}
</QueryClientProvider>
</ConfigProvider>
);
}
158 changes: 158 additions & 0 deletions frontend/apps/studio/src/components/cell-expanding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import {
type Cell,
type Column,
functionalUpdate,
makeStateUpdater,
type OnChangeFn,
type Row,
type RowData,
type Table,
type TableFeature,
type Updater,
} from "@tanstack/react-table";

export type ExpandedCellState = Record<string, Record<string, boolean>>;
export interface ExpandedCellTableState {
expandedCells: ExpandedCellState;
}
export interface ExpandedCellOptions<TData extends RowData> {
onExpandedCellsChange?: OnChangeFn<ExpandedCellState>;
enableCellExpanding?: boolean;
getCellCanExpand?: (row: Row<TData>, cell: Cell<TData, any>) => boolean;
}
export interface ExpandedCellInstance<TData extends RowData> {
setCellExpanded: (updater: Updater<ExpandedCellState>) => void;
}
export interface ExpandedCellRow<TData extends RowData> {
getIsSomeCellExpanded: () => boolean;
getExpandedCell: () => Cell<unknown, any> | undefined;
}
export interface ExpandedCell<TData, TValue> {
getToggleExpandedHandler: () => () => void;
getIsExpanded: () => boolean;
getCanExpand: () => boolean;
toggleExpanded: (expanded?: boolean) => void;
}

declare module "@tanstack/react-table" {
//merge our new feature's state with the existing table state
interface TableState extends ExpandedCellTableState {}
//merge our new feature's options with the existing table options
interface TableOptionsResolved<TData extends RowData>
extends ExpandedCellOptions<TData> {}
//merge our new feature's instance APIs with the existing table instance APIs
interface Table<TData extends RowData>
extends ExpandedCellInstance<TData> {}
// if you need to add cell instance APIs...
interface Cell<TData, TValue> extends ExpandedCell<TData, TValue> {}
// if you need to add row instance APIs...
interface Row<TData extends RowData> extends ExpandedCellRow<RowData> {}
// if you need to add column instance APIs...
// interface Column<TData extends RowData, TValue> extends DensityColumn
// if you need to add header instance APIs...
// interface Header<TData extends RowData, TValue> extends DensityHeader

// Note: declaration merging on `ColumnDef` is not possible because it is a type, not an interface.
// But you can still use declaration merging on `ColumnDef.meta`
}

// Here is all of the actual javascript code for our new feature
export const CellExpanding: TableFeature<any> = {
// define the new feature's initial state
getInitialState: (state): ExpandedCellTableState => {
return {
expandedCells: {},
...state,
};
},

// define the new feature's default options
getDefaultOptions: <TData extends RowData>(
table: Table<TData>,
): ExpandedCellOptions<TData> => {
return {
enableCellExpanding: true,
onExpandedCellsChange: makeStateUpdater("expandedCells", table),
};
},

createTable: <TData extends RowData>(table: Table<TData>): void => {
table.setCellExpanded = (updater) => {
const safeUpdater: Updater<ExpandedCellState> = (old) => {
const newState = functionalUpdate(updater, old);
return newState;
};
return table.options.onExpandedCellsChange?.(safeUpdater);
};
},

createRow: <TData extends RowData>(
row: Row<TData>,
table: Table<TData>,
): void => {
row.getIsSomeCellExpanded = () => {
const expanded = table.getState().expandedCells;
return !!expanded?.[row.id];
};
row.getExpandedCell = (() => {
const expanded = table.getState().expandedCells;
const cellIds = Object.keys(expanded?.[row.id]);
return row.getAllCells().find((cell) => cellIds.includes(cell.id));
}) as ExpandedCellRow<TData>["getExpandedCell"];
},

createCell: <TData extends RowData, TValue>(
cell: Cell<TData, TValue>,
column: Column<TData>,
row: Row<TData>,
table: Table<TData>,
): void => {
cell.getIsExpanded = () => {
return !!table.getState().expandedCells[row.id]?.[cell.id];
};
cell.toggleExpanded = (expanded) => {
row.toggleExpanded(expanded);
table.setCellExpanded((old) => {
const exists = !!old?.[row.id]?.[cell.id];

const oldExpanded: ExpandedCellState = old;
const newValue = expanded ?? !exists;

if (!exists && newValue) {
return {
...oldExpanded,
[row.id]: {
[cell.id]: true,
},
};
}

if (exists && !newValue) {
return {
...oldExpanded,
[row.id]: {
...(oldExpanded?.[row.id] ?? {}),
[cell.id]: false,
},
};
}

return old;
});
};
cell.getToggleExpandedHandler = () => {
return () => cell.toggleExpanded();
};
cell.getCanExpand = () => {
return (
table.options.getCellCanExpand?.(row, cell) ??
table.options.enableCellExpanding ??
true
);
};
},
// if you need to add column instance APIs...
// createColumn: <TData extends RowData>(column, table): void => {},
// if you need to add header instance APIs...
// createHeader: <TData extends RowData>(header, table): void => {},
};
Loading
Loading