mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-05-05 22:26:50 +02:00
Add Loading Spinner API to content action overflow menu extension point
This commit is contained in:
2
gradle/changelog/changed_content_action_menu_api.yaml
Normal file
2
gradle/changelog/changed_content_action_menu_api.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: changed
|
||||
description: The internal API for content action menus were changed, to handle loading states of extensions
|
||||
@@ -640,6 +640,7 @@ export type ContentActionExtensionProps = {
|
||||
revision: string;
|
||||
handleExtensionError: React.Dispatch<React.SetStateAction<Error | undefined>>;
|
||||
contentType?: ContentType;
|
||||
setLoading?: (isLoading: boolean) => void;
|
||||
};
|
||||
|
||||
type BaseActionBarOverflowMenuProps = {
|
||||
|
||||
@@ -23,19 +23,25 @@
|
||||
*/
|
||||
|
||||
import { binder, extensionPoints } from "@scm-manager/ui-extensions";
|
||||
import React, { FC, ReactElement, useState } from "react";
|
||||
import React, { FC, ReactElement, useCallback, useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Menu } from "@scm-manager/ui-overlays";
|
||||
import FallbackMenuButton from "./FallbackMenuButton";
|
||||
import MenuItem from "./MenuItem";
|
||||
import { Icon } from "@scm-manager/ui-buttons";
|
||||
import { Button, Icon } from "@scm-manager/ui-buttons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SmallLoadingSpinner } from "@scm-manager/ui-components";
|
||||
|
||||
const HR = styled.hr`
|
||||
margin: 0.25rem;
|
||||
background: var(--scm-border-color);
|
||||
`;
|
||||
|
||||
const StyledLoadingButton = styled(Button)`
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
extensionProps: extensionPoints.ContentActionExtensionProps;
|
||||
};
|
||||
@@ -47,6 +53,13 @@ const ContentActionMenu: FC<Props> = ({ extensionProps }) => {
|
||||
"repos.sources.content.actionbar.menu",
|
||||
extensionProps
|
||||
);
|
||||
|
||||
const [loadingExtension, setLoadingExtensions] = useState<Record<string, boolean>>({});
|
||||
|
||||
const setLoading = useCallback((isLoading: boolean, extension: string) => {
|
||||
setLoadingExtensions((prevState) => ({ ...prevState, [extension]: isLoading }));
|
||||
}, []);
|
||||
|
||||
const categories = extensions.reduce<Record<string, extensionPoints.FileViewActionBarOverflowMenu["type"][]>>(
|
||||
(result, extension) => {
|
||||
if (!(extension.category in result)) {
|
||||
@@ -64,7 +77,11 @@ const ContentActionMenu: FC<Props> = ({ extensionProps }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{extensions.length === 1 ? (
|
||||
{Object.values(loadingExtension).some((isLoading) => isLoading) ? (
|
||||
<StyledLoadingButton>
|
||||
<SmallLoadingSpinner />
|
||||
</StyledLoadingButton>
|
||||
) : extensions.length === 1 ? (
|
||||
<FallbackMenuButton
|
||||
extension={extensions[0]}
|
||||
extensionProps={extensionProps}
|
||||
@@ -88,6 +105,7 @@ const ContentActionMenu: FC<Props> = ({ extensionProps }) => {
|
||||
key={extension.label}
|
||||
extensionProps={extensionProps}
|
||||
setSelectedModal={setSelectedModal}
|
||||
setLoading={(isLoading: boolean) => setLoading(isLoading, extension.label)}
|
||||
{...extension}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -32,8 +32,9 @@ const MenuItem: FC<
|
||||
extensionPoints.FileViewActionBarOverflowMenu["type"] & {
|
||||
setSelectedModal: (element: ReactElement | undefined) => void;
|
||||
extensionProps: extensionPoints.ContentActionExtensionProps;
|
||||
setLoading?: (isLoading: boolean) => void;
|
||||
}
|
||||
> = ({ extensionProps, label, icon, props, category, setSelectedModal, ...rest }) => {
|
||||
> = ({ extensionProps, label, icon, props, category, setSelectedModal, setLoading, ...rest }) => {
|
||||
if ("action" in rest) {
|
||||
return (
|
||||
<ActionMenuItem
|
||||
@@ -66,6 +67,7 @@ const MenuItem: FC<
|
||||
icon={icon}
|
||||
extensionProps={extensionProps}
|
||||
setSelectedModal={setSelectedModal}
|
||||
setLoading={setLoading}
|
||||
props={props}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
@@ -32,15 +32,16 @@ const ModalMenuItem: FC<
|
||||
extensionPoints.ModalMenuProps & {
|
||||
setSelectedModal: (element: ReactElement | undefined) => void;
|
||||
extensionProps: extensionPoints.ContentActionExtensionProps;
|
||||
setLoading?: (isLoading: boolean) => void;
|
||||
}
|
||||
> = ({ modalElement, label, icon, props, extensionProps, setSelectedModal }) => {
|
||||
> = ({ modalElement, label, icon, props, extensionProps, setSelectedModal, setLoading }) => {
|
||||
const [t] = useTranslation("plugins");
|
||||
|
||||
return (
|
||||
<Menu.Button
|
||||
onSelect={() =>
|
||||
setSelectedModal(
|
||||
React.createElement(modalElement, { ...extensionProps, close: () => setSelectedModal(undefined) })
|
||||
React.createElement(modalElement, { ...extensionProps, close: () => setSelectedModal(undefined), setLoading })
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
|
||||
Reference in New Issue
Block a user