mirror of https://github.com/buster-so/buster.git
input search
This commit is contained in:
parent
4ae7b41191
commit
f8bf374772
|
@ -39,7 +39,7 @@ export const buttonVariants = cva(
|
|||
'inline-flex items-center overflow-hidden text-base justify-center gap-[5px] shadow rounded transition-all duration-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed data-[loading=true]:cursor-progress',
|
||||
{
|
||||
variants: {
|
||||
buttonType: buttonTypeClasses,
|
||||
variant: buttonTypeClasses,
|
||||
size: sizeVariants,
|
||||
iconButton: {
|
||||
true: '',
|
||||
|
@ -69,7 +69,7 @@ export const buttonVariants = cva(
|
|||
}
|
||||
],
|
||||
defaultVariants: {
|
||||
buttonType: 'default',
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
iconButton: false,
|
||||
block: false
|
||||
|
@ -79,7 +79,7 @@ export const buttonVariants = cva(
|
|||
|
||||
export const buttonIconVariants = cva('', {
|
||||
variants: {
|
||||
buttonType: {
|
||||
variant: {
|
||||
default: 'text-icon-color!',
|
||||
black: 'text-white!',
|
||||
primary: 'text-white!',
|
||||
|
@ -120,7 +120,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||
(
|
||||
{
|
||||
className = '',
|
||||
buttonType = 'default',
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
asChild = false,
|
||||
prefix,
|
||||
|
@ -145,7 +145,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||
return (
|
||||
<Comp
|
||||
className={cn(
|
||||
buttonVariants({ buttonType, size, iconButton, rounding, block, className }),
|
||||
buttonVariants({ variant, size, iconButton, rounding, block, className }),
|
||||
onClick && 'cursor-pointer'
|
||||
)}
|
||||
onClick={onClick}
|
||||
|
@ -155,17 +155,17 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||
data-selected={selected}
|
||||
{...props}>
|
||||
{loading ? (
|
||||
<LoadingIcon buttonType={buttonType} size={size} />
|
||||
<LoadingIcon variant={variant} size={size} />
|
||||
) : (
|
||||
prefix && (
|
||||
<span className={cn(buttonIconVariants({ buttonType, size }), prefixClassName)}>
|
||||
<span className={cn(buttonIconVariants({ variant, size }), prefixClassName)}>
|
||||
{prefix}
|
||||
</span>
|
||||
)
|
||||
)}
|
||||
{hasChildren && <span className="">{children}</span>}
|
||||
{suffix && (
|
||||
<span className={cn(buttonIconVariants({ buttonType, size }), suffixClassName)}>
|
||||
<span className={cn(buttonIconVariants({ variant, size }), suffixClassName)}>
|
||||
{suffix}
|
||||
</span>
|
||||
)}
|
||||
|
@ -177,22 +177,22 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||
Button.displayName = 'Button';
|
||||
|
||||
export const LoadingIcon: React.FC<{
|
||||
buttonType: ButtonProps['buttonType'];
|
||||
variant: ButtonProps['variant'];
|
||||
size: ButtonProps['size'];
|
||||
}> = ({ buttonType = 'default', size = 'default' }) => {
|
||||
}> = ({ variant = 'default', size = 'default' }) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-center text-black dark:text-white',
|
||||
buttonType === 'black' && 'dark',
|
||||
variant === 'black' && 'dark',
|
||||
loadingSizeVariants[size || 'default']
|
||||
)}>
|
||||
<CircleSpinnerLoader
|
||||
size={size === 'tall' ? 12.5 : 9.5}
|
||||
fill={
|
||||
buttonType === 'black'
|
||||
variant === 'black'
|
||||
? 'var(--color-white)'
|
||||
: buttonType === 'primary'
|
||||
: variant === 'primary'
|
||||
? 'var(--color-primary)'
|
||||
: 'var(--color-primary)'
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export const inputVariants = cva(
|
|||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'shadow disabled:bg-item-select bg-background border placeholder:text-gray-light hover:border-primary focus:border-primary focus-visible:border-primary outline-none disabled:border-border',
|
||||
'shadow disabled:bg-item-select bg-background border placeholder:text-gray-light hover:border-foreground focus:border-foreground focus-visible:border-foreground outline-none disabled:border-border',
|
||||
ghost: 'border-none bg-transparent shadow-none disabled:bg-transparent'
|
||||
},
|
||||
size: {
|
||||
|
|
|
@ -22,6 +22,7 @@ export interface InputTextAreaProps
|
|||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||
VariantProps<typeof inputTextAreaVariants> {
|
||||
autoResize?: AutoResizeOptions;
|
||||
onPressMetaEnter?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
||||
}
|
||||
|
||||
export const InputTextArea = React.forwardRef<HTMLTextAreaElement, InputTextAreaProps>(
|
||||
|
@ -92,6 +93,14 @@ export const InputTextArea = React.forwardRef<HTMLTextAreaElement, InputTextArea
|
|||
requestAnimationFrame(adjustHeight);
|
||||
});
|
||||
|
||||
const onPressMetaEnter = useMemoizedFn((e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.metaKey && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onPressMetaEnter?.(e);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const textarea = textareaRef.current;
|
||||
if (!textarea || !autoResize) return;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { InputTextAreaButton } from './InputTextAreaButton';
|
||||
|
||||
const meta: Meta<typeof InputTextAreaButton> = {
|
||||
title: 'Base/InputTextAreaButton',
|
||||
component: InputTextAreaButton,
|
||||
tags: ['autodocs'],
|
||||
args: {},
|
||||
argTypes: {
|
||||
disabled: {
|
||||
control: 'boolean'
|
||||
},
|
||||
placeholder: {
|
||||
control: 'text'
|
||||
},
|
||||
rows: {
|
||||
control: 'number'
|
||||
},
|
||||
className: {
|
||||
control: 'text'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof InputTextAreaButton>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
placeholder: 'Enter text here...',
|
||||
rows: 4
|
||||
}
|
||||
};
|
||||
|
||||
export const WithAutoResize: Story = {
|
||||
args: {
|
||||
placeholder: 'Type to see auto-resize in action...',
|
||||
autoResize: {
|
||||
minRows: 1,
|
||||
maxRows: 6
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
placeholder: 'Disabled textarea',
|
||||
value: 'Cannot edit this text',
|
||||
rows: 4
|
||||
}
|
||||
};
|
||||
|
||||
export const CustomPlaceholder: Story = {
|
||||
args: {
|
||||
placeholder: 'Type your message here...',
|
||||
rows: 3
|
||||
}
|
||||
};
|
||||
|
||||
export const WithInitialValue: Story = {
|
||||
args: {
|
||||
value: 'This is some initial text in the textarea...',
|
||||
rows: 4
|
||||
}
|
||||
};
|
|
@ -1,55 +1,115 @@
|
|||
import React from 'react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { InputTextArea, InputTextAreaProps } from './InputTextArea';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { Button } from '../buttons/Button';
|
||||
import { ArrowUp } from '../icons/NucleoIconOutlined';
|
||||
import { ShapeSquare } from '../icons/NucleoIconFilled';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
export interface InputTextAreaButtonProps extends Omit<InputTextAreaProps, 'variant'> {}
|
||||
const inputTextAreaButtonVariants = cva(
|
||||
'relative flex w-full items-center overflow-hidden rounded border border-border transition-all duration-200',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'has-[textarea:hover]:border-foreground has-[textarea:focus]:border-foreground has-[textarea:disabled]:border-border'
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const inputTextAreaButtonVariants = cva('relative flex w-full items-center overflow-hidden', {
|
||||
variants: {}
|
||||
});
|
||||
export interface InputTextAreaButtonProps extends Omit<InputTextAreaProps, 'variant' | 'onSubmit'> {
|
||||
sendIcon?: React.ReactNode;
|
||||
loadingIcon?: React.ReactNode;
|
||||
loading?: boolean;
|
||||
onSubmit: (text: string) => void;
|
||||
variant?: 'default';
|
||||
}
|
||||
|
||||
export const InputTextAreaButton: React.FC<InputTextAreaButtonProps> = ({
|
||||
className,
|
||||
disabled,
|
||||
autoResize,
|
||||
sendIcon = <ArrowUp />,
|
||||
loadingIcon = <ShapeSquare />,
|
||||
loading = false,
|
||||
onSubmit,
|
||||
variant = 'default',
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
// styles.inputContainer,
|
||||
// isFocused && 'focused',
|
||||
// loading && 'loading',
|
||||
inputTextAreaButtonVariants()
|
||||
)}>
|
||||
<InputTextArea
|
||||
disabled={disabled}
|
||||
variant="ghost"
|
||||
className="inline-block w-full pt-2! pr-9! pb-2! pl-3.5! align-middle"
|
||||
{...props}
|
||||
const textRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// ref={inputRef}
|
||||
// variant="borderless"
|
||||
// onBlur={onBlurInput}
|
||||
// onFocus={onFocusInput}
|
||||
// className="inline-block w-full pt-2! pr-9! pb-2! pl-3.5! align-middle"
|
||||
// placeholder="Ask a follow up..."
|
||||
// value={inputValue}
|
||||
// autoFocus={true}
|
||||
// onChange={onChangeInput}
|
||||
// onPressEnter={onPressEnter}
|
||||
// disabled={loading}
|
||||
// autoSize={autoSize}
|
||||
const onSubmitPreflight = useMemoizedFn(() => {
|
||||
if (disabled) return;
|
||||
const text = textRef.current?.value || '';
|
||||
if (text.trim() === '') return;
|
||||
onSubmit(text);
|
||||
});
|
||||
|
||||
const onPressMetaEnter = useMemoizedFn(() => {
|
||||
onSubmitPreflight();
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={cn(inputTextAreaButtonVariants(), loading && 'border-border!', className)}>
|
||||
<InputTextArea
|
||||
ref={textRef}
|
||||
disabled={disabled || loading}
|
||||
variant="ghost"
|
||||
className={cn('w-full pr-10 align-middle leading-[1.2]', loading && '!cursor-default')}
|
||||
autoResize={autoResize}
|
||||
onPressMetaEnter={onPressMetaEnter}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
<div className="absolute right-2 bottom-2">
|
||||
HERE
|
||||
{/* <SubmitButton
|
||||
disableSendButton={disableSendButton}
|
||||
<SubmitButton
|
||||
disabled={disabled}
|
||||
loading={loading}
|
||||
sendIcon={sendIcon}
|
||||
loadingIcon={loadingIcon}
|
||||
onSubmitPreflight={onSubmitPreflight}
|
||||
/> */}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SubmitButton: React.FC<{
|
||||
loading: boolean;
|
||||
disabled?: boolean;
|
||||
sendIcon: React.ReactNode;
|
||||
loadingIcon: React.ReactNode;
|
||||
onSubmitPreflight: () => void;
|
||||
}> = ({ disabled, sendIcon, loading, loadingIcon, onSubmitPreflight }) => {
|
||||
const memoizedPrefix = useMemo(() => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'relative h-4 w-4 transition-all duration-300 active:scale-80',
|
||||
loading && '!cursor-default'
|
||||
)}>
|
||||
<div
|
||||
className={`absolute inset-0 transition-all duration-300 ${loading ? 'scale-95 opacity-0' : 'scale-100 opacity-100'}`}>
|
||||
{sendIcon}
|
||||
</div>
|
||||
<div
|
||||
className={`absolute inset-0 flex items-center justify-center text-sm transition-all duration-300 ${loading ? 'scale-100 opacity-100' : 'scale-95 opacity-0'}`}>
|
||||
{loadingIcon}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [loading, sendIcon, loadingIcon]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
rounding={'large'}
|
||||
variant="black"
|
||||
prefix={memoizedPrefix}
|
||||
className="active:scale-95"
|
||||
onClick={onSubmitPreflight}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { AppMaterialIcons } from '@/components/ui';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
|
|
Loading…
Reference in New Issue