Merge pull request #415 from spacedriveapp/general-fixes

general fixes
This commit is contained in:
Oscar Beaumont
2022-10-17 11:47:18 +08:00
committed by GitHub
22 changed files with 241 additions and 196 deletions

View File

@@ -126,7 +126,7 @@ pub(crate) fn mount() -> rspc::RouterBuilder<
.join(&object.cas_id)
.with_extension("webp");
object.has_thumbnail = thumb_path.exists();
object.has_thumbnail = thumb_path.try_exists().unwrap();
}
ExplorerItem::Path(Box::new(file_path))
})

View File

@@ -43,11 +43,6 @@ mod tags;
pub mod utils;
pub mod volumes;
pub use files::*;
pub use jobs::*;
pub use libraries::*;
pub use tags::*;
#[derive(Serialize, Deserialize, Debug, Type)]
struct NodeState {
#[serde(flatten)]

View File

@@ -61,7 +61,7 @@ pub(crate) fn mount() -> RouterBuilder {
.join(&object.cas_id)
.with_extension("webp");
object.has_thumbnail = thumb_path.exists();
object.has_thumbnail = thumb_path.try_exists().unwrap();
ExplorerItem::Object(Box::new(object))
})

View File

@@ -1,6 +1,6 @@
use std::sync::Arc;
use futures::{Future, Stream};
use futures::Stream;
use rspc::{
internal::{
specta, BuiltProcedureBuilder, MiddlewareBuilderLike, RequestResult,

View File

@@ -93,7 +93,7 @@ impl LibraryManager {
};
let db_path = config_path.clone().with_extension("db");
if !db_path.exists() {
if !db_path.try_exists().unwrap() {
println!(
"Found library '{}' but no matching database file was found. Skipping...",
config_path.display()

View File

@@ -44,7 +44,7 @@ impl LocationCreateArgs {
ctx: &LibraryContext,
) -> Result<indexer_job_location::Data, LocationError> {
// check if we have access to this location
if !self.path.exists() {
if !self.path.try_exists().unwrap() {
return Err(LocationError::PathNotFound(self.path));
}

View File

@@ -115,7 +115,7 @@ impl NodeConfigManager {
async fn read(base_path: &PathBuf) -> Result<NodeConfig, NodeConfigError> {
let path = Path::new(base_path).join(NODE_STATE_CONFIG_NAME);
match path.exists() {
match path.try_exists().unwrap() {
true => {
let mut file = File::open(&path)?;
let base_config: ConfigMetadata =

View File

@@ -51,20 +51,20 @@ pub async fn generate_cas_id(path: PathBuf, size: u64) -> Result<String, io::Err
Ok(hex)
}
pub async fn full_checksum(path: &str) -> Result<String, io::Error> {
const BLOCK_SIZE: usize = 1048576;
//read file as buffer and convert to digest
let mut reader = File::open(path).await?;
let mut context = Hasher::new();
let mut buffer = [0; 1048576];
loop {
let read_count = reader.read(&mut buffer).await?;
context.update(&buffer[..read_count]);
if read_count != BLOCK_SIZE {
break;
}
}
let hex = to_hex_string(context.finalize().as_bytes());
// pub async fn full_checksum(path: &str) -> Result<String, io::Error> {
// const BLOCK_SIZE: usize = 1048576;
// //read file as buffer and convert to digest
// let mut reader = File::open(path).await?;
// let mut context = Hasher::new();
// let mut buffer = [0; 1048576];
// loop {
// let read_count = reader.read(&mut buffer).await?;
// context.update(&buffer[..read_count]);
// if read_count != BLOCK_SIZE {
// break;
// }
// }
// let hex = to_hex_string(context.finalize().as_bytes());
Ok(hex)
}
// Ok(hex)
// }

View File

@@ -5,7 +5,7 @@ use crate::{
library::LibraryContext,
prisma::{file_path, location},
};
use sd_file_ext::extensions::{Extension, ImageExtension, VideoExtension, ALL_VIDEO_EXTENSIONS};
use sd_file_ext::extensions::{Extension, ImageExtension, VideoExtension};
use image::{self, imageops, DynamicImage, GenericImageView};
use serde::{Deserialize, Serialize};
@@ -120,8 +120,8 @@ impl StatefulJob for ThumbnailJob {
&library_ctx,
state.init.location_id,
&state.init.path,
ALL_VIDEO_EXTENSIONS
.into_iter()
sd_file_ext::extensions::ALL_VIDEO_EXTENSIONS
.iter()
.map(Clone::clone)
.filter(can_generate_thumbnail_for_video)
.map(Extension::Video)
@@ -189,7 +189,7 @@ impl StatefulJob for ThumbnailJob {
let output_path = data.thumbnail_dir.join(&cas_id).with_extension("webp");
// check if file exists at output path
if !output_path.exists() {
if !output_path.try_exists().unwrap() {
info!("Writing {:?} to {:?}", path, output_path);
match step.kind {
@@ -317,10 +317,8 @@ async fn get_files_by_extensions(
.collect())
}
#[allow(unused)]
pub fn can_generate_thumbnail_for_video(video_extension: &VideoExtension) -> bool {
use VideoExtension::*;
match video_extension {
Mpg | Swf | M2v => false,
_ => true,
}
!matches!(video_extension, Mpg | Swf | M2v)
}

View File

@@ -1,10 +1,7 @@
#![allow(dead_code)]
use crate::extensions::{
ArchiveExtension, AudioExtension, CodeExtension, DatabaseExtension, ExecutableExtension,
Extension, FontExtension, ImageExtension, MeshExtension, VideoExtension,
};
use crate::extensions::{CodeExtension, Extension, VideoExtension};
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum ExtensionPossibility {
Known(Extension),
Conflicts(Vec<Extension>),
@@ -30,7 +27,7 @@ macro_rules! magic_byte_value {
$val as u8
}};
}
pub(crate) use magic_byte_value;
// pub(crate) use magic_byte_value;
#[macro_export]
macro_rules! magic_byte_offset {
@@ -41,7 +38,7 @@ macro_rules! magic_byte_offset {
$val
};
}
pub(crate) use magic_byte_offset;
// pub(crate) use magic_byte_offset;
macro_rules! extension_enum {
(
@@ -50,11 +47,12 @@ macro_rules! extension_enum {
}
) => {
// construct enum
#[derive(Debug, ::serde::Serialize, ::serde::Deserialize, PartialEq)]
#[derive(Debug, ::serde::Serialize, ::serde::Deserialize, PartialEq, Eq)]
pub enum Extension {
$( $variant($type), )*
}
impl Extension {
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Option<ExtensionPossibility> {
use std::str::FromStr;
let mut exts = [$(
@@ -100,7 +98,7 @@ macro_rules! extension_category_enum {
$($(#[$variant_attr:meta])* $variant:ident $(= $( [$($magic_bytes:tt),*] $(+ $offset:literal)? )|+ )? ,)*
}
) => {
#[derive(Debug, ::serde::Serialize, ::serde::Deserialize, Clone, Copy, PartialEq)]
#[derive(Debug, ::serde::Serialize, ::serde::Deserialize, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
$(#[$enum_attr])*
@@ -195,10 +193,10 @@ impl Extension {
Self::Audio(x) => verify_magic_bytes(x, file).map(Self::Audio),
Self::Video(x) => verify_magic_bytes(x, file).map(Self::Video),
Self::Executable(x) => verify_magic_bytes(x, file).map(Self::Executable),
_ => return None,
_ => None,
}
} else {
Some(Extension::from(e))
Some(e)
}
}
ExtensionPossibility::Conflicts(ext) => match ext_str {

View File

@@ -7,7 +7,7 @@ fn main() {
println!("Issuing sdtunnel certificate...");
let env_file = Path::new("./.env");
if env_file.exists() {
if env_file.try_exists().unwrap() {
println!("File '{}' already exists. Exiting...", env_file.display());
return;
}

View File

@@ -20,7 +20,6 @@
"@rspc/client": "^0.1.2",
"@rspc/react": "^0.1.2",
"@sd/config": "workspace:*",
"@sd/interface": "workspace:*",
"@tanstack/react-query": "^4.10.1",
"eventemitter3": "^4.0.7",
"immer": "^9.0.15",

View File

@@ -18,6 +18,7 @@
"@fontsource/inter": "^4.5.13",
"@headlessui/react": "^1.7.3",
"@heroicons/react": "^2.0.12",
"@loadable/component": "^5.15.2",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@radix-ui/react-icons": "^1.1.1",
@@ -77,6 +78,7 @@
"@sd/config": "workspace:*",
"@types/babel-core": "^6.25.7",
"@types/byte-size": "^8.1.0",
"@types/loadable__component": "^5.13.4",
"@types/lodash": "^4.14.186",
"@types/node": "^18.8.2",
"@types/pretty-bytes": "^5.2.0",

View File

@@ -1,42 +1,44 @@
import loadable from '@loadable/component';
import { useCurrentLibrary, useInvalidateQuery } from '@sd/client';
import { Suspense, lazy } from 'react';
import { Suspense } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import { AppLayout } from './AppLayout';
import { useKeybindHandler } from './hooks/useKeyboardHandler';
const DebugScreen = lazy(() => import('./screens/Debug'));
const SettingsScreen = lazy(() => import('./screens/settings/Settings'));
const TagExplorer = lazy(() => import('./screens/TagExplorer'));
const PhotosScreen = lazy(() => import('./screens/Photos'));
const OverviewScreen = lazy(() => import('./screens/Overview'));
const ContentScreen = lazy(() => import('./screens/Content'));
const LocationExplorer = lazy(() => import('./screens/LocationExplorer'));
const OnboardingScreen = lazy(() => import('./components/onboarding/Onboarding'));
const NotFound = lazy(() => import('./NotFound'));
// Using React.lazy breaks hot reload so we don't use it.
const DebugScreen = loadable(() => import('./screens/Debug'));
const SettingsScreen = loadable(() => import('./screens/settings/Settings'));
const TagExplorer = loadable(() => import('./screens/TagExplorer'));
const PhotosScreen = loadable(() => import('./screens/Photos'));
const OverviewScreen = loadable(() => import('./screens/Overview'));
const ContentScreen = loadable(() => import('./screens/Content'));
const LocationExplorer = loadable(() => import('./screens/LocationExplorer'));
const OnboardingScreen = loadable(() => import('./components/onboarding/Onboarding'));
const NotFound = loadable(() => import('./NotFound'));
const AppearanceSettings = lazy(() => import('./screens/settings/client/AppearanceSettings'));
const ExtensionSettings = lazy(() => import('./screens/settings/client/ExtensionsSettings'));
const GeneralSettings = lazy(() => import('./screens/settings/client/GeneralSettings'));
const KeybindingSettings = lazy(() => import('./screens/settings/client/KeybindingSettings'));
const PrivacySettings = lazy(() => import('./screens/settings/client/PrivacySettings'));
const AboutSpacedrive = lazy(() => import('./screens/settings/info/AboutSpacedrive'));
const Changelog = lazy(() => import('./screens/settings/info/Changelog'));
const Support = lazy(() => import('./screens/settings/info/Support'));
const ContactsSettings = lazy(() => import('./screens/settings/library/ContactsSettings'));
const KeysSettings = lazy(() => import('./screens/settings/library/KeysSetting'));
const LibraryGeneralSettings = lazy(
const AppearanceSettings = loadable(() => import('./screens/settings/client/AppearanceSettings'));
const ExtensionSettings = loadable(() => import('./screens/settings/client/ExtensionsSettings'));
const GeneralSettings = loadable(() => import('./screens/settings/client/GeneralSettings'));
const KeybindingSettings = loadable(() => import('./screens/settings/client/KeybindingSettings'));
const PrivacySettings = loadable(() => import('./screens/settings/client/PrivacySettings'));
const AboutSpacedrive = loadable(() => import('./screens/settings/info/AboutSpacedrive'));
const Changelog = loadable(() => import('./screens/settings/info/Changelog'));
const Support = loadable(() => import('./screens/settings/info/Support'));
const ContactsSettings = loadable(() => import('./screens/settings/library/ContactsSettings'));
const KeysSettings = loadable(() => import('./screens/settings/library/KeysSetting'));
const LibraryGeneralSettings = loadable(
() => import('./screens/settings/library/LibraryGeneralSettings')
);
const LocationSettings = lazy(() => import('./screens/settings/library/LocationSettings'));
const NodesSettings = lazy(() => import('./screens/settings/library/NodesSettings'));
const SecuritySettings = lazy(() => import('./screens/settings/library/SecuritySettings'));
const SharingSettings = lazy(() => import('./screens/settings/library/SharingSettings'));
const SyncSettings = lazy(() => import('./screens/settings/library/SyncSettings'));
const TagsSettings = lazy(() => import('./screens/settings/library/TagsSettings'));
const ExperimentalSettings = lazy(() => import('./screens/settings/node/ExperimentalSettings'));
const LibrarySettings = lazy(() => import('./screens/settings/node/LibrariesSettings'));
const P2PSettings = lazy(() => import('./screens/settings/node/P2PSettings'));
const LocationSettings = loadable(() => import('./screens/settings/library/LocationSettings'));
const NodesSettings = loadable(() => import('./screens/settings/library/NodesSettings'));
const SecuritySettings = loadable(() => import('./screens/settings/library/SecuritySettings'));
const SharingSettings = loadable(() => import('./screens/settings/library/SharingSettings'));
const SyncSettings = loadable(() => import('./screens/settings/library/SyncSettings'));
const TagsSettings = loadable(() => import('./screens/settings/library/TagsSettings'));
const ExperimentalSettings = loadable(() => import('./screens/settings/node/ExperimentalSettings'));
const LibrarySettings = loadable(() => import('./screens/settings/node/LibrariesSettings'));
const P2PSettings = loadable(() => import('./screens/settings/node/P2PSettings'));
export function AppRouter() {
const { library } = useCurrentLibrary();

View File

@@ -16,8 +16,6 @@ export default function CreateLibraryDialog({
'library.create',
{
onSuccess: (library: any) => {
console.log('SUBMITTING');
setOpenCreateModal(false);
queryClient.setQueryData(['library.list'], (libraries: any) => [

View File

@@ -1,8 +1,7 @@
import { Transition } from '@headlessui/react';
import { XMarkIcon } from '@heroicons/react/24/solid';
import { Button } from '@sd/ui';
import { ButtonLink } from '@sd/ui';
import clsx from 'clsx';
import { useNavigate } from 'react-router-dom';
export interface ModalProps {
full?: boolean;
@@ -10,7 +9,6 @@ export interface ModalProps {
}
export const Modal: React.FC<ModalProps> = (props) => {
const navigate = useNavigate();
return (
<div
className={clsx('absolute w-screen h-screen z-30', {
@@ -29,17 +27,12 @@ export const Modal: React.FC<ModalProps> = (props) => {
>
<div
data-tauri-drag-region
onClick={() => navigate('/')}
className="absolute top-0 left-0 w-screen h-screen bg-white -z-50 rounded-2xl dark:bg-gray-800 bg-opacity-90"
/>
</Transition>
<Button
onClick={() => navigate('/')}
variant="gray"
className="!px-1.5 absolute top-2 right-2"
>
<ButtonLink to="/" variant="gray" className="!px-1.5 absolute top-2 right-2">
<XMarkIcon className="w-4 h-4" />
</Button>
</ButtonLink>
<Transition
show
className="flex flex-grow"

View File

@@ -197,7 +197,7 @@ export function Sidebar() {
{
name: 'Library Settings',
icon: CogIcon,
onPress: () => navigate('settings/library')
to: 'settings/library'
},
{
name: 'Add Library',

View File

@@ -1,10 +1,9 @@
import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline';
import { useBridgeMutation, useBridgeQuery } from '@sd/client';
import { LibraryConfigWrapped } from '@sd/client';
import { Button } from '@sd/ui';
import { Button, ButtonLink } from '@sd/ui';
import { DotsSixVertical } from 'phosphor-react';
import { useState } from 'react';
import { useNavigate } from 'react-router';
import CreateLibraryDialog from '../../../components/dialog/CreateLibraryDialog';
import DeleteLibraryDialog from '../../../components/dialog/DeleteLibraryDialog';
@@ -13,7 +12,6 @@ import { SettingsContainer } from '../../../components/settings/SettingsContaine
import { SettingsHeader } from '../../../components/settings/SettingsHeader';
function LibraryListItem(props: { library: LibraryConfigWrapped }) {
const navigate = useNavigate();
const [openDeleteModal, setOpenDeleteModal] = useState(false);
const { mutate: deleteLib, isLoading: libDeletePending } = useBridgeMutation('library.delete', {
@@ -22,14 +20,6 @@ function LibraryListItem(props: { library: LibraryConfigWrapped }) {
}
});
function handleEditLibrary() {
// switch library if requesting to edit non-current library
navigate('/settings/library');
// if (props.library.uuid !== store.currentLibraryUuid) {
// switchLibrary(props.library.uuid);
// }
}
return (
<Card>
<DotsSixVertical weight="bold" className="mt-[15px] mr-3 opacity-30" />
@@ -38,9 +28,9 @@ function LibraryListItem(props: { library: LibraryConfigWrapped }) {
<p className="mt-0.5 text-xs text-gray-200">{props.library.uuid}</p>
</div>
<div className="mt-2 space-x-2">
<Button variant="gray" className="!p-1.5" onClick={handleEditLibrary}>
<ButtonLink to="/settings/library" variant="gray" className="!p-1.5">
<PencilIcon className="w-4 h-4" />
</Button>
</ButtonLink>
<DeleteLibraryDialog libraryUuid={props.library.uuid}>
<Button variant="gray" className="!p-1.5">
<TrashIcon className="w-4 h-4" />

View File

@@ -1,64 +1,65 @@
{
"name": "@sd/ui",
"version": "0.0.0",
"license": "GPL-3.0-only",
"main": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./postcss": "./style/postcss.config.js",
"./tailwind": "./style/tailwind.js",
"./style": "./style/index.js",
"./style/style.scss": "./style/style.scss",
"./package.json": "./package.json"
},
"scripts": {
"build": "tsc",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook"
},
"dependencies": {
"@headlessui/react": "^1.7.3",
"@heroicons/react": "^2.0.12",
"@radix-ui/react-context-menu": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@tailwindcss/forms": "^0.5.3",
"class-variance-authority": "^0.2.3",
"clsx": "^1.2.1",
"phosphor-react": "^1.4.1",
"postcss": "^8.4.17",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-loading-icons": "^1.1.0",
"react-spring": "^9.5.5",
"storybook": "^6.5.12",
"tailwindcss": "^3.1.8"
},
"devDependencies": {
"@babel/core": "^7.19.3",
"@sd/config": "workspace:*",
"@storybook/addon-actions": "^6.5.12",
"@storybook/addon-essentials": "^6.5.12",
"@storybook/addon-interactions": "^6.5.12",
"@storybook/addon-links": "^6.5.12",
"@storybook/addon-postcss": "2.0.0",
"@storybook/builder-webpack5": "^6.5.12",
"@storybook/manager-webpack5": "^6.5.12",
"@storybook/preset-scss": "^1.0.3",
"@storybook/react": "^6.5.12",
"@storybook/testing-library": "^0.0.13",
"@tailwindcss/line-clamp": "^0.4.2",
"@tailwindcss/typography": "^0.5.7",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"autoprefixer": "^10.4.12",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"postcss-loader": "^7.0.1",
"sass": "^1.55.0",
"sass-loader": "^13.0.2",
"storybook-tailwind-dark-mode": "^1.0.15",
"style-loader": "^3.3.1",
"typescript": "^4.8.4"
}
"name": "@sd/ui",
"version": "0.0.0",
"license": "GPL-3.0-only",
"main": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./postcss": "./style/postcss.config.js",
"./tailwind": "./style/tailwind.js",
"./style": "./style/index.js",
"./style/style.scss": "./style/style.scss",
"./package.json": "./package.json"
},
"scripts": {
"build": "tsc",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook"
},
"dependencies": {
"@headlessui/react": "^1.7.3",
"@heroicons/react": "^2.0.12",
"@radix-ui/react-context-menu": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@tailwindcss/forms": "^0.5.3",
"class-variance-authority": "^0.2.3",
"clsx": "^1.2.1",
"phosphor-react": "^1.4.1",
"postcss": "^8.4.17",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "6.4.2",
"react-loading-icons": "^1.1.0",
"react-spring": "^9.5.5",
"storybook": "^6.5.12",
"tailwindcss": "^3.1.8"
},
"devDependencies": {
"@babel/core": "^7.19.3",
"@sd/config": "workspace:*",
"@storybook/addon-actions": "^6.5.12",
"@storybook/addon-essentials": "^6.5.12",
"@storybook/addon-interactions": "^6.5.12",
"@storybook/addon-links": "^6.5.12",
"@storybook/addon-postcss": "2.0.0",
"@storybook/builder-webpack5": "^6.5.12",
"@storybook/manager-webpack5": "^6.5.12",
"@storybook/preset-scss": "^1.0.3",
"@storybook/react": "^6.5.12",
"@storybook/testing-library": "^0.0.13",
"@tailwindcss/line-clamp": "^0.4.2",
"@tailwindcss/typography": "^0.5.7",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"autoprefixer": "^10.4.12",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"postcss-loader": "^7.0.1",
"sass": "^1.55.0",
"sass-loader": "^13.0.2",
"storybook-tailwind-dark-mode": "^1.0.15",
"style-loader": "^3.3.1",
"typescript": "^4.8.4"
}
}

View File

@@ -1,5 +1,6 @@
import clsx from 'clsx';
import { forwardRef } from 'react';
import { Link, LinkProps } from 'react-router-dom';
const sizes = {
default: 'py-1 px-3 text-md font-medium',
@@ -147,3 +148,34 @@ export const Button = forwardRef<
);
}
);
export const ButtonLink = forwardRef<
HTMLLinkElement,
ButtonBaseProps & LinkProps & React.RefAttributes<HTMLAnchorElement>
>(
(
{ loading, justifyLeft, className, pressEffect, noBorder, noPadding, size, variant, ...props },
ref
) => {
className = clsx(
'border rounded-md items-center transition-colors duration-100 cursor-default',
{ 'opacity-70': loading, '!p-1': noPadding },
{ 'justify-center': !justifyLeft },
sizes[size || 'default'],
variants[variant || 'default'],
{ 'active:translate-y-[1px]': pressEffect },
{ 'border-0': noBorder },
'disabled:opacity-50 disabled:cursor-not-allowed',
className
);
return (
<Link {...props} ref={ref as any} className={clsx(className, 'no-underline')}>
<>
{props.icon}
{props.children}
</>
</Link>
);
}
);

View File

@@ -2,17 +2,28 @@ import { Menu, Transition } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/24/solid';
import clsx from 'clsx';
import React from 'react';
import { Link } from 'react-router-dom';
import { Button } from './Button';
export type DropdownItem = {
name: string;
icon?: any;
disabled?: boolean;
selected?: boolean;
onPress?: () => any;
wrapItemComponent?: React.FC<{ children: React.ReactNode }>;
}[];
export type DropdownItem = (
| {
name: string;
icon?: any;
selected?: boolean;
to?: string;
wrapItemComponent?: React.FC<{ children: React.ReactNode }>;
}
| {
name: string;
icon?: any;
disabled?: boolean;
selected?: boolean;
onPress?: () => any;
to?: string;
wrapItemComponent?: React.FC<{ children: React.ReactNode }>;
}
)[];
export interface DropdownProps {
items: DropdownItem[];
@@ -81,28 +92,54 @@ export const Dropdown: React.FC<DropdownProps> = (props) => {
return (
<WrappedItem>
<button
onClick={button.onPress}
disabled={button?.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>
{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>
);
}}

BIN
pnpm-lock.yaml generated
View File

Binary file not shown.