Simplify editable cell signature
Signed-off-by: Jonathan <contact@grafikart.fr>
This commit is contained in:
137
apps/console/src/components/table/GraphQLCell.tsx
Normal file
137
apps/console/src/components/table/GraphQLCell.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import { type ReactNode, Suspense } from "react";
|
||||
import { useEditableCellRef } from "@probo/ui/src/Molecules/Table/EditableCell.tsx";
|
||||
import { getKey } from "@probo/ui/src/Molecules/Table/utils.ts";
|
||||
import { useStateWithRef } from "@probo/hooks";
|
||||
import { useEditableRowContext } from "@probo/ui/src/Molecules/Table/EditableRow.tsx";
|
||||
import { EditableCell, selectCell, SelectValue, Spinner } from "@probo/ui";
|
||||
import { Command } from "cmdk";
|
||||
import type {
|
||||
GraphQLTaggedNode,
|
||||
OperationType,
|
||||
VariablesOf,
|
||||
} from "relay-runtime";
|
||||
import { useLazyLoadQuery } from "react-relay";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
|
||||
type Props<Q extends OperationType, T> = {
|
||||
name: string;
|
||||
query: GraphQLTaggedNode;
|
||||
variables: VariablesOf<Q>;
|
||||
items: (v: ReturnType<typeof useLazyLoadQuery<Q>>) => T[];
|
||||
itemRenderer: (v: { item: T; onRemove?: (item: T) => void }) => ReactNode;
|
||||
} & (
|
||||
| { defaultValue?: T; multiple?: undefined }
|
||||
| { defaultValue: T[]; multiple: true }
|
||||
);
|
||||
|
||||
export function GraphQLCell<Q extends OperationType, T>(props: Props<Q, T>) {
|
||||
const [value, setValue, valueRef] = useStateWithRef<T | T[] | undefined>(
|
||||
props.defaultValue,
|
||||
);
|
||||
const cellRef = useEditableCellRef();
|
||||
const { __ } = useTranslate();
|
||||
const usedKeys = new Set<string>(
|
||||
Array.isArray(value) ? value.map(getKey) : [getKey(value)],
|
||||
);
|
||||
const { onUpdate } = useEditableRowContext();
|
||||
|
||||
const onSelect = (item: T) => {
|
||||
if (props.multiple) {
|
||||
setValue([...((valueRef.current as T[]) ?? []), item]);
|
||||
return;
|
||||
}
|
||||
setValue(item);
|
||||
cellRef.current?.close();
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
if (valueRef.current === props.defaultValue) {
|
||||
return;
|
||||
}
|
||||
// Only send ids when updating the value
|
||||
onUpdate(
|
||||
props.name,
|
||||
Array.isArray(valueRef.current)
|
||||
? valueRef.current.map(getKey)
|
||||
: getKey(valueRef.current),
|
||||
);
|
||||
};
|
||||
|
||||
const classNames = selectCell();
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
name={props.name}
|
||||
label={<SelectValue value={value} itemRenderer={props.itemRenderer} />}
|
||||
onClose={onClose}
|
||||
ref={cellRef}
|
||||
>
|
||||
<Command className={classNames.command()}>
|
||||
<div
|
||||
className={classNames.value()}
|
||||
style={{
|
||||
paddingLeft: "var(--padding)",
|
||||
minHeight: "var(--height)",
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
<SelectValue
|
||||
onValueChange={setValue}
|
||||
value={value}
|
||||
itemRenderer={props.itemRenderer}
|
||||
/>
|
||||
</div>{" "}
|
||||
{props.multiple && (
|
||||
<Command.Input
|
||||
className={classNames.input()}
|
||||
placeholder={__("Search")}
|
||||
/>
|
||||
)}
|
||||
<Command.List>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="py-2 px-3 flex items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<ItemList
|
||||
{...props}
|
||||
usedKeys={usedKeys}
|
||||
className={classNames.item()}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</Suspense>
|
||||
</Command.List>
|
||||
</Command>
|
||||
</EditableCell>
|
||||
);
|
||||
}
|
||||
|
||||
function ItemList<Q extends OperationType, T>(
|
||||
props: Props<Q, T> & {
|
||||
className: string;
|
||||
onSelect: (item: T) => void;
|
||||
usedKeys: Set<string>;
|
||||
},
|
||||
) {
|
||||
const data = useLazyLoadQuery<Q>(props.query, props.variables, {
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
const items = props.items(data);
|
||||
return (
|
||||
<>
|
||||
{items
|
||||
.filter((item) => !props.usedKeys.has(getKey(item)))
|
||||
.map((item) => (
|
||||
<Command.Item
|
||||
key={getKey(item)}
|
||||
className={props.className}
|
||||
onSelect={() => props.onSelect(item)}
|
||||
>
|
||||
{props.itemRenderer({ item })}
|
||||
</Command.Item>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user