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() { export default function Header() {
const { navVisible } = useOutletContext<ContextType>(); const { navVisible } = useOutletContext<ContextType>();
return ( 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="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="hide-scrollbar flex items-center gap-2 overflow-x-auto">
{!navVisible && <NewChat />} {!navVisible && <NewChat />}
<EndpointsMenu /> <EndpointsMenu />
<HeaderOptions /> <HeaderOptions />

View file

@ -28,15 +28,22 @@ export default function OptionsPopover({
popoverRef, popoverRef,
() => closePopover(), () => closePopover(),
['dialog-template-content', 'shadcn-button', 'advanced-settings'], ['dialog-template-content', 'shadcn-button', 'advanced-settings'],
(target) => { (_target) => {
const tagName = (target as Element)?.tagName; 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'; return tagName === 'path' || tagName === 'svg' || tagName === 'circle';
}, },
); );
const localize = useLocalize(); const localize = useLocalize();
const cardStyle = 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) { if (!visible) {
return null; return null;
@ -45,7 +52,7 @@ export default function OptionsPopover({
return ( return (
<Portal> <Portal>
<Content sideOffset={8} align="start" ref={popoverRef} asChild> <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 <div
className={cn( className={cn(
cardStyle, cardStyle,

View file

@ -1,6 +1,7 @@
import TextareaAutosize from 'react-textarea-autosize'; import TextareaAutosize from 'react-textarea-autosize';
import { supportsFiles } from '~/common'; import { supportsFiles } from '~/common';
import { useTextarea } from '~/hooks'; import { useTextarea } from '~/hooks';
import { cn, removeFocusOutlines } from '~/utils';
export default function Textarea({ value, onChange, setText, submitMessage, endpoint }) { export default function Textarea({ value, onChange, setText, submitMessage, endpoint }) {
const { const {
@ -14,8 +15,10 @@ export default function Textarea({ value, onChange, setText, submitMessage, endp
} = useTextarea({ setText, submitMessage }); } = useTextarea({ setText, submitMessage });
const className = supportsFiles[endpoint] 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-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-[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 ( return (
<TextareaAutosize <TextareaAutosize
@ -36,7 +39,7 @@ export default function Textarea({ value, onChange, setText, submitMessage, endp
placeholder={placeholder} 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="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="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 { useChatContext } from '~/Providers';
import { useMediaQuery } from '~/hooks';
export default function Header() { export default function Header() {
const { newConversation } = useChatContext(); const { newConversation } = useChatContext();
const isSmallScreen = useMediaQuery('(max-width: 768px)');
if (isSmallScreen) {
return null;
}
return ( return (
<button <button
type="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()} onClick={() => newConversation()}
> >
<div className="flex w-full items-center justify-center gap-2"> <div className="flex w-full items-center justify-center gap-2">

View file

@ -59,7 +59,20 @@ const PresetItems: FC<{
</Dialog> </Dialog>
</div> </div>
</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 &&
presets.length > 0 &&
presets.map((preset, i) => { presets.map((preset, i) => {
if (!preset) { if (!preset) {
return null; return null;
@ -70,8 +83,7 @@ const PresetItems: FC<{
<div key={`preset-${preset.presetId}`}> <div key={`preset-${preset.presetId}`}>
<MenuItem <MenuItem
key={`preset-item-${preset.presetId}`} key={`preset-item-${preset.presetId}`}
className="w-[380px] md:min-w-[240px]" textClassName="text-xs max-w-[200px] truncate md:max-w-full "
textClassName="text-xs max-w-[180px] md:max-w-[250px]"
title={getPresetTitle(preset)} title={getPresetTitle(preset)}
disableHover={true} disableHover={true}
onClick={() => onSelectPreset(preset)} onClick={() => onSelectPreset(preset)}
@ -83,7 +95,7 @@ const PresetItems: FC<{
> >
<div className="flex h-full items-center justify-end gap-1"> <div className="flex h-full items-center justify-end gap-1">
<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) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -93,7 +105,7 @@ const PresetItems: FC<{
<TrashIcon /> <TrashIcon />
</button> </button>
<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) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
onChangePreset(preset); onChangePreset(preset);

View file

@ -1,18 +1,18 @@
import type { FC } from 'react'; import type { FC } from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { BookCopy } from 'lucide-react';
import { import {
modularEndpoints, modularEndpoints,
useDeletePresetMutation, useDeletePresetMutation,
useCreatePresetMutation, useCreatePresetMutation,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import type { TPreset } 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 { useLocalize, useDefaultConvo, useNavigateToConvo } from '~/hooks';
import { EditPresetDialog, PresetItems } from './Presets'; import { EditPresetDialog, PresetItems } from './Presets';
import { cleanupPreset, cn } from '~/utils';
import { useChatContext } from '~/Providers'; import { useChatContext } from '~/Providers';
import TitleButton from './UI/TitleButton';
import { cleanupPreset } from '~/utils';
import store from '~/store'; import store from '~/store';
const PresetsMenu: FC = () => { const PresetsMenu: FC = () => {
@ -86,7 +86,20 @@ const PresetsMenu: FC = () => {
return ( return (
<Root> <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> <Portal>
<div <div
style={{ style={{
@ -100,10 +113,9 @@ const PresetsMenu: FC = () => {
> >
<Content <Content
side="bottom" side="bottom"
align="start" align="center"
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]" 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 <PresetItems
presets={presets} presets={presets}
onSelectPreset={onSelectPreset} onSelectPreset={onSelectPreset}
@ -112,17 +124,10 @@ const PresetsMenu: FC = () => {
clearAllPresets={clearAllPresets} clearAllPresets={clearAllPresets}
onFileSelected={onFileSelected} onFileSelected={onFileSelected}
/> />
) : (
<div className="dark:text-gray-300">{localize('com_endpoint_no_presets')}</div>
)}
</Content> </Content>
</div> </div>
</Portal> </Portal>
<EditPresetDialog <EditPresetDialog open={presetModalVisible} onOpenChange={setPresetModalVisible} />
open={presetModalVisible}
onOpenChange={setPresetModalVisible}
// preset={preset as TPreset}
/>
</Root> </Root>
); );
}; };

View file

@ -81,7 +81,7 @@ const MenuItem: FC<MenuItemProps> = ({
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" 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 <path
fillRule="evenodd" fillRule="evenodd"

View file

@ -6,9 +6,11 @@ import { TConversation } from 'librechat-data-provider';
export default function Conversations({ export default function Conversations({
conversations, conversations,
moveToTop, moveToTop,
toggleNav,
}: { }: {
conversations: TConversation[]; conversations: TConversation[];
moveToTop: () => void; moveToTop: () => void;
toggleNav: () => void;
}) { }) {
const location = useLocation(); const location = useLocation();
const { pathname } = location; const { pathname } = location;
@ -24,6 +26,7 @@ export default function Conversations({
key={convo.conversationId} key={convo.conversationId}
conversation={convo} conversation={convo}
retainView={moveToTop} retainView={moveToTop}
toggleNav={toggleNav}
i={i} i={i}
/> />
); );

View file

@ -13,7 +13,7 @@ import store from '~/store';
type KeyEvent = KeyboardEvent<HTMLInputElement>; type KeyEvent = KeyboardEvent<HTMLInputElement>;
export default function Conversation({ conversation, retainView, i }) { export default function Conversation({ conversation, retainView, toggleNav, i }) {
const { conversationId: currentConvoId } = useParams(); const { conversationId: currentConvoId } = useParams();
const activeConvos = useRecoilValue(store.allConversationsSelector); const activeConvos = useRecoilValue(store.allConversationsSelector);
const updateConvoMutation = useUpdateConversationMutation(currentConvoId ?? ''); const updateConvoMutation = useUpdateConversationMutation(currentConvoId ?? '');
@ -31,6 +31,8 @@ export default function Conversation({ conversation, retainView, i }) {
return; return;
} }
toggleNav();
// set document title // set document title
document.title = title; document.title = title;

View file

@ -23,7 +23,9 @@ export default function Settings({
if (OptionComponent) { if (OptionComponent) {
return ( return (
<div className={cn('h-[480px] overflow-y-auto md:mb-2 md:h-[350px]', className)}> <div
className={cn('hide-scrollbar h-[500px] overflow-y-auto md:mb-2 md:h-[350px]', className)}
>
<OptionComponent <OptionComponent
conversation={conversation} conversation={conversation}
setOption={setOption} setOption={setOption}
@ -41,7 +43,7 @@ export default function Settings({
} }
return ( return (
<div className={cn('h-[480px] overflow-y-auto md:mb-2 md:h-[350px]', className)}> <div className={cn('hide-scrollbar h-[500px] overflow-y-auto md:mb-2 md:h-[350px]', className)}>
<MultiViewComponent conversation={conversation} models={models} isPreset={isPreset} /> <MultiViewComponent conversation={conversation} models={models} isPreset={isPreset} />
</div> </div>
); );

View file

@ -102,13 +102,14 @@ export default function PluginsByIndex({
)} )}
/> />
</Button> </Button>
{visible && (
<>
<Menu <Menu
value={conversation.model ?? ''} value={conversation.model ?? ''}
setValue={setOption('model')} setValue={setOption('model')}
availableValues={models} availableValues={models}
showAbove={showAbove} showAbove={showAbove}
showLabel={false} showLabel={false}
className={cn(cardStyle, 'min-w-60 z-40 flex w-64 sm:w-48 ', visible ? '' : 'hidden')}
/> />
<PluginsMenu <PluginsMenu
value={conversation.tools || []} value={conversation.tools || []}
@ -118,8 +119,9 @@ export default function PluginsByIndex({
optionValueKey="pluginKey" optionValueKey="pluginKey"
showAbove={false} showAbove={false}
showLabel={false} showLabel={false}
className={cn(cardStyle, 'min-w-60 z-50 w-64 sm:w-48 ', visible ? '' : 'hidden')}
/> />
</> </>
)}
</>
); );
} }

View file

@ -144,6 +144,12 @@ export default function Nav({ navVisible, setNavVisible }) {
setNavVisible((prev: boolean) => !prev); setNavVisible((prev: boolean) => !prev);
}; };
const itemToggleNav = () => {
if (isSmallScreen) {
toggleNavVisible();
}
};
const containerClasses = const containerClasses =
getConversationsQuery.isLoading && pageNumber === 1 getConversationsQuery.isLoading && pageNumber === 1
? 'flex flex-col gap-2 text-gray-100 text-sm h-full justify-center items-center' ? 'flex flex-col gap-2 text-gray-100 text-sm h-full justify-center items-center'
@ -167,12 +173,12 @@ export default function Nav({ navVisible, setNavVisible }) {
<div <div
className={cn( className={cn(
'scrollbar-trigger relative flex h-full w-full flex-1 items-start border-white/20 transition-opacity', 'scrollbar-trigger relative flex h-full w-full flex-1 items-start border-white/20 transition-opacity',
isToggleHovering ? 'opacity-50' : 'opacity-100', isToggleHovering && !isSmallScreen ? 'opacity-50' : 'opacity-100',
)} )}
> >
<nav className="relative flex h-full flex-1 flex-col space-y-1 p-2"> <nav className="relative flex h-full flex-1 flex-col space-y-1 p-2">
<div className="mb-1 flex h-11 flex-row"> <div className="mb-1 flex h-11 flex-row">
<NewChat /> <NewChat toggleNav={itemToggleNav} />
</div> </div>
{isSearchEnabled && <SearchBar clearSearch={clearSearch} />} {isSearchEnabled && <SearchBar clearSearch={clearSearch} />}
<div <div
@ -187,7 +193,11 @@ export default function Nav({ navVisible, setNavVisible }) {
{(getConversationsQuery.isLoading && pageNumber === 1) || isFetching ? ( {(getConversationsQuery.isLoading && pageNumber === 1) || isFetching ? (
<Spinner /> <Spinner />
) : ( ) : (
<Conversations conversations={conversations} moveToTop={moveToTop} /> <Conversations
conversations={conversations}
moveToTop={moveToTop}
toggleNav={itemToggleNav}
/>
)} )}
<Pages <Pages
pageNumber={pageNumber} pageNumber={pageNumber}

View file

@ -1,6 +1,6 @@
import { useLocalize, useConversation, useNewConvo, useOriginNavigate } from '~/hooks'; import { useLocalize, useConversation, useNewConvo, useOriginNavigate } from '~/hooks';
export default function NewChat() { export default function NewChat({ toggleNav }: { toggleNav: () => void }) {
const { newConversation } = useConversation(); const { newConversation } = useConversation();
const { newConversation: newConvo } = useNewConvo(); const { newConversation: newConvo } = useNewConvo();
const navigate = useOriginNavigate(); const navigate = useOriginNavigate();
@ -10,6 +10,7 @@ export default function NewChat() {
newConvo(); newConvo();
newConversation(); newConversation();
navigate('new'); navigate('new');
toggleNav();
}; };
return ( return (

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Wrench, ArrowRight } from 'lucide-react'; import { Wrench } from 'lucide-react';
import { Root, Trigger, Content, Portal } from '@radix-ui/react-popover'; import { Root, Trigger, Content, Portal } from '@radix-ui/react-popover';
import type { TPlugin } from 'librechat-data-provider'; import type { TPlugin } from 'librechat-data-provider';
import MenuItem from '~/components/Chat/Menus/UI/MenuItem'; import MenuItem from '~/components/Chat/Menus/UI/MenuItem';
@ -19,18 +19,15 @@ type SelectDropDownProps = {
optionValueKey?: string; optionValueKey?: string;
}; };
function SelectDropDownPop({ function MultiSelectPop({
title: _title = 'Plugins', title: _title = 'Plugins',
value, value,
// TODO: do we need disabled here?
disabled,
setSelected, setSelected,
availableValues, availableValues,
showAbove = false, showAbove = false,
showLabel = true, showLabel = true,
containerClassName, containerClassName,
isSelected, isSelected,
className,
optionValueKey = 'value', optionValueKey = 'value',
}: SelectDropDownProps) { }: SelectDropDownProps) {
// const localize = useLocalize(); // const localize = useLocalize();
@ -46,8 +43,9 @@ function SelectDropDownPop({
<button <button
data-testid="select-dropdown-button" data-testid="select-dropdown-button"
className={cn( className={cn(
'relative flex cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-white/20 dark:bg-gray-800 dark:bg-gray-800 sm:text-sm', 'relative flex flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-white/20 dark:bg-gray-800 dark:bg-gray-800 sm:text-sm',
className ?? '', 'pointer-cursor font-normal',
'hover:bg-gray-50 radix-state-open:bg-gray-50 dark:hover:bg-black/10 dark:radix-state-open:bg-black/20',
)} )}
> >
{' '} {' '}
@ -85,7 +83,7 @@ function SelectDropDownPop({
</span> </span>
</span> </span>
</span> </span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <span className="absolute inset-y-0 right-0 flex items-center pr-2">
<svg <svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
@ -107,7 +105,7 @@ function SelectDropDownPop({
<Portal> <Portal>
<Content <Content
side="bottom" side="bottom"
align="start" align="center"
className="mt-2 max-h-60 min-w-full overflow-hidden overflow-y-auto rounded-lg border border-gray-100 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900 dark:text-white" className="mt-2 max-h-60 min-w-full overflow-hidden overflow-y-auto rounded-lg border border-gray-100 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900 dark:text-white"
> >
{availableValues.map((option) => { {availableValues.map((option) => {
@ -144,4 +142,4 @@ function SelectDropDownPop({
); );
} }
export default SelectDropDownPop; export default MultiSelectPop;

View file

@ -17,12 +17,6 @@ type SelectDropDownProps = {
showLabel?: boolean; showLabel?: boolean;
iconSide?: 'left' | 'right'; iconSide?: 'left' | 'right';
renderOption?: () => React.ReactNode; renderOption?: () => React.ReactNode;
containerClassName?: string;
currentValueClass?: string;
optionsListClass?: string;
optionsClass?: string;
subContainerClassName?: string;
className?: string;
}; };
function SelectDropDownPop({ function SelectDropDownPop({
@ -33,10 +27,6 @@ function SelectDropDownPop({
showAbove = false, showAbove = false,
showLabel = true, showLabel = true,
emptyTitle = false, emptyTitle = false,
containerClassName,
currentValueClass,
subContainerClassName,
className,
}: SelectDropDownProps) { }: SelectDropDownProps) {
const localize = useLocalize(); const localize = useLocalize();
const transitionProps = { className: 'top-full mt-3' }; const transitionProps = { className: 'top-full mt-3' };
@ -54,14 +44,13 @@ function SelectDropDownPop({
return ( return (
<Root> <Root>
<div className={cn('flex items-center justify-center gap-2 ', containerClassName ?? '')}> <div className={'flex items-center justify-center gap-2 '}>
<div className={cn('relative w-full', subContainerClassName ?? '')}> <div className={'relative w-full'}>
<Trigger asChild> <Trigger asChild>
<button <button
data-testid="select-dropdown-button" data-testid="select-dropdown-button"
className={cn( className={cn(
'relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-white/20 dark:bg-gray-800 sm:text-sm', 'pointer-cursor relative flex flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
className ?? '',
'hover:bg-gray-50 radix-state-open:bg-gray-50 dark:hover:bg-black/10 dark:radix-state-open:bg-black/20', 'hover:bg-gray-50 radix-state-open:bg-gray-50 dark:hover:bg-black/10 dark:radix-state-open:bg-black/20',
)} )}
> >
@ -69,12 +58,12 @@ function SelectDropDownPop({
{showLabel && ( {showLabel && (
<label className="block text-xs text-gray-700 dark:text-gray-500 ">{title}</label> <label className="block text-xs text-gray-700 dark:text-gray-500 ">{title}</label>
)} )}
<span className="inline-flex w-full truncate"> <span className="inline-flex w-full ">
<span <span
className={cn( className={cn(
'flex h-6 items-center gap-1 truncate text-sm text-gray-900 dark:text-white', 'flex h-6 items-center gap-1 text-sm text-gray-900 dark:text-white',
!showLabel ? 'text-xs' : '', !showLabel ? 'text-xs' : '',
currentValueClass ?? '', 'min-w-[75px] font-normal',
)} )}
> >
{/* {!showLabel && !emptyTitle && ( {/* {!showLabel && !emptyTitle && (
@ -83,7 +72,7 @@ function SelectDropDownPop({
{typeof value !== 'string' && value ? value?.label ?? '' : value ?? ''} {typeof value !== 'string' && value ? value?.label ?? '' : value ?? ''}
</span> </span>
</span> </span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <span className="absolute inset-y-0 right-0 flex items-center pr-2">
<svg <svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"

View file

@ -192,7 +192,7 @@ export default {
com_endpoint_presets_clear_warning: com_endpoint_presets_clear_warning:
'Are you sure you want to clear all presets? This is irreversible.', 'Are you sure you want to clear all presets? This is irreversible.',
com_endpoint_not_implemented: 'Not implemented', com_endpoint_not_implemented: 'Not implemented',
com_endpoint_no_presets: 'No preset yet', com_endpoint_no_presets: 'No presets yet, use the settings button to create one',
com_endpoint_not_available: 'No endpoint available', com_endpoint_not_available: 'No endpoint available',
com_endpoint_view_options: 'View Options', com_endpoint_view_options: 'View Options',
com_endpoint_save_convo_as_preset: 'Save Conversation as Preset', com_endpoint_save_convo_as_preset: 'Save Conversation as Preset',

View file

@ -111,3 +111,13 @@
visibility: hidden; visibility: hidden;
} }
} }
.hide-scrollbar {
/* Hide scrollbar for Chrome, Safari, and Opera */
scrollbar-width: none; /* For Firefox */
-ms-overflow-style: none; /* For Internet Explorer and Edge */
}
.hide-scrollbar::-webkit-scrollbar {
display: none; /* For WebKit browsers */
}

View file

@ -1709,3 +1709,33 @@ html {
height:234px; height:234px;
width:234px width:234px
} }
.border-token-border-heavy {
border-color: #c5c5d2;
border-color: var(--border-heavy)
}
.border-token-border-light {
border-color: #ececf1;
border-color: var(--border-light)
}
.border-token-border-medium {
border-color: #d9d9e3;
border-color: var(--border-medium)
}
.border-token-surface-primary {
border-color: #fff;
border-color: var(--surface-primary)
}
.border-token-surface-secondary {
border-color: #f7f7f8;
border-color: var(--surface-secondary)
}
.border-token-surface-tertiary {
border-color: #ececf1;
border-color: var(--surface-tertiary)
}