- {/*
*/}
-
-
+
+
+
+ {me.displayName || 'User'}
+
+
-
+
Settings
logout.mutate({})}>
Sign out
diff --git a/app/components/TopBarPicker.tsx b/app/components/TopBarPicker.tsx
index 884335fc8..9e3c51599 100644
--- a/app/components/TopBarPicker.tsx
+++ b/app/components/TopBarPicker.tsx
@@ -24,8 +24,8 @@ import {
} from '~/hooks/use-params'
import { useCurrentUser } from '~/layouts/AuthenticatedLayout'
import { PAGE_SIZE } from '~/table/QueryTable'
-import { Button } from '~/ui/lib/Button'
-import { DropdownMenu } from '~/ui/lib/DropdownMenu'
+import { buttonStyle } from '~/ui/lib/Button'
+import * as DropdownMenu from '~/ui/lib/DropdownMenu'
import { Identicon } from '~/ui/lib/Identicon'
import { Wrap } from '~/ui/util/wrap'
import { pb } from '~/util/path-builder'
@@ -95,14 +95,14 @@ const TopBarPicker = (props: TopBarPickerProps) => {
{props.items && (
-
+ {/* aria-hidden is a tip from the Reach docs */}
+
)}
@@ -110,38 +110,36 @@ const TopBarPicker = (props: TopBarPickerProps) => {
{/* TODO: item size and focus highlight */}
{/* TODO: popover position should be further right */}
{props.items && (
- // portal is necessary to avoid the menu popover getting its own after:
- // separator thing
-
-
- {props.items.length > 0 ? (
- props.items.map(({ label, to }) => {
- const isSelected = props.current === label
- return (
-
-
-
- {label}
- {isSelected && }
-
-
-
- )
- })
- ) : (
- {}}
- disabled
- >
- {props.noItemsText || 'No items found'}
-
- )}
-
-
+
+ {props.items.length > 0 ? (
+ props.items.map(({ label, to }) => {
+ const isSelected = props.current === label
+ return (
+
+
+ {label}
+ {isSelected && }
+
+
+ )
+ })
+ ) : (
+ {}}
+ disabled
+ >
+ {props.noItemsText || 'No items found'}
+
+ )}
+
)}
)
diff --git a/app/table/columns/action-col.tsx b/app/table/columns/action-col.tsx
index 58a348cf0..f18d16a04 100644
--- a/app/table/columns/action-col.tsx
+++ b/app/table/columns/action-col.tsx
@@ -11,7 +11,7 @@ import { useMemo } from 'react'
import { More12Icon } from '@oxide/design-system/icons/react'
-import { DropdownMenu } from '~/ui/lib/DropdownMenu'
+import * as DropdownMenu from '~/ui/lib/DropdownMenu'
import { Tooltip } from '~/ui/lib/Tooltip'
import { Wrap } from '~/ui/util/wrap'
import { kebabCase } from '~/util/str'
@@ -75,41 +75,38 @@ export const RowActions = ({ id, copyIdLabel = 'Copy ID', actions }: RowActionsP
>
- {/* portal fixes mysterious z-index issue where menu is behind button */}
-
-
- {id && (
- {
- window.navigator.clipboard.writeText(id)
- }}
+ {/* offset moves menu in from the right so it doesn't align with the table border */}
+
+ {id && (
+ {
+ window.navigator.clipboard.writeText(id)
+ }}
+ >
+ {copyIdLabel}
+
+ )}
+ {actions?.map((action) => {
+ // TODO: Tooltip on disabled button broke, probably due to portal
+ return (
+ }
+ key={kebabCase(`action-${action.label}`)}
>
- {copyIdLabel}
-
- )}
- {actions?.map((action) => {
- // TODO: Tooltip on disabled button broke, probably due to portal
- return (
- }
- key={kebabCase(`action-${action.label}`)}
+
-
- {action.label}
-
-
- )
- })}
-
-
+ {action.label}
+
+
+ )
+ })}
+
)
}
diff --git a/app/ui/lib/DropdownMenu.tsx b/app/ui/lib/DropdownMenu.tsx
index 929296262..804e448bc 100644
--- a/app/ui/lib/DropdownMenu.tsx
+++ b/app/ui/lib/DropdownMenu.tsx
@@ -5,47 +5,77 @@
*
* Copyright Oxide Computer Company
*/
+
import {
- Content,
- Item,
- Portal,
- Root,
- Trigger,
- type DropdownMenuContentProps,
- type DropdownMenuItemProps,
-} from '@radix-ui/react-dropdown-menu'
+ Menu,
+ MenuButton,
+ MenuItem,
+ MenuItems,
+ type MenuItemsProps,
+} from '@headlessui/react'
import cn from 'classnames'
-import { forwardRef, type ForwardedRef } from 'react'
+import { forwardRef, type ForwardedRef, type ReactNode } from 'react'
import { Link } from 'react-router-dom'
-type DivRef = ForwardedRef
-
-// remove possibility of disabling links for now. if we put it back, make sure
-// to forwardRef on LinkItem so the disabled tooltip can work
-type LinkitemProps = Omit & { to: string }
-
-export const DropdownMenu = {
- Root,
- Trigger,
- Portal,
- // don't need to forward ref here for a particular reason but Radix gives a
- // big angry warning if we don't
- Content: forwardRef(({ className, ...props }: DropdownMenuContentProps, ref: DivRef) => (
- e.preventDefault()}
- className={cn('DropdownMenuContent', className)}
- ref={ref}
- />
- )),
- // need to forward ref because of tooltips on disabled menu buttons
- Item: forwardRef(({ className, ...props }: DropdownMenuItemProps, ref: DivRef) => (
-
- )),
- LinkItem: ({ className, children, to, ...props }: LinkitemProps) => (
- -
- {children}
-
- ),
+export const Root = Menu
+
+export const Trigger = MenuButton
+
+type ContentProps = {
+ className?: string
+ children: ReactNode
+ anchor?: MenuItemsProps['anchor']
+ /** Spacing in px, passed as --anchor-gap */
+ gap?: 8
}
+
+export function Content({ className, children, anchor = 'bottom end', gap }: ContentProps) {
+ return (
+
+ {children}
+
+ )
+}
+
+type LinkItemProps = { className?: string; to: string; children: ReactNode }
+
+export function LinkItem({ className, to, children }: LinkItemProps) {
+ return (
+
+ )
+}
+
+type ButtonRef = ForwardedRef
+type ItemProps = {
+ className?: string
+ onSelect?: () => void
+ children: ReactNode
+ disabled?: boolean
+}
+
+// need to forward ref because of tooltips on disabled menu buttons
+export const Item = forwardRef(
+ ({ className, onSelect, children, disabled }: ItemProps, ref: ButtonRef) => (
+
+ )
+)
diff --git a/app/ui/styles/components/menu-button.css b/app/ui/styles/components/menu-button.css
index 526a881ca..2a88a8723 100644
--- a/app/ui/styles/components/menu-button.css
+++ b/app/ui/styles/components/menu-button.css
@@ -7,10 +7,10 @@
*/
.DropdownMenuContent {
- @apply z-30 min-w-36 rounded border p-0 bg-raise border-secondary;
+ @apply z-popover min-w-36 rounded border p-0 bg-raise border-secondary;
& .DropdownMenuItem {
- @apply block cursor-pointer select-none border-b py-2 pl-3 pr-6 text-left text-sans-md text-secondary border-secondary last:border-b-0;
+ @apply block w-full cursor-pointer select-none border-b py-2 pl-3 pr-6 text-left text-sans-md text-secondary border-secondary last:border-b-0;
&.destructive {
@apply text-destructive;
@@ -24,8 +24,7 @@
@apply text-destructive-disabled;
}
- &[data-highlighted] {
- /* background: hsl(211, 81%, 36%); */
+ &[data-focus] {
outline: none;
@apply bg-tertiary;
}
diff --git a/package-lock.json b/package-lock.json
index 395353411..24e9e4015 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,6 @@
"@oxide/design-system": "^1.4.6",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-dialog": "^1.0.5",
- "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-focus-guards": "1.0.1",
"@radix-ui/react-tabs": "^1.1.0",
"@react-aria/live-announcer": "^3.3.4",
@@ -2472,29 +2471,6 @@
}
}
},
- "node_modules/@radix-ui/react-arrow": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
- "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-primitive": "2.0.0"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.0.tgz",
@@ -2699,35 +2675,6 @@
}
}
},
- "node_modules/@radix-ui/react-dropdown-menu": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.1.tgz",
- "integrity": "sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/primitive": "1.1.0",
- "@radix-ui/react-compose-refs": "1.1.0",
- "@radix-ui/react-context": "1.1.0",
- "@radix-ui/react-id": "1.1.0",
- "@radix-ui/react-menu": "2.1.1",
- "@radix-ui/react-primitive": "2.0.0",
- "@radix-ui/react-use-controllable-state": "1.1.0"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-focus-guards": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
@@ -2789,118 +2736,6 @@
}
}
},
- "node_modules/@radix-ui/react-menu": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.1.tgz",
- "integrity": "sha512-oa3mXRRVjHi6DZu/ghuzdylyjaMXLymx83irM7hTxutQbD+7IhPKdMdRHD26Rm+kHRrWcrUkkRPv5pd47a2xFQ==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/primitive": "1.1.0",
- "@radix-ui/react-collection": "1.1.0",
- "@radix-ui/react-compose-refs": "1.1.0",
- "@radix-ui/react-context": "1.1.0",
- "@radix-ui/react-direction": "1.1.0",
- "@radix-ui/react-dismissable-layer": "1.1.0",
- "@radix-ui/react-focus-guards": "1.1.0",
- "@radix-ui/react-focus-scope": "1.1.0",
- "@radix-ui/react-id": "1.1.0",
- "@radix-ui/react-popper": "1.2.0",
- "@radix-ui/react-portal": "1.1.1",
- "@radix-ui/react-presence": "1.1.0",
- "@radix-ui/react-primitive": "2.0.0",
- "@radix-ui/react-roving-focus": "1.1.0",
- "@radix-ui/react-slot": "1.1.0",
- "@radix-ui/react-use-callback-ref": "1.1.0",
- "aria-hidden": "^1.1.1",
- "react-remove-scroll": "2.5.7"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-guards": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz",
- "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==",
- "license": "MIT",
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-menu/node_modules/react-remove-scroll": {
- "version": "2.5.7",
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz",
- "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==",
- "license": "MIT",
- "dependencies": {
- "react-remove-scroll-bar": "^2.3.4",
- "react-style-singleton": "^2.2.1",
- "tslib": "^2.1.0",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-popper": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
- "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
- "license": "MIT",
- "dependencies": {
- "@floating-ui/react-dom": "^2.0.0",
- "@radix-ui/react-arrow": "1.1.0",
- "@radix-ui/react-compose-refs": "1.1.0",
- "@radix-ui/react-context": "1.1.0",
- "@radix-ui/react-primitive": "2.0.0",
- "@radix-ui/react-use-callback-ref": "1.1.0",
- "@radix-ui/react-use-layout-effect": "1.1.0",
- "@radix-ui/react-use-rect": "1.1.0",
- "@radix-ui/react-use-size": "1.1.0",
- "@radix-ui/rect": "1.1.0"
- },
- "peerDependencies": {
- "@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
"node_modules/@radix-ui/react-portal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz",
@@ -3117,48 +2952,6 @@
}
}
},
- "node_modules/@radix-ui/react-use-rect": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
- "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/rect": "1.1.0"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-use-size": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
- "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
- "license": "MIT",
- "dependencies": {
- "@radix-ui/react-use-layout-effect": "1.1.0"
- },
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/rect": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
- "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
- "license": "MIT"
- },
"node_modules/@react-aria/breadcrumbs": {
"version": "3.5.16",
"resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.16.tgz",
diff --git a/package.json b/package.json
index 35b52c362..de52f1cad 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,6 @@
"@oxide/design-system": "^1.4.6",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-dialog": "^1.0.5",
- "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-focus-guards": "1.0.1",
"@radix-ui/react-tabs": "^1.1.0",
"@react-aria/live-announcer": "^3.3.4",
diff --git a/tailwind.config.js b/tailwind.config.js
index 17b8108be..be83d8b78 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -48,6 +48,7 @@ module.exports = {
modal: '40',
sideModalDropdown: '40',
sideModal: '30',
+ topBarPopover: '25',
topBar: '20',
popover: '10',
contentDropdown: '10',