+const DivItem = ({ variant, ...props }: ContextMenuItemProps) => (
+
);
-const ItemInternals = ({ icon, label, rightArrow, keybind }: ItemProps) => {
+export const ItemInternals = ({
+ icon,
+ label,
+ rightArrow,
+ keybind,
+ iconProps
+}: ContextMenuItemProps) => {
const ItemIcon = icon;
return (
<>
- {ItemIcon &&
}
+ {ItemIcon &&
}
{label &&
{label}
}
{keybind && (
@@ -134,3 +143,10 @@ const ItemInternals = ({ icon, label, rightArrow, keybind }: ItemProps) => {
>
);
};
+
+export const ContextMenu = {
+ Root,
+ Item,
+ Separator,
+ SubMenu
+};
diff --git a/packages/ui/src/Dropdown.tsx b/packages/ui/src/Dropdown.tsx
index 41383429f..62eb61f25 100644
--- a/packages/ui/src/Dropdown.tsx
+++ b/packages/ui/src/Dropdown.tsx
@@ -2,7 +2,7 @@ import { ReactComponent as CaretDown } from '@sd/assets/svgs/caret.svg';
import { Menu, Transition } from '@headlessui/react';
import { VariantProps, cva } from 'class-variance-authority';
import clsx from 'clsx';
-import { Fragment, PropsWithChildren } from 'react';
+import { Fragment, PropsWithChildren, forwardRef } from 'react';
import { Link } from 'react-router-dom';
import * as UI from '.';
import { tw } from './utils';
@@ -60,18 +60,20 @@ export const Item = ({ to, className, icon: Icon, children, ...props }: Dropdown
);
};
-export const Button = ({ children, className, ...props }: UI.ButtonProps) => {
- return (
-
- {children}
-
-
-
- );
-};
+export const Button = forwardRef
(
+ ({ children, className, ...props }, ref) => {
+ return (
+
+ {children}
+
+
+
+ );
+ }
+);
export interface DropdownRootProps {
button: React.ReactNode;
diff --git a/packages/ui/src/DropdownMenu.tsx b/packages/ui/src/DropdownMenu.tsx
new file mode 100644
index 000000000..9340f51f8
--- /dev/null
+++ b/packages/ui/src/DropdownMenu.tsx
@@ -0,0 +1,162 @@
+import * as RadixDM from '@radix-ui/react-dropdown-menu';
+import clsx from 'clsx';
+import React, { PropsWithChildren, Suspense, useCallback, useRef, useState } from 'react';
+import { Link } from 'react-router-dom';
+import {
+ ContextMenuItemProps,
+ ItemInternals,
+ contextMenuClasses,
+ contextMenuItemStyles,
+ contextMenuSeparatorClassNames,
+ contextSubMenuTriggerClassNames
+} from './ContextMenu';
+
+interface DropdownMenuProps
+ extends RadixDM.MenuContentProps,
+ Pick {
+ trigger: React.ReactNode;
+ triggerClassName?: string;
+ alignToTrigger?: boolean;
+ animate?: boolean;
+}
+
+const Root = ({
+ trigger,
+ children,
+ className,
+ asChild = true,
+ triggerClassName,
+ alignToTrigger,
+ onOpenChange,
+ animate,
+ ...props
+}: PropsWithChildren) => {
+ const [width, setWidth] = useState();
+
+ const measureRef = useCallback((ref: HTMLButtonElement | null) => {
+ alignToTrigger && ref && setWidth(ref.getBoundingClientRect().width);
+ }, []);
+
+ return (
+
+
+ {trigger}
+
+
+
+
+
+ );
+};
+
+const Separator = (props: { className?: string }) => (
+
+);
+
+const SubMenu = ({
+ label,
+ icon,
+ className,
+ ...props
+}: RadixDM.MenuSubContentProps & ContextMenuItemProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+interface DropdownItemProps extends ContextMenuItemProps, RadixDM.MenuItemProps {
+ to?: string;
+ selected?: boolean;
+}
+
+const Item = ({
+ icon,
+ iconProps,
+ label,
+ rightArrow,
+ children,
+ keybind,
+ variant,
+ className,
+ selected,
+ to,
+ ...props
+}: DropdownItemProps) => {
+ const ref = useRef(null);
+
+ return (
+
+ {to ? (
+ ref.current?.click()}
+ >
+ {children ? (
+ {children}
+ ) : (
+
+ )}
+
+ ) : (
+
+ {children || }
+
+ )}
+
+ );
+};
+
+export const DropdownMenu = {
+ Root,
+ Item,
+ Separator,
+ SubMenu
+};
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index 953bde36f..04103c51e 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -1,7 +1,8 @@
export { cva, cx } from 'class-variance-authority';
export * from './Button';
export * from './CheckBox';
-export * as ContextMenu from './ContextMenu';
+export { ContextMenu } from './ContextMenu';
+export { DropdownMenu } from './DropdownMenu';
export * from './Dialog';
export * as Dropdown from './Dropdown';
export * from './Input';
diff --git a/packages/ui/style/tailwind.js b/packages/ui/style/tailwind.js
index 9d8b5b7d0..6bf664bde 100644
--- a/packages/ui/style/tailwind.js
+++ b/packages/ui/style/tailwind.js
@@ -162,6 +162,7 @@ module.exports = function (app, options) {
// addVariant('open', '&[data-state="open"]');
// addVariant('closed', '&[data-state="closed"]');
// }),
+ require('tailwindcss-animate'),
require('@headlessui/tailwindcss'),
require('tailwindcss-radix')()
]
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a8b3c4c5e..76e0ef5d3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -593,6 +593,7 @@ importers:
storybook-tailwind-dark-mode: ^1.0.15
style-loader: ^3.3.1
tailwindcss: ^3.1.8
+ tailwindcss-animate: ^1.0.5
tailwindcss-radix: ^2.6.0
typescript: ^4.8.4
dependencies:
@@ -646,6 +647,7 @@ importers:
storybook-tailwind-dark-mode: 1.0.15_pmt6gdvpkbsejpsyufhkfrbkda
style-loader: 3.3.1_webpack@5.75.0
tailwindcss: 3.2.4_postcss@8.4.21
+ tailwindcss-animate: 1.0.5_tailwindcss@3.2.4
typescript: 4.9.4
packages:
@@ -20327,6 +20329,14 @@ packages:
resolution: {integrity: sha512-qImOD23aDfnIDNqlG1NOehdB9IYsn1V9oByPjKY1nakv2MQYCEMyX033/q+aEtYCpmYK1cv2+NTmlH+ra6GA5A==}
dev: true
+ /tailwindcss-animate/1.0.5_tailwindcss@3.2.4:
+ resolution: {integrity: sha512-UU3qrOJ4lFQABY+MVADmBm+0KW3xZyhMdRvejwtXqYOL7YjHYxmuREFAZdmVG5LPe5E9CAst846SLC4j5I3dcw==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders'
+ dependencies:
+ tailwindcss: 3.2.4_postcss@8.4.21
+ dev: true
+
/tailwindcss-radix/2.7.0:
resolution: {integrity: sha512-fIVkT5zQYdsjT9+/Mvp+DTlJDdTFpRDuyS5+PLuJDAIIVr9+rWYKhK6rsB9QtjwUwwb0YF+BkAJN6CjZivOfLA==}
dev: false