style: Minor Beta UI fixes (#1197)

* style(Header): hide scrollbar but still allow side scroll/swipe/drag/touch

* feat: make menu buttons flexiblewith min-width, delete passed in classes, add pointer-cursor

* refactor: use conditional for visibility of plugins settings

* fix: make advanced settings popover appear over nav

* refactor(textarea): minor padding restyling, add max height

* style: make menuItem checkmark invisible instead of hidden so it takes up width space

* style: make presetsMenu trigger an icon button, remove max-width of presets except in mobile view

* style: improve advanced settings mobile styling

* feat: newchat and convo items toggle nav on small screens

* style: improve no presets view

* style: make sure toggle hover effect does not apply on smaller screens
This commit is contained in:
Danny Avila 2023-11-17 08:00:42 -05:00 committed by GitHub
parent c64970525b
commit 1ba8d4ffa9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 171 additions and 92 deletions

View file

@ -6,8 +6,8 @@ import HeaderOptions from './Input/HeaderOptions';
export default function Header() {
const { navVisible } = useOutletContext<ContextType>();
return (
<div className="sticky top-0 z-10 flex h-14 w-full items-center justify-between bg-white/95 p-2 font-semibold dark:bg-gray-800/90 dark:text-white">
<div className="flex items-center gap-2 overflow-x-auto">
<div className="sticky top-0 z-10 flex h-14 w-full items-center justify-between bg-white/95 p-2 font-semibold dark:bg-gray-800/90 dark:text-white ">
<div className="hide-scrollbar flex items-center gap-2 overflow-x-auto">
{!navVisible && <NewChat />}
<EndpointsMenu />
<HeaderOptions />

View file

@ -28,15 +28,22 @@ export default function OptionsPopover({
popoverRef,
() => closePopover(),
['dialog-template-content', 'shadcn-button', 'advanced-settings'],
(target) => {
const tagName = (target as Element)?.tagName;
(_target) => {
const target = _target as Element;
if (
target?.id === 'presets-button' ||
(target?.parentNode instanceof Element && target.parentNode.id === 'presets-button')
) {
return false;
}
const tagName = target?.tagName;
return tagName === 'path' || tagName === 'svg' || tagName === 'circle';
},
);
const localize = useLocalize();
const cardStyle =
'shadow-xl rounded-md min-w-[75px] font-normal bg-white border-black/10 border dark:bg-gray-700 text-black dark:text-white';
'shadow-xl rounded-md min-w-[75px] font-normal bg-white border-black/10 border dark:bg-gray-700 text-black dark:text-white ';
if (!visible) {
return null;
@ -45,7 +52,7 @@ export default function OptionsPopover({
return (
<Portal>
<Content sideOffset={8} align="start" ref={popoverRef} asChild>
<div className="z-0 flex w-full flex-col items-center md:px-4">
<div className="z-[70] flex w-screen flex-col items-center md:w-full md:px-4">
<div
className={cn(
cardStyle,

View file

@ -1,6 +1,7 @@
import TextareaAutosize from 'react-textarea-autosize';
import { supportsFiles } from '~/common';
import { useTextarea } from '~/hooks';
import { cn, removeFocusOutlines } from '~/utils';
export default function Textarea({ value, onChange, setText, submitMessage, endpoint }) {
const {
@ -14,8 +15,10 @@ export default function Textarea({ value, onChange, setText, submitMessage, endp
} = useTextarea({ setText, submitMessage });
const className = supportsFiles[endpoint]
? 'm-0 w-full resize-none border-0 bg-transparent py-3.5 pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent placeholder-black/50 dark:placeholder-white/50 pl-10 md:py-3.5 md:pr-12 md:pl-[55px]'
: 'm-0 w-full resize-none border-0 bg-transparent py-[10px] pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:py-4 md:pr-12 gizmo:md:py-3.5 gizmo:placeholder-black/50 gizmo:dark:placeholder-white/50 pl-3 md:pl-4';
? // ? 'm-0 w-full resize-none border-0 bg-transparent py-3.5 pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent placeholder-black/50 dark:placeholder-white/50 pl-10 md:py-3.5 md:pr-12 md:pl-[55px]'
// : 'm-0 w-full resize-none border-0 bg-transparent py-[10px] pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:py-4 md:pr-12 gizmo:md:py-3.5 gizmo:placeholder-black/50 gizmo:dark:placeholder-white/50 pl-3 md:pl-4';
'm-0 w-full resize-none border-0 bg-transparent py-3.5 pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent placeholder-black/50 dark:placeholder-white/50 pl-10 md:py-3.5 md:pr-12 md:pl-[55px]'
: 'm-0 w-full resize-none border-0 bg-transparent py-[10px] pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:py-3.5 md:pr-12 placeholder-black/50 dark:placeholder-white/50 pl-3 md:pl-4';
return (
<TextareaAutosize
@ -36,7 +39,7 @@ export default function Textarea({ value, onChange, setText, submitMessage, endp
placeholder={placeholder}
// className="m-0 w-full resize-none border-0 bg-transparent py-[10px] pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:py-4 md:pr-12 gizmo:md:py-3.5 gizmo:placeholder-black/50 gizmo:dark:placeholder-white/50 pl-12 gizmo:pl-10 md:pl-[46px] gizmo:md:pl-[55px]"
// className="gizmo:md:py-3.5 gizmo:placeholder-black/50 gizmo:dark:placeholder-white/50 gizmo:pl-10 gizmo:md:pl-[55px] m-0 h-auto max-h-52 w-full resize-none overflow-y-hidden border-0 bg-transparent py-[10px] pl-12 pr-10 focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:py-4 md:pl-[46px] md:pr-12"
className={className}
className={cn(className, removeFocusOutlines, 'max-h-52')}
/>
);
}

View file

@ -1,11 +1,16 @@
import { useChatContext } from '~/Providers';
import { useMediaQuery } from '~/hooks';
export default function Header() {
const { newConversation } = useChatContext();
const isSmallScreen = useMediaQuery('(max-width: 768px)');
if (isSmallScreen) {
return null;
}
return (
<button
type="button"
className=" btn btn-neutral btn-small border-token-border-medium relative ml-2 flex h-9 w-9 items-center justify-center whitespace-nowrap rounded-lg rounded-lg border focus:ring-0 focus:ring-offset-0"
className="btn btn-neutral btn-small border-token-border-medium relative ml-2 flex hidden h-9 w-9 items-center justify-center whitespace-nowrap rounded-lg rounded-lg border focus:ring-0 focus:ring-offset-0 md:flex"
onClick={() => newConversation()}
>
<div className="flex w-full items-center justify-center gap-2">

View file

@ -59,7 +59,20 @@ const PresetItems: FC<{
</Dialog>
</div>
</div>
{presets && presets.length === 0 && (
<div
role="menuitem"
className="pointer-none group m-1.5 flex h-8 min-w-[170px] gap-2 rounded px-5 py-2.5 !pr-3 text-sm !opacity-100 hover:bg-black/5 focus:ring-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-white/5 md:min-w-[240px]"
tabIndex={-1}
>
<div className="flex h-full grow items-center justify-end gap-2">
{/* TODO: Create Preset from here */}
{localize('com_endpoint_no_presets')}
</div>
</div>
)}
{presets &&
presets.length > 0 &&
presets.map((preset, i) => {
if (!preset) {
return null;
@ -70,8 +83,7 @@ const PresetItems: FC<{
<div key={`preset-${preset.presetId}`}>
<MenuItem
key={`preset-item-${preset.presetId}`}
className="w-[380px] md:min-w-[240px]"
textClassName="text-xs max-w-[180px] md:max-w-[250px]"
textClassName="text-xs max-w-[200px] truncate md:max-w-full "
title={getPresetTitle(preset)}
disableHover={true}
onClick={() => onSelectPreset(preset)}
@ -83,7 +95,7 @@ const PresetItems: FC<{
>
<div className="flex h-full items-center justify-end gap-1">
<button
className="m-0 h-full rounded-md px-4 text-gray-400 hover:text-gray-700 dark:bg-gray-700 dark:text-gray-400 dark:hover:text-gray-200 sm:invisible sm:p-2 sm:group-hover:visible"
className="m-0 h-full rounded-md p-2 px-4 text-gray-400 hover:text-gray-700 dark:bg-gray-700 dark:text-gray-400 dark:hover:text-gray-200 sm:invisible sm:group-hover:visible"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
@ -93,7 +105,7 @@ const PresetItems: FC<{
<TrashIcon />
</button>
<button
className="m-0 h-full rounded-md px-4 text-gray-400 hover:text-gray-700 dark:bg-gray-700 dark:text-gray-400 dark:hover:text-gray-200 sm:invisible sm:p-2 sm:group-hover:visible"
className="m-0 h-full rounded-md p-2 px-4 text-gray-400 hover:text-gray-700 dark:bg-gray-700 dark:text-gray-400 dark:hover:text-gray-200 sm:invisible sm:group-hover:visible"
onClick={(e) => {
e.preventDefault();
onChangePreset(preset);

View file

@ -1,18 +1,18 @@
import type { FC } from 'react';
import { useState } from 'react';
import { useRecoilState } from 'recoil';
import { BookCopy } from 'lucide-react';
import {
modularEndpoints,
useDeletePresetMutation,
useCreatePresetMutation,
} from 'librechat-data-provider';
import type { TPreset } from 'librechat-data-provider';
import { Content, Portal, Root } from '@radix-ui/react-popover';
import { Content, Portal, Root, Trigger } from '@radix-ui/react-popover';
import { useLocalize, useDefaultConvo, useNavigateToConvo } from '~/hooks';
import { EditPresetDialog, PresetItems } from './Presets';
import { cleanupPreset, cn } from '~/utils';
import { useChatContext } from '~/Providers';
import TitleButton from './UI/TitleButton';
import { cleanupPreset } from '~/utils';
import store from '~/store';
const PresetsMenu: FC = () => {
@ -86,7 +86,20 @@ const PresetsMenu: FC = () => {
return (
<Root>
<TitleButton primaryText={'Presets'} />
<Trigger asChild>
<button
className={cn(
'pointer-cursor relative flex flex-col rounded-md border border-black/10 bg-white text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
'hover:bg-gray-50 radix-state-open:bg-gray-50 dark:hover:bg-black/10 dark:radix-state-open:bg-black/20',
'min-w-4 z-50 flex h-[40px] flex-none items-center justify-center px-3 focus:ring-0 focus:ring-offset-0',
)}
id="presets-button"
data-testid="presets-button"
title={localize('com_ui_presets')}
>
<BookCopy className="icon-sm" id="presets-button" />
</button>
</Trigger>
<Portal>
<div
style={{
@ -100,29 +113,21 @@ const PresetsMenu: FC = () => {
>
<Content
side="bottom"
align="start"
className="mt-2 max-h-[495px] max-w-[370px] overflow-x-hidden rounded-lg border border-gray-100 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900 dark:text-white md:min-w-[400px]"
align="center"
className="mt-2 max-h-[495px] overflow-x-hidden rounded-lg border border-gray-100 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900 dark:text-white md:min-w-[400px]"
>
{presets.length ? (
<PresetItems
presets={presets}
onSelectPreset={onSelectPreset}
onChangePreset={onChangePreset}
onDeletePreset={onDeletePreset}
clearAllPresets={clearAllPresets}
onFileSelected={onFileSelected}
/>
) : (
<div className="dark:text-gray-300">{localize('com_endpoint_no_presets')}</div>
)}
<PresetItems
presets={presets}
onSelectPreset={onSelectPreset}
onChangePreset={onChangePreset}
onDeletePreset={onDeletePreset}
clearAllPresets={clearAllPresets}
onFileSelected={onFileSelected}
/>
</Content>
</div>
</Portal>
<EditPresetDialog
open={presetModalVisible}
onOpenChange={setPresetModalVisible}
// preset={preset as TPreset}
/>
<EditPresetDialog open={presetModalVisible} onOpenChange={setPresetModalVisible} />
</Root>
);
};

View file

@ -81,7 +81,7 @@ const MenuItem: FC<MenuItemProps> = ({
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="icon-md block hidden gap-x-1 group-hover:flex "
className="icon-md invisible block gap-x-1 group-hover:visible group-hover:flex"
>
<path
fillRule="evenodd"