import type * as React from "react"; import { type FetcherWithComponents, useFetcher } from "react-router"; import { type ActionsOf, type FieldsOf, serializeFieldValue, } from "~/utils/action-schemas"; import type { AnySchema } from "~/utils/schema"; import { SendouButton, type SendouButtonProps } from "./elements/Button"; import { FormWithConfirm } from "./FormWithConfirm"; import { SubmitButton } from "./SubmitButton"; interface ActionButtonBaseProps< TSchema extends AnySchema, TAction extends ActionsOf, > extends Omit { /** Route's action schema, only used for typing `action` and `fields`. */ schema: TSchema; action: TAction; /** Defaults to the current route. */ formAction?: string; formClassName?: string; /** e.g. to share submitting state between buttons */ fetcher?: FetcherWithComponents; /** submits only after the user confirms via a dialog */ confirm?: { dialogHeading: string; description?: React.ReactNode; submitButtonText?: string; submitButtonVariant?: SendouButtonProps["variant"]; }; } type ActionButtonProps< TSchema extends AnySchema, TAction extends ActionsOf, > = ActionButtonBaseProps & // biome-ignore lint/complexity/noBannedTypes: {} models "branch with no extra fields" ({} extends FieldsOf ? { fields?: FieldsOf } : { fields: FieldsOf }); /** * Button that submits a mutation to a route action as `_action` + hidden fields, * type checked against the route's action schema. * * @example * * {t("q:looking.groups.actions.invite")} * */ export function ActionButton< TSchema extends AnySchema, const TAction extends ActionsOf, >({ schema, action, fields, formAction, formClassName, fetcher: fetcherFromProps, confirm, children, ...buttonProps }: ActionButtonProps) { const ownFetcher = useFetcher(); const fetcher = fetcherFromProps ?? ownFetcher; const fieldEntries = Object.entries( (fields ?? {}) as Record, ).flatMap(([name, value]) => value === undefined || value === null ? [] : ([[name, serializeFieldValue(value)]] as const), ); if (confirm) { return ( {children} ); } return ( {fieldEntries.map(([name, value]) => ( ))} {children} ); }