mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 13:23:28 +00:00
Merge remote-tracking branch 'origin/refactor-dropdown' into spacedrive-but-themable
This commit is contained in:
commit
90a267930a
|
@ -1,9 +1,7 @@
|
|||
import { Disclosure, Transition } from '@headlessui/react';
|
||||
import { ChevronRightIcon, XMarkIcon } from '@heroicons/react/24/solid';
|
||||
import { ChevronRightIcon } from '@heroicons/react/24/solid';
|
||||
import { Button } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { List, X } from 'phosphor-react';
|
||||
import { PropsWithChildren, useEffect, useState } from 'react';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import pkg from 'react-burger-menu';
|
||||
|
||||
import { Doc, DocsNavigation, toTitleCase } from '../pages/docs/api';
|
||||
|
@ -32,9 +30,10 @@ export default function DocsLayout(props: Props) {
|
|||
<div className="visible h-screen pb-20 overflow-x-hidden custom-scroll doc-sidebar-scroll bg-gray-650 pt-7 px-7 sm:invisible">
|
||||
<Button
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
icon={<X weight="bold" className="w-6 h-6" />}
|
||||
className="!px-1 -ml-0.5 mb-3 !border-none"
|
||||
/>
|
||||
>
|
||||
<X weight="bold" className="w-6 h-6" />
|
||||
</Button>
|
||||
<DocsSidebar activePath={props?.doc?.url} navigation={props.navigation} />
|
||||
</div>
|
||||
</Menu>
|
||||
|
@ -45,11 +44,9 @@ export default function DocsLayout(props: Props) {
|
|||
<div className="flex flex-col w-full sm:flex-row" id="page-container">
|
||||
<div className="h-12 px-5 flex w-full border-t border-gray-600 border-b mt-[65px] sm:hidden items-center ">
|
||||
<div className="flex sm:hidden">
|
||||
<Button
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
icon={<List weight="bold" className="w-6 h-6" />}
|
||||
className="!px-2 ml-1 !border-none"
|
||||
/>
|
||||
<Button onClick={() => setMenuOpen(!menuOpen)} className="!px-2 ml-1 !border-none">
|
||||
<List weight="bold" className="w-6 h-6" />
|
||||
</Button>
|
||||
</div>
|
||||
{props.doc?.url.split('/').map((item, index) => {
|
||||
if (index === 2) return null;
|
||||
|
|
|
@ -9,11 +9,9 @@ import { Discord, Github } from '@icons-pack/react-simple-icons';
|
|||
import AppLogo from '@sd/assets/images/logo.png';
|
||||
import { Dropdown, DropdownItem } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { DotsThreeVertical } from 'phosphor-react';
|
||||
import { PropsWithChildren, useEffect, useState } from 'react';
|
||||
|
||||
|
||||
import { positions } from '../pages/careers.page';
|
||||
import { getWindow } from '../utils';
|
||||
|
||||
|
@ -96,7 +94,7 @@ export default function NavBar() {
|
|||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<Dropdown
|
||||
<Dropdown.Root
|
||||
className="absolute block h-6 text-white w-44 top-2 right-4 lg:hidden"
|
||||
itemsClassName="!rounded-2xl shadow-2xl shadow-black p-2 !bg-gray-850 mt-2 !border-gray-500"
|
||||
itemButtonClassName="!py-1 !rounded-md text-[15px]"
|
||||
|
|
|
@ -3,9 +3,10 @@ import { PlusIcon } from '@heroicons/react/24/solid';
|
|||
import { useCurrentLibrary, useLibraryMutation, useLibraryQuery, usePlatform } from '@sd/client';
|
||||
import { LocationCreateArgs } from '@sd/client';
|
||||
import { Button, CategoryHeading, Dropdown, OverlayPanel } from '@sd/ui';
|
||||
import { restyle } from '@sd/ui';
|
||||
import clsx from 'clsx';
|
||||
import { CheckCircle, CirclesFour, Planet, WaveTriangle } from 'phosphor-react';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { PropsWithChildren, forwardRef } from 'react';
|
||||
import { NavLink, NavLinkProps, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useOperatingSystem } from '../../hooks/useOperatingSystem';
|
||||
|
@ -157,6 +158,8 @@ export function Sidebar() {
|
|||
const os = useOperatingSystem();
|
||||
const { library, libraries, isLoading: isLoadingLibraries, switchLibrary } = useCurrentLibrary();
|
||||
|
||||
const itemStyles = macOnly(os, 'dark:hover:bg-gray-550 dark:hover:bg-opacity-50');
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
|
@ -166,52 +169,59 @@ export function Sidebar() {
|
|||
>
|
||||
<WindowControls />
|
||||
|
||||
<Dropdown
|
||||
buttonProps={{
|
||||
justify: 'left',
|
||||
className: clsx(
|
||||
`flex w-full text-left max-w-full mb-1 mt-1 -mr-0.5 shadow-xs rounded !bg-gray-50 border-gray-150 hover:!bg-gray-1000 dark:!bg-gray-500 dark:hover:!bg-gray-500 dark:!border-gray-550 dark:hover:!border-gray-500`,
|
||||
macOnly(
|
||||
os,
|
||||
'dark:!bg-opacity-40 dark:hover:!bg-opacity-70 dark:!border-[#333949] dark:hover:!border-[#394052]'
|
||||
)
|
||||
),
|
||||
variant: 'gray'
|
||||
}}
|
||||
<Dropdown.Root
|
||||
className="mt-2"
|
||||
button={
|
||||
<Dropdown.Button
|
||||
variant="gray"
|
||||
className={clsx(
|
||||
`flex w-full text-left max-w-full mb-1 mt-1 -mr-0.5 shadow-xs rounded !bg-gray-50 border-gray-150 hover:!bg-gray-1000 dark:!bg-gray-500 dark:hover:!bg-gray-500 dark:!border-gray-550 dark:hover:!border-gray-500`,
|
||||
(library === null || isLoadingLibraries) && 'text-gray-300',
|
||||
macOnly(
|
||||
os,
|
||||
'dark:!bg-opacity-40 dark:hover:!bg-opacity-70 dark:!border-[#333949] dark:hover:!border-[#394052]'
|
||||
)
|
||||
)}
|
||||
>
|
||||
{/* this shouldn't default to "My Library", it is only this way for landing demo */}
|
||||
<span className="w-32 truncate">
|
||||
{isLoadingLibraries ? 'Loading...' : library ? library.config.name : ' '}
|
||||
</span>
|
||||
</Dropdown.Button>
|
||||
}
|
||||
// to support the transparent sidebar on macOS we use slightly adjusted styles
|
||||
itemsClassName={macOnly(os, 'dark:bg-gray-800 dark:divide-gray-600')}
|
||||
itemButtonClassName={macOnly(os, 'dark:hover:bg-gray-550 dark:hover:bg-opacity-50')}
|
||||
// this shouldn't default to "My Library", it is only this way for landing demo
|
||||
buttonText={isLoadingLibraries ? 'Loading...' : library ? library.config.name : ' '}
|
||||
buttonTextClassName={library === null || isLoadingLibraries ? 'text-gray-300' : undefined}
|
||||
items={[
|
||||
libraries?.map((lib) => ({
|
||||
name: lib.config.name,
|
||||
selected: lib.uuid === library?.uuid,
|
||||
onPress: () => switchLibrary(lib.uuid)
|
||||
})) || [],
|
||||
[
|
||||
{
|
||||
name: 'Library Settings',
|
||||
icon: CogIcon,
|
||||
to: 'settings/library'
|
||||
},
|
||||
{
|
||||
name: 'Add Library',
|
||||
icon: PlusIcon,
|
||||
wrapItemComponent: CreateLibraryDialog
|
||||
},
|
||||
{
|
||||
name: 'Lock',
|
||||
icon: LockClosedIcon,
|
||||
disabled: true,
|
||||
onPress: () => {
|
||||
alert('TODO: Not implemented yet!');
|
||||
}
|
||||
}
|
||||
]
|
||||
]}
|
||||
/>
|
||||
>
|
||||
<Dropdown.Section>
|
||||
{libraries?.map((lib) => (
|
||||
<Dropdown.Item
|
||||
className={itemStyles}
|
||||
selected={lib.uuid === library?.uuid}
|
||||
key={lib.uuid}
|
||||
onClick={() => switchLibrary(lib.uuid)}
|
||||
>
|
||||
{lib.config.name}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown.Section>
|
||||
<Dropdown.Section>
|
||||
<Dropdown.Item className={itemStyles} icon={CogIcon} to="settings/library">
|
||||
Library Settings
|
||||
</Dropdown.Item>
|
||||
<CreateLibraryDialog>
|
||||
<Dropdown.Item className={itemStyles} icon={PlusIcon}>
|
||||
Add Library
|
||||
</Dropdown.Item>
|
||||
</CreateLibraryDialog>
|
||||
<Dropdown.Item
|
||||
className={itemStyles}
|
||||
icon={LockClosedIcon}
|
||||
onClick={() => alert('TODO: Not implemented yet!')}
|
||||
>
|
||||
Lock
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Section>
|
||||
</Dropdown.Root>
|
||||
<div className="pt-1">
|
||||
<SidebarLink to="/overview">
|
||||
<Icon component={Planet} />
|
||||
|
|
|
@ -171,11 +171,8 @@ export default function OverviewScreen() {
|
|||
// ctaAction={() => {}}
|
||||
ctaLabel="Connect"
|
||||
trigger={
|
||||
<Button
|
||||
size="sm"
|
||||
icon={<PlusIcon className="inline w-4 h-4 -mt-0.5 xl:mr-1" />}
|
||||
variant="gray"
|
||||
>
|
||||
<Button size="sm" variant="gray">
|
||||
<PlusIcon className="inline w-4 h-4 -mt-0.5 xl:mr-1" />
|
||||
<span className="hidden xl:inline-block">Add Device</span>
|
||||
</Button>
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ import clsx from 'clsx';
|
|||
import { forwardRef } from 'react';
|
||||
import { Link, LinkProps } from 'react-router-dom';
|
||||
|
||||
export interface ButtonBaseProps extends VariantProps<typeof styles> {
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
export interface ButtonBaseProps extends VariantProps<typeof styles> {}
|
||||
|
||||
export type ButtonProps = ButtonBaseProps &
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
|
@ -69,7 +67,8 @@ const styles = cva(
|
|||
colored: ['text-white shadow-sm hover:bg-opacity-90 active:bg-opacity-100'],
|
||||
selected: [
|
||||
'bg-gray-100 dark:bg-gray-500 text-black hover:text-black active:text-black dark:hover:text-white dark:text-white'
|
||||
]
|
||||
],
|
||||
bare: ''
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
|
@ -86,21 +85,10 @@ export const Button = forwardRef<
|
|||
>(({ className, ...props }, ref) => {
|
||||
className = clsx(styles(props), className);
|
||||
|
||||
let children = (
|
||||
<>
|
||||
{props.icon}
|
||||
{props.children}
|
||||
</>
|
||||
);
|
||||
|
||||
return hasHref(props) ? (
|
||||
<a {...props} ref={ref as any} className={clsx(className, 'no-underline inline-block')}>
|
||||
{children}
|
||||
</a>
|
||||
<a {...props} ref={ref as any} className={clsx(className, 'no-underline inline-block')} />
|
||||
) : (
|
||||
<button {...(props as ButtonProps)} ref={ref as any} className={className}>
|
||||
{children}
|
||||
</button>
|
||||
<button {...(props as ButtonProps)} ref={ref as any} className={className} />
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -116,10 +104,7 @@ export const ButtonLink = forwardRef<
|
|||
|
||||
return (
|
||||
<Link to={to} ref={ref as any} className={className}>
|
||||
<>
|
||||
{props.icon}
|
||||
{props.children}
|
||||
</>
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -60,28 +60,29 @@ export const SubMenu = ({
|
|||
);
|
||||
};
|
||||
|
||||
const ITEM_CLASSES = `
|
||||
flex flex-row items-center justify-start flex-1
|
||||
px-2 py-1 space-x-2
|
||||
cursor-default rounded
|
||||
focus:outline-none
|
||||
`;
|
||||
|
||||
const itemStyles = cva([ITEM_CLASSES], {
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'hover:bg-primary focus:bg-primary',
|
||||
danger: `
|
||||
text-red-600 dark:text-red-400
|
||||
hover:text-white focus:text-white
|
||||
hover:bg-red-500 focus:bg-red-500
|
||||
`
|
||||
const itemStyles = cva(
|
||||
[
|
||||
'flex flex-row items-center justify-start flex-1',
|
||||
'px-2 py-1 space-x-2',
|
||||
'cursor-default rounded',
|
||||
'focus:outline-none'
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'hover:bg-primary focus:bg-primary',
|
||||
danger: [
|
||||
'text-red-600 dark:text-red-400',
|
||||
'hover:text-white focus:text-white',
|
||||
'hover:bg-red-500 focus:bg-red-500'
|
||||
]
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
interface ItemProps extends VariantProps<typeof itemStyles> {
|
||||
icon?: Icon;
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
||||
|
||||
import { Dropdown } from './Dropdown';
|
||||
import { Root } from './Dropdown';
|
||||
|
||||
export default {
|
||||
title: 'UI/Dropdown',
|
||||
component: Dropdown,
|
||||
component: Root,
|
||||
argTypes: {},
|
||||
parameters: {
|
||||
backgrounds: {
|
||||
default: 'dark'
|
||||
}
|
||||
}
|
||||
} as ComponentMeta<typeof Dropdown>;
|
||||
} as ComponentMeta<typeof Root>;
|
||||
|
||||
const Template: ComponentStory<typeof Dropdown> = (args) => <Dropdown {...args} />;
|
||||
const Template: ComponentStory<typeof Root> = (args) => <Root {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
buttonText: 'Item 1',
|
||||
items: [
|
||||
[
|
||||
{
|
||||
name: 'Item 1',
|
||||
selected: true
|
||||
},
|
||||
{
|
||||
name: 'Item 2',
|
||||
selected: false
|
||||
}
|
||||
]
|
||||
]
|
||||
};
|
||||
// Default.args = {
|
||||
// buttonText: 'Item 1',
|
||||
// items: [
|
||||
// [
|
||||
// {
|
||||
// name: 'Item 1',
|
||||
// selected: true
|
||||
// },
|
||||
// {
|
||||
// name: 'Item 2',
|
||||
// selected: false
|
||||
// }
|
||||
// ]
|
||||
// ]
|
||||
// };
|
||||
|
|
|
@ -1,155 +1,114 @@
|
|||
import { Menu, Transition } from '@headlessui/react';
|
||||
import { ChevronDownIcon } from '@heroicons/react/24/solid';
|
||||
import { VariantProps, cva } from 'class-variance-authority';
|
||||
import clsx from 'clsx';
|
||||
import { Fragment, PropsWithChildren } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Button } from './Button';
|
||||
import * as UI from '.';
|
||||
import { tw } from './utils';
|
||||
|
||||
export type DropdownItem = (
|
||||
| {
|
||||
name: string;
|
||||
icon?: any;
|
||||
selected?: boolean;
|
||||
to?: string;
|
||||
wrapItemComponent?: React.FC<PropsWithChildren>;
|
||||
}
|
||||
| {
|
||||
name: string;
|
||||
icon?: any;
|
||||
disabled?: boolean;
|
||||
selected?: boolean;
|
||||
onPress?: () => any;
|
||||
to?: string;
|
||||
wrapItemComponent?: React.FC<PropsWithChildren>;
|
||||
}
|
||||
)[];
|
||||
export const Section = tw.div`px-1 py-1 space-y-[2px]`;
|
||||
|
||||
export interface DropdownProps {
|
||||
items: DropdownItem[];
|
||||
buttonText?: string;
|
||||
buttonTextClassName?: string;
|
||||
buttonProps?: React.ComponentProps<typeof Button>;
|
||||
buttonComponent?: React.ReactNode;
|
||||
buttonIcon?: any;
|
||||
const itemStyles = cva(
|
||||
'text-sm group flex grow shrink-0 rounded items-center w-full whitespace-nowrap px-2 py-1 mb-[2px] dark:hover:bg-gray-650 disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
{
|
||||
variants: {
|
||||
selected: {
|
||||
true: 'bg-gray-300 dark:bg-primary dark:hover:bg-primary'
|
||||
},
|
||||
active: {
|
||||
true: ''
|
||||
// false: 'text-gray-900 dark:text-gray-200'
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const itemIconStyles = cva('mr-2 w-4 h-4', {
|
||||
variants: {
|
||||
active: {
|
||||
true: 'dark:text-gray-100',
|
||||
false: 'text-gray-600 dark:text-gray-200'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
type DropdownItemProps =
|
||||
| PropsWithChildren<{
|
||||
to?: string;
|
||||
className?: string;
|
||||
icon?: any;
|
||||
onClick?: () => void;
|
||||
}> &
|
||||
VariantProps<typeof itemStyles>;
|
||||
|
||||
export const Item = ({ to, className, icon: Icon, children, ...props }: DropdownItemProps) => {
|
||||
let content = (
|
||||
<>
|
||||
{Icon && <Icon className={itemIconStyles(props)} />}
|
||||
<span className="text-left">{children}</span>
|
||||
</>
|
||||
);
|
||||
|
||||
return to ? (
|
||||
<Link {...props} to={to} className={clsx(itemStyles(props), className)}>
|
||||
{content}
|
||||
</Link>
|
||||
) : (
|
||||
<button {...props} className={clsx(itemStyles(props), className)}>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export const Button = ({ children, ...props }: UI.ButtonProps) => {
|
||||
return (
|
||||
<UI.Button size="sm" {...props}>
|
||||
{children}
|
||||
<div className="flex-grow" />
|
||||
<ChevronDownIcon
|
||||
className="w-5 h-5 ml-2 -mr-1 text-violet-200 hover:text-violet-100"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</UI.Button>
|
||||
);
|
||||
};
|
||||
|
||||
export interface DropdownRootProps {
|
||||
button: React.ReactNode;
|
||||
className?: string;
|
||||
itemsClassName?: string;
|
||||
itemButtonClassName?: string;
|
||||
align?: 'left' | 'right';
|
||||
}
|
||||
|
||||
export const Dropdown: React.FC<DropdownProps> = (props) => {
|
||||
export const Root = (props: PropsWithChildren<DropdownRootProps>) => {
|
||||
return (
|
||||
<div className={clsx('w-full mt-2', props.className)}>
|
||||
<Menu as="div" className="relative flex w-full text-left">
|
||||
<Menu.Button as="div" className="flex-1 outline-none">
|
||||
{props.buttonComponent ? (
|
||||
props.buttonComponent
|
||||
) : (
|
||||
<Button size="sm" {...props.buttonProps}>
|
||||
{props.buttonIcon}
|
||||
{props.buttonText && (
|
||||
<>
|
||||
<span className={clsx('w-32 truncate', props.buttonTextClassName)}>
|
||||
{props.buttonText}
|
||||
</span>
|
||||
<div className="flex-grow" />
|
||||
<ChevronDownIcon
|
||||
className="w-5 h-5 ml-2 -mr-1 text-violet-200 hover:text-violet-100 "
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Menu as="div" className={clsx('relative flex w-full text-left', props.className)}>
|
||||
<Menu.Button as="div" className="flex-1 outline-none">
|
||||
{props.button}
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition duration-100 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Menu.Items
|
||||
className={clsx(
|
||||
'absolute z-50 min-w-fit w-full bg-white border divide-y divide-gray-100 rounded shadow-xl top-full dark:bg-gray-550 dark:divide-gray-500 dark:border-gray-600 ring-1 ring-black ring-opacity-5 focus:outline-none',
|
||||
props.itemsClassName,
|
||||
{ 'left-0': props.align === 'left' },
|
||||
{ 'right-0': props.align === 'right' }
|
||||
)}
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition duration-100 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Menu.Items
|
||||
className={clsx(
|
||||
'absolute z-50 min-w-fit w-full bg-white border divide-y divide-gray-100 rounded shadow-xl top-full dark:bg-gray-550 dark:divide-gray-500 dark:border-gray-600 ring-1 ring-black ring-opacity-5 focus:outline-none',
|
||||
props.itemsClassName,
|
||||
{ 'left-0': props.align === 'left' },
|
||||
{ 'right-0': props.align === 'right' }
|
||||
)}
|
||||
>
|
||||
{props.items.map((item, index) => (
|
||||
<div key={index} className="px-1 py-1 space-y-[2px]">
|
||||
{item.map((button, index) => (
|
||||
<Menu.Item key={index}>
|
||||
{({ active }) => {
|
||||
const WrappedItem: any = button.wrapItemComponent
|
||||
? button.wrapItemComponent
|
||||
: (props: React.PropsWithChildren) => <>{props.children}</>;
|
||||
|
||||
return (
|
||||
<WrappedItem>
|
||||
{button.to ? (
|
||||
<Link
|
||||
to={button.to}
|
||||
className={clsx(
|
||||
'text-sm group flex grow shrink-0 rounded items-center w-full whitespace-nowrap px-2 py-1 mb-[2px] dark:hover:bg-gray-650 disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
{
|
||||
'bg-gray-300 dark:bg-primary dark:hover:bg-primary':
|
||||
button.selected
|
||||
// 'text-gray-900 dark:text-gray-200': !active
|
||||
},
|
||||
props.itemButtonClassName
|
||||
)}
|
||||
>
|
||||
{button.icon && (
|
||||
<button.icon
|
||||
className={clsx('mr-2 w-4 h-4', {
|
||||
'dark:text-gray-100': active,
|
||||
'text-gray-600 dark:text-gray-200': !active
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<span className="text-left">{button.name}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
onClick={(button as any).onPress}
|
||||
disabled={(button as any)?.disabled === true}
|
||||
className={clsx(
|
||||
'text-sm group flex grow shrink-0 rounded items-center w-full whitespace-nowrap px-2 py-1 mb-[2px] dark:hover:bg-gray-650 disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
{
|
||||
'bg-gray-300 dark:bg-primary dark:hover:bg-primary':
|
||||
button.selected
|
||||
// 'text-gray-900 dark:text-gray-200': !active
|
||||
},
|
||||
props.itemButtonClassName
|
||||
)}
|
||||
>
|
||||
{button.icon && (
|
||||
<button.icon
|
||||
className={clsx('mr-2 w-4 h-4', {
|
||||
'dark:text-gray-100': active,
|
||||
'text-gray-600 dark:text-gray-200': !active
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<span className="text-left">{button.name}</span>
|
||||
</button>
|
||||
)}
|
||||
</WrappedItem>
|
||||
);
|
||||
}}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
{props.children}
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export * from './Button';
|
||||
export * from './Dropdown';
|
||||
export * as Dropdown from './Dropdown';
|
||||
export * from './Dialog';
|
||||
export * from './Loader';
|
||||
export * as ContextMenu from './ContextMenu';
|
||||
|
@ -8,5 +8,5 @@ export * from './Input';
|
|||
export * from './Select';
|
||||
export * as Tabs from './Tabs';
|
||||
export * from './Typography';
|
||||
export { tw } from './utils';
|
||||
export * from './utils';
|
||||
export { cva } from 'class-variance-authority';
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
function twFactory(element: any) {
|
||||
return ([className, ..._]: TemplateStringsArray) => {
|
||||
const Component = React.forwardRef(({ className: pClassName, ...props }: any, ref) =>
|
||||
React.createElement(element, {
|
||||
...props,
|
||||
className: [className, pClassName],
|
||||
ref
|
||||
})
|
||||
);
|
||||
|
||||
return Component;
|
||||
return restyle(element)(() => className);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -28,3 +21,21 @@ export const tw = new Proxy((() => {}) as unknown as TailwindFactory, {
|
|||
get: (_, property: string) => twFactory(property),
|
||||
apply: (_, __, [el]: [React.ReactElement]) => twFactory(el)
|
||||
});
|
||||
|
||||
export const restyle = <
|
||||
T extends
|
||||
| string
|
||||
| React.FunctionComponent<{ className: string }>
|
||||
| React.ComponentClass<{ className: string }>
|
||||
>(
|
||||
element: T
|
||||
) => {
|
||||
return (cls: () => string) =>
|
||||
React.forwardRef(({ className, ...props }: any, ref) =>
|
||||
React.createElement(element, {
|
||||
...props,
|
||||
className: clsx(cls(), className),
|
||||
ref
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue