Skip to content
Merged
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
10 changes: 4 additions & 6 deletions app/components/form/ReadOnlySideModalForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
import type { ReactNode } from 'react'

import { useShouldAnimateModal } from '~/hooks/use-should-animate-modal'
import { Button } from '~/ui/lib/Button'
import { SideModal } from '~/ui/lib/SideModal'

Expand All @@ -15,11 +16,7 @@ type ReadOnlySideModalFormProps = {
subtitle?: ReactNode
onDismiss: () => void
children: ReactNode
/**
* Whether to animate the modal opening. Defaults to true. Used to prevent
* modal from animating in on a fresh pageload where it should already be
* open.
*/
/** Pass `true` for state-driven modals. Omit for route-driven modals to use nav type. */
animate?: boolean
}

Expand All @@ -34,13 +31,14 @@ export function ReadOnlySideModalForm({
children,
animate,
}: ReadOnlySideModalFormProps) {
const animateDefault = useShouldAnimateModal()
return (
<SideModal
isOpen
onDismiss={onDismiss}
title={title}
subtitle={subtitle}
animate={animate}
animate={animate ?? animateDefault}
>
<SideModal.Body>
<div className="ox-form">{children}</div>
Expand Down
12 changes: 1 addition & 11 deletions app/components/form/SideModalForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

import { useEffect, useId, useState, type ReactNode } from 'react'
import type { FieldValues, UseFormReturn } from 'react-hook-form'
import { NavigationType, useNavigationType } from 'react-router'

import type { ApiError } from '@oxide/api'

import { useShouldAnimateModal } from '~/hooks/use-should-animate-modal'
import { Button } from '~/ui/lib/Button'
import { Modal } from '~/ui/lib/Modal'
import { SideModal } from '~/ui/lib/SideModal'
Expand Down Expand Up @@ -49,16 +49,6 @@ type SideModalFormProps<TFieldValues extends FieldValues> = {
onSubmit?: (values: TFieldValues) => void
} & (CreateFormProps | EditFormProps)

/**
* Only animate the modal in when we're navigating by a client-side click.
* Don't animate on a fresh pageload or on back/forward. The latter may be
* slightly awkward but it also makes some sense. I do not believe there is
* any way to distinguish between fresh pageload and back/forward.
*/
function useShouldAnimateModal() {
return useNavigationType() === NavigationType.Push
}

export function SideModalForm<TFieldValues extends FieldValues>({
form,
formType,
Expand Down
9 changes: 1 addition & 8 deletions app/forms/idp/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@
* Copyright Oxide Computer Company
*/
import { useForm } from 'react-hook-form'
import {
NavigationType,
useNavigate,
useNavigationType,
type LoaderFunctionArgs,
} from 'react-router'
import { useNavigate, type LoaderFunctionArgs } from 'react-router'

import { api, q, queryClient, usePrefetchedQuery } from '@oxide/api'
import { Access16Icon } from '@oxide/design-system/icons/react'
Expand Down Expand Up @@ -45,15 +40,13 @@ export default function EditIdpSideModalForm() {

const navigate = useNavigate()
const onDismiss = () => navigate(pb.silo({ silo }))
const animate = useNavigationType() === NavigationType.Push

const form = useForm({ defaultValues: idp })

return (
<ReadOnlySideModalForm
title="Identity provider"
onDismiss={onDismiss}
animate={animate}
subtitle={
<ResourceLabel>
<Access16Icon /> {idp.name}
Expand Down
3 changes: 0 additions & 3 deletions app/forms/image-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@ export function EditImageSideModalForm({
image,
dismissLink,
type,
animate,
}: {
image: Image
dismissLink: string
type: 'Project' | 'Silo'
animate?: boolean
}) {
const navigate = useNavigate()
const form = useForm({ defaultValues: image })
Expand All @@ -40,7 +38,6 @@ export function EditImageSideModalForm({
<ReadOnlySideModalForm
title={capitalize(resourceName)}
onDismiss={onDismiss}
animate={animate}
subtitle={
<ResourceLabel>
<Images16Icon /> {image.name}
Expand Down
9 changes: 1 addition & 8 deletions app/forms/ssh-key-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@
* Copyright Oxide Computer Company
*/
import { useForm } from 'react-hook-form'
import {
NavigationType,
useNavigate,
useNavigationType,
type LoaderFunctionArgs,
} from 'react-router'
import { useNavigate, type LoaderFunctionArgs } from 'react-router'

import { api, q, queryClient, usePrefetchedQuery } from '@oxide/api'
import { Key16Icon } from '@oxide/design-system/icons/react'
Expand Down Expand Up @@ -47,13 +42,11 @@ export default function EditSSHKeySideModalForm() {

const form = useForm({ defaultValues: data })
const onDismiss = () => navigate(pb.sshKeys())
const animate = useNavigationType() === NavigationType.Push

return (
<ReadOnlySideModalForm
title="View SSH key"
onDismiss={onDismiss}
animate={animate}
subtitle={
<ResourceLabel>
<Key16Icon /> {data.name}
Expand Down
19 changes: 19 additions & 0 deletions app/hooks/use-should-animate-modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright Oxide Computer Company
*/
import { NavigationType, useNavigationType } from 'react-router'

/**
* Only animate the modal in when we're navigating by a client-side click.
* Don't animate on a fresh pageload or on back/forward. The latter may be
* slightly awkward but it also makes some sense. I do not believe there is
* any way to distinguish between fresh pageload and back/forward.
*/
export function useShouldAnimateModal() {
const navType = useNavigationType()
return navType === NavigationType.Push || navType === NavigationType.Replace
}
12 changes: 2 additions & 10 deletions app/pages/SiloImageEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* Copyright Oxide Computer Company
*/
import { NavigationType, useNavigationType, type LoaderFunctionArgs } from 'react-router'
import type { LoaderFunctionArgs } from 'react-router'

import { api, q, queryClient, usePrefetchedQuery } from '@oxide/api'

Expand All @@ -28,14 +28,6 @@ export const handle = titleCrumb('Edit Image')
export default function SiloImageEdit() {
const selector = useSiloImageSelector()
const { data } = usePrefetchedQuery(imageView(selector))
const animate = useNavigationType() === NavigationType.Push

return (
<EditImageSideModalForm
image={data}
dismissLink={pb.siloImages()}
type="Silo"
animate={animate}
/>
)
return <EditImageSideModalForm image={data} dismissLink={pb.siloImages()} type="Silo" />
}
18 changes: 4 additions & 14 deletions app/pages/project/disks/DiskDetailSideModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@
*
* Copyright Oxide Computer Company
*/
import {
NavigationType,
useNavigate,
useNavigationType,
type LoaderFunctionArgs,
} from 'react-router'
import { useNavigate, type LoaderFunctionArgs } from 'react-router'

import { api, q, queryClient, usePrefetchedQuery, type Disk } from '@oxide/api'
import { Storage16Icon } from '@oxide/design-system/icons/react'
Expand Down Expand Up @@ -41,14 +36,9 @@ export default function DiskDetailSideModalRoute() {
const { project, disk } = useDiskSelector()
const navigate = useNavigate()
const { data } = usePrefetchedQuery(diskView({ project, disk }))
const animate = useNavigationType() === NavigationType.Push

return (
<DiskDetailSideModal
disk={data}
onDismiss={() => navigate(pb.disks({ project }))}
animate={animate}
/>
<DiskDetailSideModal disk={data} onDismiss={() => navigate(pb.disks({ project }))} />
)
}

Expand All @@ -60,14 +50,14 @@ export default function DiskDetailSideModalRoute() {
type DiskDetailSideModalProps = {
disk: Disk
onDismiss: () => void
/** Default true because when used outside a route (e.g., StorageTab), it's always a click action */
/** Pass `true` for state-driven usage (e.g., StorageTab). Omit for route usage. */
animate?: boolean
}

export function DiskDetailSideModal({
disk,
onDismiss,
animate = true,
animate,
}: DiskDetailSideModalProps) {
return (
<ReadOnlySideModalForm
Expand Down
12 changes: 2 additions & 10 deletions app/pages/project/images/ProjectImageEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* Copyright Oxide Computer Company
*/
import { NavigationType, useNavigationType, type LoaderFunctionArgs } from 'react-router'
import type { LoaderFunctionArgs } from 'react-router'

import { api, q, queryClient, usePrefetchedQuery } from '@oxide/api'

Expand All @@ -29,15 +29,7 @@ export const handle = titleCrumb('Edit Image')
export default function ProjectImageEdit() {
const selector = useProjectImageSelector()
const { data } = usePrefetchedQuery(imageView(selector))
const animate = useNavigationType() === NavigationType.Push

const dismissLink = pb.projectImages({ project: selector.project })
return (
<EditImageSideModalForm
image={data}
dismissLink={dismissLink}
type="Project"
animate={animate}
/>
)
return <EditImageSideModalForm image={data} dismissLink={dismissLink} type="Project" />
}
6 changes: 5 additions & 1 deletion app/pages/project/instances/StorageTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,11 @@ export default function StorageTab() {
/>
)}
{selectedDisk && (
<DiskDetailSideModal disk={selectedDisk} onDismiss={() => setSelectedDisk(null)} />
<DiskDetailSideModal
disk={selectedDisk}
onDismiss={() => setSelectedDisk(null)}
animate
/>
)}
</div>
)
Expand Down
6 changes: 5 additions & 1 deletion app/pages/project/snapshots/SnapshotsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,11 @@ export default function SnapshotsPage() {
{table}
<Outlet />
{selectedDisk && (
<DiskDetailSideModal disk={selectedDisk} onDismiss={() => setSelectedDisk(null)} />
<DiskDetailSideModal
disk={selectedDisk}
onDismiss={() => setSelectedDisk(null)}
animate
/>
)}
</>
)
Expand Down
10 changes: 1 addition & 9 deletions app/pages/project/vpcs/internet-gateway-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@
*/

import { useQuery } from '@tanstack/react-query'
import {
Link,
NavigationType,
useNavigate,
useNavigationType,
type LoaderFunctionArgs,
} from 'react-router'
import { Link, useNavigate, type LoaderFunctionArgs } from 'react-router'

import { Gateway16Icon } from '@oxide/design-system/icons/react'

Expand Down Expand Up @@ -96,7 +90,6 @@ export default function EditInternetGatewayForm() {
const navigate = useNavigate()
const { project, vpc, gateway } = useInternetGatewaySelector()
const onDismiss = () => navigate(pb.vpcInternetGateways({ project, vpc }))
const animate = useNavigationType() === NavigationType.Push
const { data: internetGateway } = usePrefetchedQuery(
q(api.internetGatewayView, {
query: { project, vpc },
Expand All @@ -116,7 +109,6 @@ export default function EditInternetGatewayForm() {
<ReadOnlySideModalForm
title="Internet gateway"
onDismiss={onDismiss}
animate={animate}
subtitle={
<ResourceLabel>
<Gateway16Icon /> {internetGateway.name}
Expand Down
Loading