videos in explorer section, update search video, and spacing

This commit is contained in:
ameer2468
2024-09-18 15:28:06 +03:00
parent d4adecd2bc
commit eb4fac8d73
13 changed files with 221 additions and 51 deletions

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,51 +1,65 @@
import React from 'react';
import { twMerge } from 'tailwind-merge';
import libraryArt from '~/assets/bento/library.svg?url';
import lockArt from '~/assets/bento/lock.svg?url';
import tagsArt from '~/assets/bento/tags.svg?url';
import { BentoBox } from '~/components/bento-box';
'use client';
import { AnimatePresence } from 'framer-motion';
import { useState } from 'react';
import { SelectedVideo, Video } from '~/components/video';
const videos: {
title: string;
src: string;
description: string;
}[] = [
{
title: 'Drag and Drop',
src: '/videos/Spacedrive_DragAndDrop.webm',
description: 'Easily drag and drop files or folders.'
},
{
title: 'Tabs',
src: '/videos/Spacedrive_Tabs.webm',
description: 'Browse seamlessly with multiple tabs.'
},
{
title: 'Quick Preview',
src: '/videos/Spacedrive_QuickPreview.webm',
description: 'Instantly preview files and object data.'
}
];
export const Explorer = () => {
const [selectedVideo, setSelectedVideo] = useState<null | string>(null);
return (
<div className="container mx-auto flex flex-col flex-wrap items-center gap-10 p-4">
<h1 className="flex-1 self-start text-2xl font-semibold leading-8 md:text-3xl md:leading-10 lg:self-start">
Explorer.{' '}
<span className="bg-gradient-to-r from-zinc-400 to-zinc-600 bg-clip-text text-transparent">
{/* Some controlled line breaks here based on breakpoint to make sure the breaks looks nice always :) */}
<br className="lg:hidden" />
Browse and manage your data
<br className="sm:hidden" /> like never before.
</span>
</h1>
<div
className={twMerge(
'grid w-full grid-cols-3 grid-rows-2 gap-5 max-lg:grid-cols-1 lg:grid-rows-1'
)}
>
<BentoBox
className="bento-border-top lg:bento-border-left"
imageSrc={libraryArt}
imageAlt="Library"
title="Seamless Sync & Access"
titleColor="#63C3F3"
description="Whether online or offline, instantly access your data anytime, anywhere. Keeping everything updated and available across your devices."
/>
<BentoBox
imageSrc={lockArt}
imageAlt="Lock"
title="Privacy & Control"
titleColor="#6368F3"
description="Your data is yours. With Spacedrives top-notch security, only you can access your information — no third parties, no exceptions."
/>
<BentoBox
className="bento-border-bottom lg:bento-border-right"
imageSrc={tagsArt}
imageAlt="Tags"
title="Effortless Organization"
titleColor="#DF63F3"
description="Keep your digital life organized with automatic categorization and smart structuring, making it easy to find what you need instantly."
/>
<>
{selectedVideo ? (
<AnimatePresence>
<SelectedVideo src={selectedVideo} />
</AnimatePresence>
) : null}
<div className="container mx-auto flex flex-col flex-wrap items-center gap-10 p-4">
<h1 className="flex-1 self-start text-2xl font-semibold leading-8 md:text-3xl md:leading-10 lg:self-start">
Explorer.{' '}
<span className="bg-gradient-to-r from-zinc-400 to-zinc-600 bg-clip-text text-transparent">
{/* Some controlled line breaks here based on breakpoint to make sure the breaks looks nice always :) */}
<br className="lg:hidden" />
Browse and manage your data
<br className="sm:hidden" /> like never before.
</span>
</h1>
<div className="grid w-full grid-cols-1 gap-10 md:grid-cols-3 md:gap-4">
{videos.map((video) => (
<div className="h-fit" key={video.src}>
<Video
setSelectedVideo={setSelectedVideo}
layoutId={`video-${video.src}`}
onClick={() => setSelectedVideo(video.src)}
{...video}
/>
<h2 className="mt-5 text-lg font-bold text-white">{video.title}</h2>
<p className="text-md text-ink-dull">{video.description}</p>
</div>
))}
</div>
</div>
</div>
</>
);
};

View File

@@ -4,3 +4,4 @@ export * from './explorer';
export * from './github';
export * from './features';
export * from './search';
export * from './tags';

View File

@@ -20,7 +20,7 @@ export const Search = () => {
muted
controls={false}
loop
src="/videos/search.mp4"
src="/videos/Spacedrive_search.webm"
/>
</div>
</div>

View File

@@ -0,0 +1,61 @@
'use client';
import { AnimatePresence } from 'framer-motion';
import React from 'react';
import { SelectedVideo, Video } from '~/components/video';
const videos: {
title: string;
src: string;
description: string;
}[] = [
{
title: 'Tag Assignment mode',
src: '/videos/Spacedrive_tagmode.webm',
description: 'Assign tags to files and folders quickly and easily'
},
{
title: 'Contextual Tagging',
src: '/videos/Spacedrive_tags.webm',
description: 'Tag files and folders directly from the right-click menu'
}
];
const Tags = () => {
const [selectedVideo, setSelectedVideo] = React.useState<null | string>(null);
return (
<>
{selectedVideo ? (
<AnimatePresence>
<SelectedVideo src={selectedVideo} />
</AnimatePresence>
) : null}
<div className="container mx-auto flex flex-col flex-wrap items-center gap-10 p-4">
<h1 className="flex-1 self-start text-2xl font-semibold leading-8 md:text-3xl md:leading-10 lg:self-start">
Multiple ways to set tags.{' '}
<span className="bg-gradient-to-r from-zinc-400 to-zinc-600 bg-clip-text text-transparent">
{/* Some controlled line breaks here based on breakpoint to make sure the breaks looks nice always :) */}
<br className="lg:hidden" />
quickly organize your files.
</span>
</h1>
<div className="grid w-full grid-cols-1 gap-10 md:grid-cols-2 md:gap-4">
{videos.map((video) => (
<div className="h-fit" key={video.src}>
<Video
setSelectedVideo={setSelectedVideo}
layoutId={`video-${video.src}`}
onClick={() => setSelectedVideo(video.src)}
{...video}
/>
<h2 className="mt-5 text-lg font-bold text-white">{video.title}</h2>
<p className="text-md text-ink-dull">{video.description}</p>
</div>
))}
</div>
</div>
</>
);
};
export default Tags;

View File

@@ -1,5 +1,7 @@
import { Assistant, Explorer, Features, Github, Header, Search } from '~/app/page-sections';
import Tags from './page-sections/tags';
export const metadata = {
title: 'Spacedrive — A file manager from the future.',
description:
@@ -14,10 +16,11 @@ export const metadata = {
export default function Page() {
return (
<div className="flex flex-col gap-6 md:gap-[200px]">
<div className="flex flex-col gap-6 md:gap-[150px]">
<Header />
<Explorer />
<Features />
<Explorer />
<Tags />
<Search />
<Assistant />
<Github />

View File

@@ -0,0 +1,66 @@
import clsx from 'clsx';
import { motion, MotionProps } from 'framer-motion';
import { ComponentProps, useRef } from 'react';
import { useClickOutside } from '~/hooks/useClickOutside';
type Props = ComponentProps<'video'> &
MotionProps & {
containerClassName?: string;
setSelectedVideo: (src: string | null) => void;
};
const Video = ({ containerClassName, setSelectedVideo, ...rest }: Props) => {
const videoRef = useRef<HTMLVideoElement>(null);
useClickOutside(videoRef, () => setSelectedVideo(null));
return (
<div className={clsx(containerClassName)}>
<motion.video
style={{
borderRadius: 12 // for framer-motion
}}
ref={videoRef}
whileHover={{
scale: 1.05
}}
whileTap={{ scale: 1 }}
autoPlay
loop
muted
playsInline
className="size-full cursor-pointer object-cover"
{...rest}
/>
</div>
);
};
const SelectedVideo = ({ src }: { src: string }) => {
return (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
layout
exit={{ opacity: 0 }}
className="bg-opacity/50 fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-md"
/>
<div className="fixed inset-0 z-[60] flex items-center justify-center p-5 md:p-0">
<motion.video
src={src}
style={{
borderRadius: 12
}}
transition={{ duration: 0.3, ease: 'easeInOut' }}
autoPlay
layoutId={`video-${src}`}
loop
muted
playsInline
className="object-cover md:size-3/5"
/>
</div>
</>
);
};
export { SelectedVideo, Video };

View File

@@ -0,0 +1,25 @@
import { useEffect } from "react";
export const useClickOutside = (
ref: React.RefObject<HTMLElement>,
handler: (event: MouseEvent | TouchEvent) => void
): void => {
useEffect(() => {
const listener = (event: MouseEvent | TouchEvent) => {
if (!ref.current || ref.current.contains(event.target as Node)) {
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
}, [ref, handler]);
}

View File

@@ -12,8 +12,8 @@ const SUPPORTED_ICONS = ['Document', 'Code', 'Text', 'Config'];
const positionConfig: Record<string, string> = {
Text: 'flex h-full w-full items-center justify-center',
Code: 'flex h-full w-full items-center justify-center',
Config: 'flex h-full w-full items-center justify-center'
Code: 'flex h-full w-full items-center justify-center pt-[18px]',
Config: 'flex h-full w-full items-center justify-center pt-[18px]'
};
const LayeredFileIcon = forwardRef<HTMLImageElement, LayeredFileIconProps>(
@@ -38,7 +38,7 @@ const LayeredFileIcon = forwardRef<HTMLImageElement, LayeredFileIconProps>(
className={clsx('pointer-events-none absolute bottom-0 right-0', positionClass)}
>
<Suspense>
<IconComponent viewBox="0 0 16 16" height="40%" width="40%" />
<IconComponent viewBox="0 0 16 16" height="50%" width="50%" />
</Suspense>
</div>
</div>