feat(layout): allow to minimize chat (#35)

This commit is contained in:
Dominic Elm
2024-08-14 11:08:52 +02:00
committed by GitHub
parent 8fd9d4477e
commit d5a29c2427
18 changed files with 262 additions and 114 deletions

View File

@@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
import { ClientOnly } from 'remix-utils/client-only';
import { chatStore } from '~/lib/stores/chat';
import { classNames } from '~/utils/classNames';
import { OpenStackBlitz } from './OpenStackBlitz.client';
import { HeaderActionButtons } from './HeaderActionButtons.client';
export function Header() {
const chat = useStore(chatStore);
@@ -17,14 +17,22 @@ export function Header() {
},
)}
>
<div className="flex items-center gap-2 z-logo text-bolt-elements-textPrimary">
<div className="flex items-center gap-2 z-logo text-bolt-elements-textPrimary cursor-pointer">
<div className="i-ph:sidebar-simple-duotone text-xl" />
<a href="/" className="text-2xl font-semibold text-accent flex items-center">
<span className="i-bolt:logo-text?mask w-[46px] inline-block" />
</a>
</div>
<div className="ml-auto flex gap-2">
<ClientOnly>{() => <OpenStackBlitz />}</ClientOnly>
</div>
<div className="flex-1" />
{chat.started && (
<ClientOnly>
{() => (
<div className="mr-1">
<HeaderActionButtons />
</div>
)}
</ClientOnly>
)}
</header>
);
}

View File

@@ -0,0 +1,72 @@
import { useStore } from '@nanostores/react';
import { chatStore } from '~/lib/stores/chat';
import { workbenchStore } from '~/lib/stores/workbench';
import { classNames } from '~/utils/classNames';
import { OpenStackBlitz } from './OpenStackBlitz.client';
interface HeaderActionButtonsProps {}
export function HeaderActionButtons({}: HeaderActionButtonsProps) {
const showWorkbench = useStore(workbenchStore.showWorkbench);
const { showChat } = useStore(chatStore);
const canHideChat = showWorkbench || !showChat;
return (
<div className="flex">
<div className="flex border border-bolt-elements-borderColor rounded-md overflow-hidden">
<Button
active={showChat}
disabled={!canHideChat}
onClick={() => {
if (canHideChat) {
chatStore.setKey('showChat', !showChat);
}
}}
>
<div className="i-bolt:chat text-sm" />
</Button>
<div className="w-[1px] bg-bolt-elements-borderColor" />
<Button
active={showWorkbench}
onClick={() => {
if (showWorkbench && !showChat) {
chatStore.setKey('showChat', true);
}
workbenchStore.showWorkbench.set(!showWorkbench);
}}
>
<div className="i-ph:code-bold" />
</Button>
</div>
<div className="flex ml-2">
<OpenStackBlitz />
</div>
</div>
);
}
interface ButtonProps {
active?: boolean;
disabled?: boolean;
children?: any;
onClick?: VoidFunction;
}
function Button({ active = false, disabled = false, children, onClick }: ButtonProps) {
return (
<button
className={classNames('flex items-center p-1.5', {
'bg-bolt-elements-item-backgroundDefault hover:bg-bolt-elements-item-backgroundActive text-bolt-elements-textTertiary hover:text-bolt-elements-textPrimary':
!active,
'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent': active && !disabled,
'bg-bolt-elements-item-backgroundDefault text-alpha-gray-20 dark:text-alpha-white-20 cursor-not-allowed':
disabled,
})}
onClick={onClick}
>
{children}
</button>
);
}

View File

@@ -1,10 +1,11 @@
import path from 'path';
import { useStore } from '@nanostores/react';
import sdk from '@stackblitz/sdk';
import path from 'path';
import { memo, useCallback, useEffect, useState } from 'react';
import type { FileMap } from '~/lib/stores/files';
import { workbenchStore, type ArtifactState } from '~/lib/stores/workbench';
import { classNames } from '~/utils/classNames';
import { WORK_DIR } from '~/utils/constants';
import { memo, useCallback, useEffect, useState } from 'react';
// extract relative path and content from file, wrapped in array for flatMap use
const extractContent = ([file, value]: [string, FileMap[string]]) => {
@@ -47,6 +48,8 @@ const useFirstArtifact = (): [boolean, ArtifactState | undefined] => {
export const OpenStackBlitz = memo(() => {
const [artifactLoaded, artifact] = useFirstArtifact();
const disabled = !artifactLoaded;
const handleClick = useCallback(() => {
if (!artifact) {
return;
@@ -66,13 +69,34 @@ export const OpenStackBlitz = memo(() => {
});
}, [artifact]);
if (!artifactLoaded) {
return null;
}
return (
<a onClick={handleClick} className="cursor-pointer">
<img alt="Open in StackBlitz" src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" />
</a>
<button
className={classNames(
'relative flex items-stretch p-[1px] overflow-hidden text-xs text-bolt-elements-cta-text rounded-lg bg-bolt-elements-borderColor dark:bg-gray-800',
{
'cursor-not-allowed opacity-50': disabled,
'group hover:bg-gradient-to-t from-accent-900 to-accent-500 hover:text-white': !disabled,
},
)}
onClick={handleClick}
disabled={disabled}
>
<div
className={classNames(
'flex items-center gap-1.5 px-3 bg-bolt-elements-cta-background dark:bg-alpha-gray-80 group-hover:bg-transparent rounded-[calc(0.5rem-1px)] group-hover:bg-opacity-0',
{
'opacity-50': disabled,
},
)}
>
<svg width="11" height="16">
<path
fill="currentColor"
d="M4.67 9.85a.3.3 0 0 0-.27-.4H.67a.3.3 0 0 1-.21-.49l7.36-7.9c.22-.24.6 0 .5.3l-1.75 4.8a.3.3 0 0 0 .28.39h3.72c.26 0 .4.3.22.49l-7.37 7.9c-.21.24-.6 0-.49-.3l1.74-4.8Z"
/>
</svg>
<span>Open in StackBlitz</span>
</div>
</button>
);
});