Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: playground new layout #89

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions apps/playground-web/components/Overlays/CommandPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import SearchBox from '../Search/SearchBox';
import { Terminal, X } from 'lucide-react';
import { useCommandContext } from '@/context/command';

const CommandPanel = () => {
const { setIsOpen, isOpen } = useCommandContext();

return (
<div
className={`h-[100vh] px-4 bg-white border-l border-gray-200 transition-all duration-300 w-[25%] fixed top-0 right-0 ${
isOpen ? 'translate-x-0' : 'translate-x-full'
}`}
>
<div className="flex py-4 items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Terminal className="w-5 h-5" />
<p className="text-lg font-medium">DiceDB Commands</p>
</div>

<button
onClick={() => setIsOpen(false)}
className="flex items-center hover:cursor-pointer hover:bg-gray-100 rounded-md p-1 gap-2"
>
<X className="w-5 h-5" />
</button>
</div>
<SearchBox />
</div>
);
};

export default CommandPanel;
125 changes: 79 additions & 46 deletions apps/playground-web/components/Playground/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,18 @@ import { TerminalUI } from './TerminalUI';
import Link from 'next/link';
import { Button } from '@dicedb/ui/button';
import GitHub from '@mui/icons-material/GitHub';

// utils

export default function Playground() {
return (
<div
data-testid="playground"
className="container mx-auto p-4 lg:p-0 flex flex-col flex-grow h-screen bg-white text-gray-900"
>
<Header />

<main
data-testid="playground-main"
className="h-full flex flex-col lg:flex-row gap-10 flex-grow overflow-hidden"
>
<div className="h-4/6 lg:h-full w-full lg:w-[60%] flex flex-col">
<TerminalUI />
</div>
<div
data-testid="searchbox-container"
className="h-2/6 lg:h-full w-full lg:w-[40%] flex flex-col pb-4"
>
<div
data-testid="searchbox-wrapper"
className="h-full w-full flex-grow border border-gray-400 bg-gray-100 p-4 pb-0 rounded-lg shadow-md"
>
<SearchBox />
</div>
</div>
</main>
</div>
);
}
import { Terminal } from 'lucide-react';
import CommandProvider, { useCommandContext } from '@/context/command';
import CommandPanel from '../Overlays/CommandPanel';

function Header() {
const { setIsOpen } = useCommandContext();
return (
<header
data-testid="playground-header"
className="navbar flex items-center justify-between py-5"
>
<div className="flex items-center">
<div className="flex gap-2 items-center">
<Link
href="https://dicedb.io"
target="_blank"
Expand All @@ -64,24 +35,86 @@ function Header() {
unoptimized
/>
</Link>
<h2 className="font-light text-2xl ml-2">PlayGround</h2>
<h2 className="font-light text-2xl">PlayGround</h2>
</div>
<Link
href="https://github.com/DiceDB/playground-mono/issues/new/choose"
target="_blank"
rel="noopener noreferrer"
aria-label="Submit an issue or feedback"
data-testid="submit-issue-link"
>
<div className="flex items-center gap-2">
<Link
href="https://github.com/DiceDB/playground-mono/issues/new/choose"
target="_blank"
rel="noopener noreferrer"
aria-label="Submit an issue or feedback"
data-testid="submit-issue-link"
>
<Button
variant="outline"
className="w-full mt-2 gap-2 border-1 border-gray-700 text-gray-700 hover:scale-95 transition flex items-center justify-center rounded-lg"
data-testid="submit-issue-button"
>
<GitHub fontSize="small" />
Submit an Issue
</Button>
</Link>
<Button
onClick={() => setIsOpen(true)}
variant="outline"
className="!w-full mt-2 !border-1 !border-gray-700 bg-blue-50 hover:text-blue text-black hover:text-blue-600 flex items-center justify-center rounded-lg"
className="mt-2 hidden lg:flex w-fit gap-2 border-1 border-gray-700 !bg-gray-700 hover:scale-95 transition text-white items-center justify-center rounded-lg"
data-testid="submit-issue-button"
>
Submit an Issue
<GitHub className="ml-2 h-4 w-4" />
<Terminal className="h-4 w-4" />
Commands
</Button>
</Link>
</div>
</header>
);
}

function Commands() {
return (
<>
<div data-testid="searchbox-container" className="hidden lg:block">
<CommandPanel />
</div>
<div
data-testid="searchbox-container"
className="h-2/6 lg:h-full w-full lg:w-[40%] flex lg:hidden flex-col pb-4"
>
<div
data-testid="searchbox-wrapper"
className="h-full w-full flex-grow border border-gray-400 bg-gray-100 p-4 pb-0 rounded-lg shadow-md"
>
<SearchBox />
</div>
</div>
</>
);
}

function PlaygroundUI() {
const { isOpen } = useCommandContext();
return (
<div
data-testid="playground"
className={`p-4 lg:p-0 flex flex-col h-screen bg-white text-gray-900 ${isOpen ? 'transition-all duration-200 lg:ml-[4%] lg:mr-[28%]' : 'lg:mx-[4%] mx-auto'}`}
>
<Header />

<main
data-testid="playground-main"
className="h-full flex flex-col lg:flex-row gap-10 lg:gap-0 flex-grow overflow-hidden"
>
<div className="h-4/6 lg:h-full w-full flex flex-col">
<TerminalUI />
</div>
<Commands />
</main>
</div>
);
}

export default function Playground() {
return (
<CommandProvider>
<PlaygroundUI />
</CommandProvider>
);
}
36 changes: 23 additions & 13 deletions apps/playground-web/components/Playground/TerminalUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function TerminalUI({ initialCommandsLeft = 1000 }) {
return (
<>
<div
className="h-full max-h-[26rem] bg-gray-900 rounded-lg flex flex-col"
className="h-full bg-gray-900 rounded-xl flex flex-col"
data-testid="terminal-container"
>
<div className="bg-gray-900 px-4 py-4 flex items-center rounded-lg">
Expand All @@ -27,7 +27,7 @@ export function TerminalUI({ initialCommandsLeft = 1000 }) {
</div>
</div>
<div
className="h-full md:h-[30rem] bg-gray-100 rounded-lg overflow-hidden shadow-md"
className="h-full flex-grow bg-gray-100 rounded-lg overflow-hidden shadow-md"
data-testid="shell-container"
>
<Shell onCommandExecuted={handleCommandExecuted} />
Expand All @@ -50,8 +50,9 @@ function TerminalCounter({
}) {
return (
<div className="flex flex-col" data-testid="terminal-counter">
<div className="flex flex-row justify-between text-gray-900 mt-4">
<div className="flex flex-row items-center space-x-2">
<div className="flex items-center justify-between text-gray-900 my-4">
<InstanceMessage extraClassname="hidden lg:flex" />
<div className="w-full flex justify-between md:justify-end items-center space-x-2">
<div
className="flex items-center justify-between gap-1 border border-gray-400 text-sm bg-transparent p-3 rounded-lg"
data-testid="cleanup-timer"
Expand All @@ -62,9 +63,6 @@ function TerminalCounter({
{formatTime(cleanupTimeLeft)} mins
</span>
</div>
</div>

<div className="flex flex-row items-center space-x-2">
<div
className="flex items-center justify-between gap-1 border border-gray-400 text-sm bg-transparent p-3 rounded-lg"
data-testid="commands-left"
Expand All @@ -77,12 +75,24 @@ function TerminalCounter({
</div>
</div>
</div>
<div className="flex flex-row items-center mt-5 justify-center md:justify-start">
<Info className="w-4 h-4 text-gray-500 mr-1" />
<p className="text-sm text-gray-500">
DiceDB instance is shared across all users.
</p>
</div>
<InstanceMessage extraClassname="lg:hidden" />
</div>
);
}

const InstanceMessage = ({
extraClassname = '',
}: {
extraClassname?: string;
}) => {
return (
<div
className={`flex flex-row w-full items-center gap-2 justify-center md:justify-start ${extraClassname}`}
>
<Info className="w-4 h-4 text-gray-500" />
<p className="text-sm text-gray-500">
DiceDB instance is shared across all users.
</p>
</div>
);
};
6 changes: 3 additions & 3 deletions apps/playground-web/components/Search/CommandPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function CommandPage({
return (
<div
data-testid="command-page"
className="w-full p-6 bg-gray-100 text-white rounded-lg shadow-lg border border-gray-700 mb-4"
className="w-full p-6 bg-gray-100 text-white rounded-lg border border-gray-700/30 mb-4"
>
<h2
data-testid="command-title"
Expand All @@ -41,7 +41,7 @@ export default function CommandPage({
</h2>

<div className="flex items-center justify-between mb-4 pt-4">
<h3 className="text-gray-700 text-2xl font-semibold">Syntax</h3>
<h3 className="text-gray-700 text-xl font-semibold">Syntax</h3>
<div className="flex flex-row">
{isCopied && (
<div
Expand Down Expand Up @@ -71,7 +71,7 @@ export default function CommandPage({
</code>
</div>

<h2 className="text-gray-700 text-2xl font-semibold pt-4 mb-4">
<h2 className="text-gray-700 text-xl font-semibold pt-4 mb-4">
Description
</h2>
<div className="bg-gray-200 p-4 rounded-lg mb-4">
Expand Down
32 changes: 30 additions & 2 deletions apps/playground-web/components/Search/SearchBox.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use client';

import { useMemo, useState } from 'react';
import { useMemo, useRef, useState } from 'react';
import { Search } from 'lucide-react';
import { DiceCmds, DiceCmdMeta } from '@/data/command';
import CommandPage from './CommandPage';

export default function SearchBox() {
const [search, setSearch] = useState('');
const scrollRef = useRef<HTMLDivElement>(null);
const filteredCommands = useMemo(
() =>
Object.values(DiceCmds).filter((cmd: DiceCmdMeta) =>
Expand All @@ -33,7 +34,10 @@ export default function SearchBox() {
/>
</div>
</div>
<div className="mt-4 max-w-full space-y-4 h-full overflow-y-auto">
<div
ref={scrollRef}
className="mt-4 max-w-full space-y-4 h-full pr-1 pb-[130px] mobile-scrollbar overflow-y-auto"
>
{filteredCommands.map((cmdMeta) => (
<CommandPage
key={cmdMeta.title}
Expand All @@ -44,6 +48,30 @@ export default function SearchBox() {
data-testid={`command-title-${cmdMeta.title}`}
/>
))}

<button
onClick={() =>
scrollRef.current?.scrollTo({ top: 0, behavior: 'smooth' })
}
className="fixed bottom-4 right-4 bg-gray-700 hover:bg-gray-800 text-white rounded-full p-3 shadow-lg transition-all duration-300"
aria-label="Scroll to top"
data-testid="scroll-to-top"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 10l7-7m0 0l7 7m-7-7v18"
/>
</svg>
</button>
{filteredCommands.length === 0 && <NotFoundPage />}
</div>
</div>
Expand Down
23 changes: 23 additions & 0 deletions apps/playground-web/context/command.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createContext, useContext, useState } from 'react';

const CommandContext = createContext<{
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
}>({
isOpen: true,
setIsOpen: () => {},
});

export const useCommandContext = () => useContext(CommandContext);

const CommandProvider = ({ children }: { children: React.ReactNode }) => {
const [isOpen, setIsOpen] = useState(false);

return (
<CommandContext.Provider value={{ isOpen, setIsOpen }}>
{children}
</CommandContext.Provider>
);
};

export default CommandProvider;
19 changes: 19 additions & 0 deletions apps/playground-web/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,22 @@ body {
.width-absolute {
width: 100%;
}

.mobile-scrollbar::-webkit-scrollbar {
width: 6px;
}

.mobile-scrollbar::-webkit-scrollbar-track {
background: #f1f1f1;
cursor: pointer;
}

.mobile-scrollbar::-webkit-scrollbar-thumb {
background: #88888888;
border-radius: 10px;
width: 5px;
}

.mobile-scrollbar::-webkit-scrollbar-thumb:hover {
background: #555;
}
Loading