### What problem does this PR solve? Feat: Add LoadingButton #4368 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.16.0
| @@ -0,0 +1,75 @@ | |||
| // https://github.com/hsuanyi-chou/shadcn-ui-expansions/blob/main/components/ui/loading-button.tsx | |||
| import { cn } from '@/lib/utils'; | |||
| import { Slot, Slottable } from '@radix-ui/react-slot'; | |||
| import { cva, type VariantProps } from 'class-variance-authority'; | |||
| import { Loader2 } from 'lucide-react'; | |||
| import * as React from 'react'; | |||
| const buttonVariants = cva( | |||
| 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', | |||
| { | |||
| variants: { | |||
| variant: { | |||
| default: 'bg-primary text-primary-foreground hover:bg-primary/90', | |||
| destructive: | |||
| 'bg-destructive text-destructive-foreground hover:bg-destructive/90', | |||
| outline: | |||
| 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', | |||
| secondary: | |||
| 'bg-secondary text-secondary-foreground hover:bg-secondary/80', | |||
| ghost: 'hover:bg-accent hover:text-accent-foreground', | |||
| link: 'text-primary underline-offset-4 hover:underline', | |||
| }, | |||
| size: { | |||
| default: 'h-10 px-4 py-2', | |||
| sm: 'h-9 rounded-md px-3', | |||
| lg: 'h-11 rounded-md px-8', | |||
| icon: 'h-10 w-10', | |||
| }, | |||
| }, | |||
| defaultVariants: { | |||
| variant: 'default', | |||
| size: 'default', | |||
| }, | |||
| }, | |||
| ); | |||
| export interface ButtonProps | |||
| extends React.ButtonHTMLAttributes<HTMLButtonElement>, | |||
| VariantProps<typeof buttonVariants> { | |||
| asChild?: boolean; | |||
| loading?: boolean; | |||
| } | |||
| const LoadingButton = React.forwardRef<HTMLButtonElement, ButtonProps>( | |||
| ( | |||
| { | |||
| className, | |||
| loading = false, | |||
| children, | |||
| disabled, | |||
| variant, | |||
| size, | |||
| asChild = false, | |||
| ...props | |||
| }, | |||
| ref, | |||
| ) => { | |||
| const Comp = asChild ? Slot : 'button'; | |||
| return ( | |||
| <Comp | |||
| className={cn(buttonVariants({ variant, size, className }))} | |||
| ref={ref} | |||
| disabled={loading || disabled} | |||
| {...props} | |||
| > | |||
| {loading && <Loader2 className="mr-2 h-5 w-5 animate-spin" />} | |||
| <Slottable>{children}</Slottable> | |||
| </Comp> | |||
| ); | |||
| }, | |||
| ); | |||
| LoadingButton.displayName = 'LoadingButton'; | |||
| export { LoadingButton, buttonVariants }; | |||
| @@ -321,7 +321,7 @@ export const useRenameTag = () => { | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| mutationKey: ['deleteTag'], | |||
| mutationKey: ['renameTag'], | |||
| mutationFn: async (params: IRenameTag) => { | |||
| const { data } = await renameTag(knowledgeBaseId, params); | |||
| if (data.code === 0) { | |||
| @@ -337,4 +337,8 @@ export const useRenameTag = () => { | |||
| return { data, loading, renameTag: mutateAsync }; | |||
| }; | |||
| export const useTagIsRenaming = () => { | |||
| return useIsMutating({ mutationKey: ['renameTag'] }) > 0; | |||
| }; | |||
| //#endregion | |||
| @@ -2,7 +2,6 @@ import { LlmModelType } from '@/constants/knowledge'; | |||
| import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { | |||
| useFetchKnowledgeBaseConfiguration, | |||
| useRenameTag, | |||
| useUpdateKnowledge, | |||
| } from '@/hooks/knowledge-hooks'; | |||
| import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks'; | |||
| @@ -97,21 +96,6 @@ export const useRenameKnowledgeTag = () => { | |||
| hideModal: hideTagRenameModal, | |||
| showModal: showFileRenameModal, | |||
| } = useSetModalState(); | |||
| const { renameTag, loading } = useRenameTag(); | |||
| const onTagRenameOk = useCallback( | |||
| async (name: string) => { | |||
| const ret = await renameTag({ | |||
| fromTag: tag, | |||
| toTag: name, | |||
| }); | |||
| if (ret === 0) { | |||
| hideTagRenameModal(); | |||
| } | |||
| }, | |||
| [renameTag, tag, hideTagRenameModal], | |||
| ); | |||
| const handleShowTagRenameModal = useCallback( | |||
| (record: string) => { | |||
| @@ -122,9 +106,7 @@ export const useRenameKnowledgeTag = () => { | |||
| ); | |||
| return { | |||
| renameLoading: loading, | |||
| initialName: tag, | |||
| onTagRenameOk, | |||
| tagRenameVisible, | |||
| hideTagRenameModal, | |||
| showTagRenameModal: handleShowTagRenameModal, | |||
| @@ -74,7 +74,6 @@ export function TagTable() { | |||
| showTagRenameModal, | |||
| hideTagRenameModal, | |||
| tagRenameVisible, | |||
| onTagRenameOk, | |||
| initialName, | |||
| } = useRenameKnowledgeTag(); | |||
| @@ -300,7 +299,6 @@ export function TagTable() { | |||
| {tagRenameVisible && ( | |||
| <RenameDialog | |||
| hideModal={hideTagRenameModal} | |||
| onOk={onTagRenameOk} | |||
| initialName={initialName} | |||
| ></RenameDialog> | |||
| )} | |||
| @@ -1,4 +1,3 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| Dialog, | |||
| DialogContent, | |||
| @@ -6,6 +5,8 @@ import { | |||
| DialogHeader, | |||
| DialogTitle, | |||
| } from '@/components/ui/dialog'; | |||
| import { LoadingButton } from '@/components/ui/loading-button'; | |||
| import { useTagIsRenaming } from '@/hooks/knowledge-hooks'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { TagRenameId } from '@/pages/add-knowledge/constant'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| @@ -16,6 +17,7 @@ export function RenameDialog({ | |||
| initialName, | |||
| }: IModalProps<any> & { initialName: string }) { | |||
| const { t } = useTranslation(); | |||
| const loading = useTagIsRenaming(); | |||
| return ( | |||
| <Dialog open onOpenChange={hideModal}> | |||
| @@ -28,9 +30,9 @@ export function RenameDialog({ | |||
| hideModal={hideModal} | |||
| ></RenameForm> | |||
| <DialogFooter> | |||
| <Button type="submit" form={TagRenameId}> | |||
| <LoadingButton type="submit" form={TagRenameId} loading={loading}> | |||
| {t('common.save')} | |||
| </Button> | |||
| </LoadingButton> | |||
| </DialogFooter> | |||
| </DialogContent> | |||
| </Dialog> | |||