mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-10-26 10:58:43 -04:00
working location creation + thumbnail gen
This commit is contained in:
1 parent
232acd3eff
commit
65b9dcf71c
15 files changed
+176
-70
No files matched your search
@@ -34,6 +34,7 @@
|
||||
"@radix-ui/react-dialog": "^0.1.7",
|
||||
"@radix-ui/react-dropdown-menu": "^0.1.6",
|
||||
"@radix-ui/react-icons": "^1.1.0",
|
||||
"@radix-ui/react-progress": "^0.1.4",
|
||||
"@radix-ui/react-slider": "^0.1.4",
|
||||
"@sd/client": "workspace:*",
|
||||
"@sd/core": "workspace:*",
|
||||
|
||||
@@ -135,10 +135,10 @@ function NotFound() {
|
||||
role="alert"
|
||||
className="flex flex-col items-center justify-center w-full h-full p-4 rounded-lg dark:text-white"
|
||||
>
|
||||
<p className="m-3 text-sm font-bold text-gray-400">Error: 404</p>
|
||||
<h1 className="text-2xl font-bold">Not found bozo.</h1>
|
||||
<p className="m-3 mt-20 text-sm font-semibold text-gray-500 uppercase">Error: 404</p>
|
||||
<h1 className="text-4xl font-bold">You chose nothingness.</h1>
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button variant="primary" className="mt-2" onClick={() => navigate(-1)}>
|
||||
<Button variant="primary" className="mt-4" onClick={() => navigate(-1)}>
|
||||
Go Back
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -14,11 +14,20 @@ import { useSearchParams } from 'react-router-dom';
|
||||
type ExplorerState = {
|
||||
selectedRowIndex: number;
|
||||
setSelectedRowIndex: (index: number) => void;
|
||||
locationId: number;
|
||||
setLocationId: (index: number) => void;
|
||||
newThumbnails: Record<string, boolean>;
|
||||
addNewThumbnail: (cas_id: string) => void;
|
||||
};
|
||||
|
||||
export const useExplorerState = create<ExplorerState>((set) => ({
|
||||
selectedRowIndex: 1,
|
||||
setSelectedRowIndex: (index) => set((state) => ({ ...state, selectedRowIndex: index }))
|
||||
setSelectedRowIndex: (index) => set((state) => ({ ...state, selectedRowIndex: index })),
|
||||
locationId: -1,
|
||||
setLocationId: (id: number) => set((state) => ({ ...state, locationId: id })),
|
||||
newThumbnails: {},
|
||||
addNewThumbnail: (cas_id: string) =>
|
||||
set((state) => ({ ...state, newThumbnails: { ...state.newThumbnails, [cas_id]: true } }))
|
||||
}));
|
||||
|
||||
interface IColumn {
|
||||
@@ -36,7 +45,7 @@ function ensureIsColumns<T extends IColumn[]>(data: T) {
|
||||
|
||||
const columns = ensureIsColumns([
|
||||
{ column: 'Name', key: 'name', width: 280 } as const,
|
||||
{ column: 'Size', key: 'size_in_bytes', width: 120 } as const,
|
||||
// { column: 'Size', key: 'size_in_bytes', width: 120 } as const,
|
||||
{ column: 'Type', key: 'extension', width: 100 } as const
|
||||
]);
|
||||
|
||||
@@ -51,7 +60,7 @@ export const FileList: React.FC<{ location_id: number; path: string; limit: numb
|
||||
|
||||
const path = props.path;
|
||||
|
||||
const { selectedRowIndex, setSelectedRowIndex } = useExplorerState();
|
||||
const { selectedRowIndex, setSelectedRowIndex, setLocationId } = useExplorerState();
|
||||
const [goingUp, setGoingUp] = useState(false);
|
||||
|
||||
const { data: currentDir } = useBridgeQuery('LibGetExplorerDir', {
|
||||
@@ -71,6 +80,10 @@ export const FileList: React.FC<{ location_id: number; path: string; limit: numb
|
||||
}
|
||||
}, [selectedRowIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
setLocationId(props.location_id);
|
||||
}, [props.location_id]);
|
||||
|
||||
useKey('ArrowUp', (e) => {
|
||||
e.preventDefault();
|
||||
setGoingUp(true);
|
||||
@@ -197,6 +210,9 @@ const RenderCell: React.FC<{ colKey?: ColumnKey; dirId?: number; file?: FilePath
|
||||
refetchOnWindowFocus: false
|
||||
});
|
||||
const location = useContext(LocationContext);
|
||||
const { newThumbnails } = useExplorerState();
|
||||
|
||||
const hasThumbnail = !!row?.has_local_thumbnail || !!newThumbnails[row?.temp_cas_id ?? ''];
|
||||
|
||||
switch (colKey) {
|
||||
case 'name':
|
||||
@@ -206,7 +222,7 @@ const RenderCell: React.FC<{ colKey?: ColumnKey; dirId?: number; file?: FilePath
|
||||
{row.is_dir ? (
|
||||
<img className="mt-0.5 pointer-events-none z-90" src="/svg/folder.svg" />
|
||||
) : (
|
||||
row.has_local_thumbnail &&
|
||||
hasThumbnail &&
|
||||
client?.data_path && (
|
||||
<img
|
||||
className="mt-0.5 pointer-events-none z-90"
|
||||
|
||||
@@ -9,8 +9,10 @@ import { TrafficLights } from '../os/TrafficLights';
|
||||
import { Button } from '../primitive';
|
||||
import { Dropdown } from '../primitive/Dropdown';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
import { useBridgeQuery } from '@sd/client';
|
||||
import { useBridgeCommand, useBridgeQuery } from '@sd/client';
|
||||
import { platform } from '@tauri-apps/api/os';
|
||||
import { dialog } from '@tauri-apps/api';
|
||||
import RunningJobsWidget from '../jobs/RunningJobsWidget';
|
||||
|
||||
interface SidebarProps extends DefaultProps {}
|
||||
|
||||
@@ -54,6 +56,7 @@ export function MacOSTrafficLights() {
|
||||
export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||
const [isMacos, setIsMacos] = useState(false);
|
||||
const { data: locations } = useBridgeQuery('SysGetLocations');
|
||||
const { mutate: createLocation } = useBridgeCommand('LocCreate');
|
||||
|
||||
useEffect(() => {
|
||||
platform().then((platform) => {
|
||||
@@ -72,7 +75,7 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-shrink-0 min-h-full px-3 pb-1 overflow-x-hidden overflow-y-scroll border-r border-gray-100 w-46 bg-gray-50 dark:bg-gray-850 dark:border-gray-600">
|
||||
<div className="flex flex-col w-56 min-h-full px-3 pb-1 overflow-x-hidden overflow-y-scroll border-r border-gray-100 bg-gray-50 dark:bg-gray-850 dark:border-gray-600">
|
||||
{isMacos ? (
|
||||
<>
|
||||
<MacOSTrafficLights /> <div className="mt-6" />
|
||||
@@ -157,9 +160,17 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div className="w-full px-2 py-1.5 mt-1 text-xs font-bold text-center text-gray-400 dark:text-gray-500 border border-dashed rounded border-transparent cursor-normal border-gray-350 dark:border-gray-550">
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
dialog.open({ directory: true }).then((result) => {
|
||||
createLocation({ path: result });
|
||||
});
|
||||
}}
|
||||
className="w-full px-2 py-1.5 mt-1 text-xs font-bold text-center text-gray-400 dark:text-gray-500 border border-dashed rounded border-transparent cursor-normal border-gray-350 dark:border-gray-550 hover:dark:border-gray-500 transition"
|
||||
>
|
||||
Add Location
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<Heading>Tags</Heading>
|
||||
@@ -176,6 +187,9 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-grow" />
|
||||
<RunningJobsWidget />
|
||||
{/* <div className="flex w-full">
|
||||
</div> */}
|
||||
<div className="mb-2">
|
||||
<NavLink to="/settings/general">
|
||||
{({ isActive }) => (
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { useBridgeQuery } from '@sd/client';
|
||||
import ProgressBar from '../primitive/ProgressBar';
|
||||
import { Transition } from '@headlessui/react';
|
||||
|
||||
export default function RunningJobsWidget() {
|
||||
const { data: jobs } = useBridgeQuery('JobGetRunning');
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
{jobs?.map((job) => (
|
||||
<Transition
|
||||
show={true}
|
||||
enter="transition-translate ease-in-out duration-200"
|
||||
enterFrom="translate-y-24"
|
||||
enterTo="translate-y-0"
|
||||
leave="transition-translate ease-in-out duration-200"
|
||||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-24"
|
||||
>
|
||||
<div key={job.id} className="flex flex-col px-2 pt-1.5 pb-2 bg-gray-700 rounded">
|
||||
{/* <span className="mb-0.5 text-tiny font-bold text-gray-400">{job.status} Job</span> */}
|
||||
<span className="mb-1.5 truncate text-gray-450 text-tiny">{job.message}</span>
|
||||
<ProgressBar value={job.completed_task_count} total={job.task_count} />
|
||||
</div>
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,22 @@
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/outline';
|
||||
import clsx from 'clsx';
|
||||
import { ArrowsLeftRight, Cloud, FolderPlus, Key, Tag, TerminalWindow } from 'phosphor-react';
|
||||
import {
|
||||
ArrowsClockwise,
|
||||
ArrowsLeftRight,
|
||||
Cloud,
|
||||
FolderPlus,
|
||||
Key,
|
||||
Tag,
|
||||
TerminalWindow
|
||||
} from 'phosphor-react';
|
||||
import React from 'react';
|
||||
import { ButtonProps } from '../primitive';
|
||||
import { Shortcut } from '../primitive/Shortcut';
|
||||
import { DefaultProps } from '../primitive/types';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useBridgeCommand } from '@sd/client';
|
||||
import { useExplorerState } from '../file/FileList';
|
||||
|
||||
export interface TopBarProps extends DefaultProps {}
|
||||
export interface TopBarButtonProps extends ButtonProps {
|
||||
@@ -38,7 +48,12 @@ const TopBarButton: React.FC<TopBarButtonProps> = ({ icon: Icon, ...props }) =>
|
||||
};
|
||||
|
||||
export const TopBar: React.FC<TopBarProps> = (props) => {
|
||||
let location = useLocation();
|
||||
const { locationId } = useExplorerState();
|
||||
const { mutate: generateThumbsForLocation } = useBridgeCommand('GenerateThumbsForLocation', {
|
||||
onMutate: (data) => {
|
||||
console.log('GenerateThumbsForLocation', data);
|
||||
}
|
||||
});
|
||||
let navigate = useNavigate();
|
||||
return (
|
||||
<>
|
||||
@@ -75,8 +90,10 @@ export const TopBar: React.FC<TopBarProps> = (props) => {
|
||||
<TopBarButton icon={Key} />
|
||||
<TopBarButton icon={Cloud} />
|
||||
<TopBarButton
|
||||
icon={ArrowsLeftRight}
|
||||
onClick={() => invoke('start_watcher', { path: '/Users/jamie' })}
|
||||
icon={ArrowsClockwise}
|
||||
onClick={() => {
|
||||
generateThumbsForLocation({ id: locationId, path: '' });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import * as ProgressPrimitive from '@radix-ui/react-progress';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
interface Props {
|
||||
value: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
const ProgressBar = (props: Props) => {
|
||||
const percentage = Math.round((props.value / props.total) * 100);
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
value={percentage}
|
||||
className="w-full h-1 overflow-hidden bg-gray-200 rounded-full dark:bg-gray-500"
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
style={{ width: `${percentage}%` }}
|
||||
className="h-full duration-300 ease-in-out bg-primary "
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressBar;
|
||||
@@ -3,12 +3,17 @@ import { emit, listen, Event } from '@tauri-apps/api/event';
|
||||
// import { useExplorerStore } from '../store/explorer';
|
||||
import { CoreEvent } from '../../../../core';
|
||||
import { useQuery, useQueryClient } from 'react-query';
|
||||
import { useExplorerState } from '../components/file/FileList';
|
||||
|
||||
export function useCoreEvents() {
|
||||
const client = useQueryClient();
|
||||
const { addNewThumbnail } = useExplorerState();
|
||||
useEffect(() => {
|
||||
listen('core_event', (e: Event<CoreEvent>) => {
|
||||
switch (e.payload?.key) {
|
||||
case 'NewThumbnail':
|
||||
addNewThumbnail(e.payload.data.cas_id);
|
||||
break;
|
||||
case 'InvalidateQuery':
|
||||
case 'InvalidateQueryDebounced':
|
||||
let query = [e.payload.data.key];
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useBridgeCommand, useBridgeQuery } from '@sd/client';
|
||||
|
||||
export default function GeneralSettings() {
|
||||
const { data: volumes } = useBridgeQuery('SysGetVolumes');
|
||||
const [tempWatchDir, setTempWatchDir] = useState('/users/jamie/Projects');
|
||||
const [tempWatchDir, setTempWatchDir] = useState('/users/jamie/Desktop/Pictures');
|
||||
|
||||
const [fakeSliderVal, setFakeSliderVal] = useState([30, 0]);
|
||||
|
||||
@@ -33,24 +33,12 @@ export default function GeneralSettings() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-grow max-w-4xl space-y-4">
|
||||
<div className="mt-3 mb-6">
|
||||
<div className="mt-3 mb-3">
|
||||
<h1 className="text-2xl font-bold">General Settings</h1>
|
||||
<p className="mt-1 text-sm text-gray-400">Basic settings related to this client</p>
|
||||
{/* <hr className="mt-4 border-gray-550" /> */}
|
||||
</div>
|
||||
<div className="flex flex-row pb-4 space-x-2">
|
||||
<Button
|
||||
className="w-40"
|
||||
variant="gray"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
generateThumbsForLocation({
|
||||
id: 1
|
||||
})
|
||||
}
|
||||
>
|
||||
Generate Thumbnails
|
||||
</Button>
|
||||
<Button className="w-40" variant="gray" size="sm" onClick={() => {}}>
|
||||
Open data folder
|
||||
</Button>
|
||||
@@ -58,7 +46,7 @@ export default function GeneralSettings() {
|
||||
Purge database
|
||||
</Button>
|
||||
</div>
|
||||
<InputContainer
|
||||
{/* <InputContainer
|
||||
title="Test scan directory"
|
||||
description="This will create a job to scan the directory you specify to the database."
|
||||
>
|
||||
@@ -81,38 +69,8 @@ export default function GeneralSettings() {
|
||||
Scan Now
|
||||
</Button>
|
||||
</div>
|
||||
</InputContainer>
|
||||
</InputContainer> */}
|
||||
|
||||
<ReactJson
|
||||
// collapsed
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
theme="ocean"
|
||||
src={{ runningJobs: jobs }}
|
||||
style={{
|
||||
padding: 20,
|
||||
borderRadius: 5,
|
||||
backgroundColor: '#101016',
|
||||
border: 1,
|
||||
borderColor: '#1E1E27',
|
||||
borderStyle: 'solid'
|
||||
}}
|
||||
/>
|
||||
<ReactJson
|
||||
// collapsed
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
theme="ocean"
|
||||
src={{ jobsHistory: [...(jobsHistory || [])].reverse() }}
|
||||
style={{
|
||||
padding: 20,
|
||||
borderRadius: 5,
|
||||
backgroundColor: '#101016',
|
||||
border: 1,
|
||||
borderColor: '#1E1E27',
|
||||
borderStyle: 'solid'
|
||||
}}
|
||||
/>
|
||||
<InputContainer
|
||||
title="Locations"
|
||||
description="Local cache storage for media previews and thumbnails."
|
||||
@@ -146,13 +104,12 @@ export default function GeneralSettings() {
|
||||
/>
|
||||
</InputContainer>
|
||||
<InputContainer
|
||||
title="Media cache directory"
|
||||
title="A long input"
|
||||
description="Local cache storage for media previews and thumbnails."
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<Input
|
||||
className="flex-grow"
|
||||
value={'uuuuuu'}
|
||||
placeholder="/users/jamie/Library/Application Support/spacedrive/cache"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -140,9 +140,9 @@ pub async fn scan_path(
|
||||
on_progress(vec![
|
||||
ScanProgress::SavedChunks(i as usize),
|
||||
ScanProgress::Message(format!(
|
||||
"Writing {} files to db at chunk {}",
|
||||
chunk.len(),
|
||||
i
|
||||
"Writing {} of {} to db",
|
||||
i * chunk.len(),
|
||||
paths.len(),
|
||||
)),
|
||||
]);
|
||||
|
||||
@@ -220,7 +220,7 @@ fn prepare_values(
|
||||
location.id,
|
||||
materialized_path,
|
||||
name,
|
||||
extension,
|
||||
extension.to_lowercase(),
|
||||
parent_id
|
||||
.clone()
|
||||
.map(|id| format!("\"{}\"", &id))
|
||||
|
||||
+10
-1
@@ -1,13 +1,14 @@
|
||||
use crate::job::jobs::JobReportUpdate;
|
||||
use crate::prisma::FilePathData;
|
||||
use crate::state::client;
|
||||
use crate::sys;
|
||||
use crate::{
|
||||
job::{jobs::Job, worker::WorkerContext},
|
||||
prisma::FilePath,
|
||||
CoreContext,
|
||||
};
|
||||
use crate::{sys, CoreEvent};
|
||||
use anyhow::Result;
|
||||
use futures::executor::block_on;
|
||||
use image::*;
|
||||
use prisma_client_rust::or;
|
||||
use std::fs;
|
||||
@@ -47,6 +48,8 @@ impl Job for ThumbnailJob {
|
||||
|
||||
println!("Found {:?} files", image_files.len());
|
||||
|
||||
let is_background = self.background.clone();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
ctx.progress(vec![
|
||||
JobReportUpdate::TaskCount(image_files.len()),
|
||||
@@ -76,6 +79,12 @@ impl Job for ThumbnailJob {
|
||||
}
|
||||
|
||||
ctx.progress(vec![JobReportUpdate::CompletedTaskCount(i + 1)]);
|
||||
|
||||
if !is_background {
|
||||
block_on(ctx.core_ctx.emit(CoreEvent::NewThumbnail {
|
||||
cas_id: checksum.to_string(),
|
||||
}));
|
||||
};
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
+2
-1
@@ -231,7 +231,7 @@ impl Core {
|
||||
}
|
||||
|
||||
async fn exec_command(&mut self, cmd: ClientCommand) -> Result<CoreResponse, CoreError> {
|
||||
info!("Core command: {:?}", cmd);
|
||||
println!("Core command: {:?}", cmd);
|
||||
let ctx = self.get_context();
|
||||
Ok(match cmd {
|
||||
// CRUD for locations
|
||||
@@ -359,6 +359,7 @@ pub enum CoreEvent {
|
||||
InvalidateQuery(ClientQuery),
|
||||
InvalidateQueryDebounced(ClientQuery),
|
||||
InvalidateResource(CoreResource),
|
||||
NewThumbnail { cas_id: String },
|
||||
Log { message: String },
|
||||
DatabaseDisconnected { reason: Option<String> },
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
prisma::{FilePath, Location},
|
||||
state::client,
|
||||
sys::{volumes, volumes::Volume},
|
||||
CoreContext,
|
||||
ClientQuery, CoreContext, CoreEvent,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
@@ -176,6 +176,11 @@ pub async fn create_location(ctx: &CoreContext, path: &str) -> Result<LocationRe
|
||||
Ok(_) => (),
|
||||
Err(e) => Err(LocationError::DotfileWriteFailure(e, path.to_string()))?,
|
||||
}
|
||||
|
||||
ctx
|
||||
.emit(CoreEvent::InvalidateQuery(ClientQuery::SysGetLocations))
|
||||
.await;
|
||||
|
||||
location
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,6 +7,20 @@ module.exports = {
|
||||
darkMode: 'media',
|
||||
mode: 'jit',
|
||||
theme: {
|
||||
fontSize: {
|
||||
'tiny': '.65rem',
|
||||
'xs': '.75rem',
|
||||
'sm': '.84rem',
|
||||
'base': '1rem',
|
||||
'lg': '1.125rem',
|
||||
'xl': '1.25rem',
|
||||
'2xl': '1.5rem',
|
||||
'3xl': '1.875rem',
|
||||
'4xl': '2.25rem',
|
||||
'5xl': '3rem',
|
||||
'6xl': '4rem',
|
||||
'7xl': '5rem'
|
||||
},
|
||||
extend: {
|
||||
boxShadow: {
|
||||
box: '0px 4px 9px rgba(0, 0, 0, 0.05)',
|
||||
|
||||
Generated
+13
@@ -18,6 +18,7 @@ importers:
|
||||
'@radix-ui/react-dialog': ^0.1.7
|
||||
'@radix-ui/react-dropdown-menu': ^0.1.6
|
||||
'@radix-ui/react-icons': ^1.1.0
|
||||
'@radix-ui/react-progress': ^0.1.4
|
||||
'@radix-ui/react-slider': ^0.1.4
|
||||
'@sd/client': workspace:*
|
||||
'@sd/core': workspace:*
|
||||
@@ -74,6 +75,7 @@ importers:
|
||||
'@radix-ui/react-dialog': 0.1.7_761282c47ea27a9f40e1344337910648
|
||||
'@radix-ui/react-dropdown-menu': 0.1.6_761282c47ea27a9f40e1344337910648
|
||||
'@radix-ui/react-icons': 1.1.0_react@18.0.0
|
||||
'@radix-ui/react-progress': 0.1.4_react@18.0.0
|
||||
'@radix-ui/react-slider': 0.1.4_react@18.0.0
|
||||
'@sd/client': link:../../packages/client
|
||||
'@sd/core': link:../../core
|
||||
@@ -826,6 +828,17 @@ packages:
|
||||
react: 18.0.0
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-progress/0.1.4_react@18.0.0:
|
||||
resolution: {integrity: sha512-Q+H7r/S0HMlwVriksgyUgBtol5TgkNllQPVtaXDDaeJBNFK2uiToHBp3j1Gi88/4Fw+EKT+SyWpeM/5nVtGujw==}
|
||||
peerDependencies:
|
||||
react: ^16.8 || ^17.0
|
||||
dependencies:
|
||||
'@babel/runtime': 7.17.9
|
||||
'@radix-ui/react-context': 0.1.1_react@18.0.0
|
||||
'@radix-ui/react-primitive': 0.1.4_react@18.0.0
|
||||
react: 18.0.0
|
||||
dev: false
|
||||
|
||||
/@radix-ui/react-roving-focus/0.1.5_react@18.0.0:
|
||||
resolution: {integrity: sha512-ClwKPS5JZE+PaHCoW7eu1onvE61pDv4kO8W4t5Ra3qMFQiTJLZMdpBQUhksN//DaVygoLirz4Samdr5Y1x1FSA==}
|
||||
peerDependencies:
|
||||
|
||||
Reference in new issue
Block a user