mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-01-07 16:12:10 +01:00
Rework branch overview
Committed-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com> Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
2
gradle/changelog/rework_branch_overview.yaml
Normal file
2
gradle/changelog/rework_branch_overview.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: changed
|
||||
description: Rework branch overview
|
||||
@@ -26,6 +26,7 @@ import React, { ComponentType, ReactNode } from "react";
|
||||
import {
|
||||
Branch,
|
||||
Changeset,
|
||||
ContentType,
|
||||
File,
|
||||
Group,
|
||||
HalRepresentation,
|
||||
@@ -44,7 +45,6 @@ import {
|
||||
RepositoryTypeCollection,
|
||||
Tag,
|
||||
User,
|
||||
ContentType,
|
||||
} from "@scm-manager/ui-types";
|
||||
import { ExtensionPointDefinition } from "./binder";
|
||||
import { RenderableExtensionPointDefinition, SimpleRenderableDynamicExtensionPointDefinition } from "./ExtensionPoint";
|
||||
@@ -680,3 +680,21 @@ export type GroupInformationTableBottom = RenderableExtensionPointDefinition<
|
||||
"group.information.table.bottom",
|
||||
{ group: Group }
|
||||
>;
|
||||
|
||||
type BranchListDetailProps = {
|
||||
repository: Repository;
|
||||
branch: Branch;
|
||||
labelId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @since 2.46.0
|
||||
*/
|
||||
export type BranchListDetail = ExtensionPointDefinition<
|
||||
"branches.list.detail",
|
||||
{
|
||||
name: string;
|
||||
render: (props: BranchListDetailProps) => ReactNode | null;
|
||||
},
|
||||
Omit<BranchListDetailProps, "labelId">
|
||||
>;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"@scm-manager/ui-overlays": "2.45.3-SNAPSHOT",
|
||||
"@scm-manager/ui-buttons": "2.45.3-SNAPSHOT",
|
||||
"@storybook/addon-actions": "^6.5.10",
|
||||
"@storybook/addon-docs": "^6.5.14",
|
||||
"@storybook/addon-essentials": "^6.5.10",
|
||||
"@storybook/addon-interactions": "^6.5.10",
|
||||
"@storybook/addon-links": "^6.5.10",
|
||||
@@ -27,17 +28,18 @@
|
||||
"@storybook/manager-webpack5": "^6.5.10",
|
||||
"@storybook/react": "^6.5.10",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@storybook/addon-docs": "^6.5.14",
|
||||
"babel-loader": "^8.2.5",
|
||||
"storybook-addon-mock": "^3.2.0",
|
||||
"storybook-addon-themes": "^6.1.0",
|
||||
"tsup": "^6.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"classnames": "2",
|
||||
"react": "17",
|
||||
"react-dom": "17",
|
||||
"classnames": "2",
|
||||
"styled-components": "5"
|
||||
"styled-components": "5",
|
||||
"@scm-manager/ui-buttons": "2.45.3-SNAPSHOT",
|
||||
"@scm-manager/ui-overlays": "2.45.3-SNAPSHOT"
|
||||
},
|
||||
"prettier": "@scm-manager/prettier-config",
|
||||
"eslintConfig": {
|
||||
@@ -45,5 +47,9 @@
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-collapsible": "^1.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
import React, { ComponentType, HTMLAttributes, ReactHTML, Ref } from "react";
|
||||
import styled from "styled-components";
|
||||
import classNames from "classnames";
|
||||
import CSS from "csstype";
|
||||
|
||||
const CardRowsContainer = styled.div`
|
||||
overflow: hidden;
|
||||
@@ -33,28 +34,38 @@ const CardRowsContainer = styled.div`
|
||||
type Props = HTMLAttributes<HTMLElement> & {
|
||||
action?: React.ReactElement;
|
||||
/**
|
||||
* @default 'div'
|
||||
* @default "div"
|
||||
*/
|
||||
as?: keyof ReactHTML | ComponentType<HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> }>;
|
||||
|
||||
/**
|
||||
* @default "0.5rem"
|
||||
* @since 2.46.0
|
||||
*/
|
||||
rowGap?: CSS.Properties<string | number>["gap"];
|
||||
};
|
||||
|
||||
/**
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
const Card = React.forwardRef<HTMLElement, Props>(({ className, children, as: Comp = "div", action, ...props }, ref) =>
|
||||
React.createElement(
|
||||
Comp,
|
||||
{
|
||||
className: classNames(className, "is-relative", "is-flex", "scmm-card"),
|
||||
ref,
|
||||
...props,
|
||||
},
|
||||
<CardRowsContainer className="is-flex is-flex-direction-column is-justify-content-center is-flex-grow-1">
|
||||
{children}
|
||||
</CardRowsContainer>,
|
||||
action ? <span className="ml-2">{action}</span> : null
|
||||
)
|
||||
const Card = React.forwardRef<HTMLElement, Props>(
|
||||
({ className, rowGap = "0.5rem", children, as: Comp = "div", action, ...props }, ref) =>
|
||||
React.createElement(
|
||||
Comp,
|
||||
{
|
||||
className: classNames(className, "is-relative", "is-flex", "scmm-card"),
|
||||
ref,
|
||||
...props,
|
||||
},
|
||||
<CardRowsContainer
|
||||
className="is-flex is-flex-direction-column is-justify-content-center is-flex-grow-1"
|
||||
style={{ gap: rowGap }}
|
||||
>
|
||||
{children}
|
||||
</CardRowsContainer>,
|
||||
action ? <span className="ml-2">{action}</span> : null
|
||||
)
|
||||
);
|
||||
|
||||
export default Card;
|
||||
|
||||
@@ -26,6 +26,6 @@
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
const CardRow = "div";
|
||||
const CardRow = "div" as const;
|
||||
|
||||
export default CardRow;
|
||||
|
||||
50
scm-ui/ui-layout/src/collapsible/Collapsible.stories.tsx
Normal file
50
scm-ui/ui-layout/src/collapsible/Collapsible.stories.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import StoryRouter from "storybook-react-router";
|
||||
import { ComponentMeta, StoryFn } from "@storybook/react";
|
||||
import React, { ComponentProps } from "react";
|
||||
import { ExtractProps } from "@scm-manager/ui-extensions";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Menu } from "@scm-manager/ui-overlays";
|
||||
import { Icon } from "@scm-manager/ui-buttons";
|
||||
import CardRow from "../card/CardRow";
|
||||
import Collapsible from "./Collapsible";
|
||||
|
||||
export default {
|
||||
title: "Collapsible",
|
||||
component: Collapsible,
|
||||
decorators: [StoryRouter()],
|
||||
} as ComponentMeta<typeof Collapsible>;
|
||||
|
||||
const Template: StoryFn<ExtractProps<typeof Collapsible>> = (args) => <Collapsible {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||
Default.args = {
|
||||
children: [
|
||||
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
|
||||
],
|
||||
header: "This is a collapsible",
|
||||
} as ComponentProps<typeof Collapsible>;
|
||||
61
scm-ui/ui-layout/src/collapsible/Collapsible.tsx
Normal file
61
scm-ui/ui-layout/src/collapsible/Collapsible.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { ComponentProps, ReactNode, useState } from "react";
|
||||
import * as RadixCollapsible from "@radix-ui/react-collapsible";
|
||||
import { Icon } from "@scm-manager/ui-buttons";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledTrigger = styled(RadixCollapsible.Trigger)`
|
||||
margin-right: 0.5rem;
|
||||
`;
|
||||
|
||||
const StyledCollapsibleHeader = styled.div`
|
||||
background-color: var(--scm-secondary-less-color);
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
header: ReactNode;
|
||||
} & Pick<ComponentProps<typeof RadixCollapsible.Root>, "defaultOpen">;
|
||||
|
||||
/**
|
||||
* @beta;
|
||||
* @since 2.46.0
|
||||
*/
|
||||
const Collapsible = React.forwardRef<HTMLButtonElement, Props>(({ children, header, defaultOpen }, ref) => {
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
return (
|
||||
<RadixCollapsible.Root className="card" open={open} onOpenChange={setOpen} defaultOpen={defaultOpen}>
|
||||
<StyledCollapsibleHeader className="card-header is-flex is-justify-content-space-between is-shadowless">
|
||||
<span className="card-header-title">{header}</span>
|
||||
<StyledTrigger className="card-header-icon" ref={ref}>
|
||||
<Icon>{open ? "angle-up" : "angle-down"}</Icon>
|
||||
</StyledTrigger>
|
||||
</StyledCollapsibleHeader>
|
||||
<RadixCollapsible.Content className="card-content p-2">{children}</RadixCollapsible.Content>
|
||||
</RadixCollapsible.Root>
|
||||
);
|
||||
});
|
||||
|
||||
export default Collapsible;
|
||||
@@ -26,6 +26,8 @@ import CardListComponent, { CardListBox as CardListBoxComponent, CardListCard }
|
||||
import CardTitle from "./card/CardTitle";
|
||||
import CardRow from "./card/CardRow";
|
||||
|
||||
export { default as Collapsible } from "./collapsible/Collapsible";
|
||||
|
||||
const CardListExport = {
|
||||
Card: Object.assign(CardListCard, {
|
||||
Row: CardRow,
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-tooltip": "1.0.2",
|
||||
"@radix-ui/react-dropdown-menu": "2.0.4",
|
||||
"@radix-ui/react-dialog": "1.0.2",
|
||||
"@radix-ui/react-dropdown-menu": "2.0.1",
|
||||
"@scm-manager/ui-buttons": "2.45.3-SNAPSHOT"
|
||||
},
|
||||
"prettier": "@scm-manager/prettier-config",
|
||||
@@ -51,4 +52,5 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
65
scm-ui/ui-overlays/src/dialog/Dialog.stories.tsx
Normal file
65
scm-ui/ui-overlays/src/dialog/Dialog.stories.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import StoryRouter from "storybook-react-router";
|
||||
import { ComponentMeta, StoryFn } from "@storybook/react";
|
||||
import React, { ComponentProps } from "react";
|
||||
import { ExtractProps } from "@scm-manager/ui-extensions";
|
||||
import { Button } from "@scm-manager/ui-buttons";
|
||||
import Dialog from "./Dialog";
|
||||
|
||||
export default {
|
||||
title: "Dialog",
|
||||
component: Dialog,
|
||||
decorators: [StoryRouter()],
|
||||
render: (story) => <div style={{ height: "100vh" }}>{story}</div>,
|
||||
} as ComponentMeta<typeof Dialog>;
|
||||
|
||||
const Template: StoryFn<ExtractProps<typeof Dialog>> = (args) => <Dialog {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||
Default.args = {
|
||||
title: "My Dialog",
|
||||
trigger: <Button>Open Dialog</Button>,
|
||||
description: "Do you really want to do this ?",
|
||||
children: (
|
||||
<table>
|
||||
<tr>
|
||||
<th>Yes</th>
|
||||
<th>No</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>42</td>
|
||||
<td>12</td>
|
||||
</tr>
|
||||
</table>
|
||||
),
|
||||
footer: [
|
||||
<Button>Do it</Button>,
|
||||
<Button variant="primary" autoFocus>
|
||||
Cancel
|
||||
</Button>,
|
||||
],
|
||||
} as ComponentProps<typeof Dialog>;
|
||||
85
scm-ui/ui-overlays/src/dialog/Dialog.tsx
Normal file
85
scm-ui/ui-overlays/src/dialog/Dialog.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import * as RadixDialog from "@radix-ui/react-dialog";
|
||||
import { DialogProps } from "@radix-ui/react-dialog";
|
||||
import React, { ComponentProps, ReactComponentElement, ReactElement, ReactNode, useCallback } from "react";
|
||||
import { Button } from "@scm-manager/ui-buttons";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const CloseButton = React.forwardRef<HTMLButtonElement, ComponentProps<typeof Button>>(
|
||||
({ children, ...props }, ref) => (
|
||||
<RadixDialog.Close asChild>
|
||||
<Button {...props} ref={ref}>
|
||||
{children}
|
||||
</Button>
|
||||
</RadixDialog.Close>
|
||||
)
|
||||
);
|
||||
|
||||
type Props = {
|
||||
trigger: ReactComponentElement<typeof Button>;
|
||||
title: ReactNode;
|
||||
description?: ReactNode;
|
||||
footer?: ReactElement[] | ((close: () => void) => ReactElement[]);
|
||||
} & Pick<DialogProps, "onOpenChange" | "open">;
|
||||
|
||||
/**
|
||||
* @beta
|
||||
* @since 2.46.0
|
||||
*/
|
||||
const Dialog = React.forwardRef<HTMLDivElement, Props>(
|
||||
({ children, onOpenChange, open, footer, title, trigger, description }, ref) => {
|
||||
const contentProps = description ? {} : { "aria-describedby": undefined };
|
||||
const close = useCallback(() => onOpenChange && onOpenChange(false), [onOpenChange]);
|
||||
const [t] = useTranslation("commons");
|
||||
return (
|
||||
<RadixDialog.Root open={open} onOpenChange={onOpenChange}>
|
||||
<RadixDialog.Trigger asChild>{trigger}</RadixDialog.Trigger>
|
||||
<RadixDialog.Portal>
|
||||
<RadixDialog.Overlay className="modal-background" />
|
||||
<RadixDialog.Content className="modal is-active" ref={ref} {...contentProps}>
|
||||
<div className="modal-card">
|
||||
<div className="modal-card-head">
|
||||
<RadixDialog.Title className="modal-card-title">{title}</RadixDialog.Title>
|
||||
<RadixDialog.Close asChild>
|
||||
<button className="delete" aria-label={t("dialog.closeButton.ariaLabel")} />
|
||||
</RadixDialog.Close>
|
||||
</div>
|
||||
<div className="modal-card-body">
|
||||
{description ? <RadixDialog.Description>{description}</RadixDialog.Description> : null}
|
||||
{children}
|
||||
</div>
|
||||
{footer ? (
|
||||
<div className="modal-card-foot">{typeof footer === "function" ? footer(close) : footer}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</RadixDialog.Content>
|
||||
</RadixDialog.Portal>
|
||||
</RadixDialog.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default Dialog;
|
||||
@@ -22,8 +22,9 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import MenuComponent, { MenuButton, MenuExternalLink, MenuLink } from "./menu/Menu";
|
||||
import MenuComponent, { MenuButton, MenuDialog, MenuExternalLink, MenuLink } from "./menu/Menu";
|
||||
import MenuTrigger, { DefaultMenuTrigger } from "./menu/MenuTrigger";
|
||||
import DialogComponent, { CloseButton } from "./dialog/Dialog";
|
||||
|
||||
export { default as Tooltip } from "./tooltip/Tooltip";
|
||||
|
||||
@@ -31,6 +32,11 @@ export const Menu = Object.assign(MenuComponent, {
|
||||
Button: MenuButton,
|
||||
Link: MenuLink,
|
||||
ExternalLink: MenuExternalLink,
|
||||
DialogButton: MenuDialog,
|
||||
Trigger: MenuTrigger,
|
||||
DefaultTrigger: DefaultMenuTrigger,
|
||||
});
|
||||
|
||||
export const Dialog = Object.assign(DialogComponent, {
|
||||
CloseButton,
|
||||
});
|
||||
|
||||
@@ -26,8 +26,9 @@ import StoryRouter from "storybook-react-router";
|
||||
import { ComponentMeta, StoryFn } from "@storybook/react";
|
||||
import React, { ComponentProps } from "react";
|
||||
import { ExtractProps } from "@scm-manager/ui-extensions";
|
||||
import Menu, { MenuButton, MenuExternalLink, MenuLink } from "./Menu";
|
||||
import { Icon } from "@scm-manager/ui-buttons";
|
||||
import Menu, { MenuButton, MenuDialog, MenuExternalLink, MenuLink } from "./Menu";
|
||||
import { Button, Icon } from "@scm-manager/ui-buttons";
|
||||
import { CloseButton } from "../dialog/Dialog";
|
||||
|
||||
export default {
|
||||
title: "Menu",
|
||||
@@ -54,5 +55,25 @@ Default.args = {
|
||||
<MenuButton disabled>
|
||||
<Icon>trash</Icon>A disabled button
|
||||
</MenuButton>,
|
||||
<MenuDialog
|
||||
title="My Dialog"
|
||||
description="Do you really want to do this ?"
|
||||
footer={[<Button autoFocus>Do it</Button>, <CloseButton variant="primary">Cancel</CloseButton>]}
|
||||
dialogContent={
|
||||
<table>
|
||||
<tr>
|
||||
<th>Yes</th>
|
||||
<th>No</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>42</td>
|
||||
<td>12</td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
>
|
||||
<Icon />
|
||||
Open Dialog
|
||||
</MenuDialog>,
|
||||
],
|
||||
} as ComponentProps<typeof Menu>;
|
||||
|
||||
@@ -22,17 +22,28 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { AnchorHTMLAttributes, ButtonHTMLAttributes, FC } from "react";
|
||||
import React, {
|
||||
AnchorHTMLAttributes,
|
||||
ButtonHTMLAttributes,
|
||||
ComponentProps,
|
||||
createContext,
|
||||
FC,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import * as RadixMenu from "@radix-ui/react-dropdown-menu";
|
||||
import styled from "styled-components";
|
||||
import { DefaultMenuTrigger } from "./MenuTrigger";
|
||||
import classNames from "classnames";
|
||||
import { Link as ReactRouterLink, LinkProps as ReactRouterLinkProps } from "react-router-dom";
|
||||
import Dialog from "../dialog/Dialog";
|
||||
|
||||
const MenuContent = styled(RadixMenu.Content)`
|
||||
border: var(--scm-border);
|
||||
background-color: var(--scm-secondary-background);
|
||||
z-index: 400;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
@@ -105,6 +116,53 @@ export const MenuButton = React.forwardRef<HTMLButtonElement, MenuButtonProps>(
|
||||
)
|
||||
);
|
||||
|
||||
type MenuContextType = {
|
||||
handleDialogItemOpenChange: (open: boolean) => void;
|
||||
};
|
||||
const MenuContext = createContext<MenuContextType>(null as unknown as MenuContextType);
|
||||
|
||||
type MenuDialogProps = Omit<MenuButtonProps, "onSelect"> &
|
||||
Omit<ComponentProps<typeof Dialog>, "trigger"> & {
|
||||
dialogContent?: ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @beta
|
||||
* @since 2.46.0
|
||||
* @see {@link Dialog}
|
||||
*/
|
||||
export const MenuDialog = React.forwardRef<HTMLButtonElement, MenuDialogProps>(
|
||||
({ children, dialogContent, title, description, footer }, ref) => {
|
||||
const { handleDialogItemOpenChange } = useContext(MenuContext);
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleSelect = useCallback((event: Event) => event.preventDefault(), []);
|
||||
const changeOpen = useCallback(
|
||||
(newValue: boolean) => {
|
||||
setOpen(newValue);
|
||||
handleDialogItemOpenChange(newValue);
|
||||
},
|
||||
[handleDialogItemOpenChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
trigger={
|
||||
<MenuButton ref={ref} onSelect={handleSelect}>
|
||||
{children}
|
||||
</MenuButton>
|
||||
}
|
||||
title={title}
|
||||
description={description}
|
||||
footer={footer}
|
||||
onOpenChange={changeOpen}
|
||||
open={open}
|
||||
>
|
||||
{dialogContent}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
trigger?: React.ReactElement;
|
||||
@@ -119,8 +177,23 @@ type Props = {
|
||||
* @see https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
|
||||
*/
|
||||
const Menu: FC<Props> = ({ children, side, className, trigger = <DefaultMenuTrigger /> }) => {
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const [hasOpenDialog, setHasOpenDialog] = useState(false);
|
||||
const handleDialogItemOpenChange = useCallback((open: boolean) => {
|
||||
setHasOpenDialog(open);
|
||||
if (!open) {
|
||||
setDropdownOpen(false);
|
||||
}
|
||||
}, []);
|
||||
const menuContextValue = useMemo<MenuContextType>(
|
||||
() => ({
|
||||
handleDialogItemOpenChange,
|
||||
}),
|
||||
[handleDialogItemOpenChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<RadixMenu.Root>
|
||||
<RadixMenu.Root open={dropdownOpen} onOpenChange={setDropdownOpen}>
|
||||
{trigger}
|
||||
<RadixMenu.Portal>
|
||||
<MenuContent
|
||||
@@ -128,8 +201,9 @@ const Menu: FC<Props> = ({ children, side, className, trigger = <DefaultMenuTrig
|
||||
side={side}
|
||||
sideOffset={4}
|
||||
collisionPadding={4}
|
||||
hidden={hasOpenDialog}
|
||||
>
|
||||
{children}
|
||||
<MenuContext.Provider value={menuContextValue}>{children}</MenuContext.Provider>
|
||||
</MenuContent>
|
||||
</RadixMenu.Portal>
|
||||
</RadixMenu.Root>
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
.is-white-space-pre {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.has-background-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"dialog": {
|
||||
"closeButton": {
|
||||
"ariaLabel": "Dialog schließen"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"defaultTriggerLabel": "Menü"
|
||||
},
|
||||
|
||||
@@ -121,15 +121,24 @@
|
||||
},
|
||||
"branches": {
|
||||
"overview": {
|
||||
"title": "Übersicht aller verfügbaren Branches",
|
||||
"title": "Branches",
|
||||
"noBranches": "Keine Branches gefunden.",
|
||||
"createButton": "Branch erstellen",
|
||||
"lastCommit": "Letzter Commit"
|
||||
"lastCommit": "Letzter Commit",
|
||||
"sort": {
|
||||
"label": "Sortierung nach",
|
||||
"option": {
|
||||
"default": "Standard",
|
||||
"name_asc": "Name A-Z",
|
||||
"name_desc": "Name Z-A"
|
||||
}
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"branches": {
|
||||
"active": "Aktive Branches",
|
||||
"stale": "Stale Branches"
|
||||
"stale": "Stale Branches",
|
||||
"subtitle": "Letzter Commit <date /> von {{author}}"
|
||||
},
|
||||
"lastCommit": "Letzter Commit",
|
||||
"lastCommitter": "von {{name}}"
|
||||
@@ -153,7 +162,7 @@
|
||||
"behindLabel": "{{count}} hinterher"
|
||||
},
|
||||
"delete": {
|
||||
"button": "Branch löschen",
|
||||
"button": "Löschen",
|
||||
"subtitle": "Branch löschen",
|
||||
"description": "Gelöschte Branches können nicht wiederhergestellt werden.",
|
||||
"confirmAlert": {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"dialog": {
|
||||
"closeButton": {
|
||||
"ariaLabel": "Close Dialog"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"defaultTriggerLabel": "Menu"
|
||||
},
|
||||
|
||||
@@ -121,15 +121,24 @@
|
||||
},
|
||||
"branches": {
|
||||
"overview": {
|
||||
"title": "Overview of All Branches",
|
||||
"title": "Branches",
|
||||
"noBranches": "No branches found.",
|
||||
"createButton": "Create Branch",
|
||||
"lastCommit": "Last commit"
|
||||
"lastCommit": "Last commit",
|
||||
"sort": {
|
||||
"label": "Sort by",
|
||||
"option": {
|
||||
"default": "Default",
|
||||
"name_asc": "Name A-Z",
|
||||
"name_desc": "Name Z-A"
|
||||
}
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"branches": {
|
||||
"active": "Active Branches",
|
||||
"stale": "Stale Branches"
|
||||
"stale": "Stale Branches",
|
||||
"subtitle": "Last commit <date /> by {{author}}"
|
||||
},
|
||||
"lastCommit": "Last commit",
|
||||
"lastCommitter": "by {{name}}"
|
||||
@@ -153,7 +162,7 @@
|
||||
"behindLabel": "{{count}} behind"
|
||||
},
|
||||
"delete": {
|
||||
"button": "Delete Branch",
|
||||
"button": "Delete",
|
||||
"subtitle": "Delete Branch",
|
||||
"description": "Deleted branches cannot be restored.",
|
||||
"confirmAlert": {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Branch, BranchDetails, Repository } from "@scm-manager/ui-types";
|
||||
import React, { FC, useMemo } from "react";
|
||||
import { extensionPoints } from "@scm-manager/ui-extensions";
|
||||
import { CardList } from "@scm-manager/ui-layout";
|
||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { useDeleteBranch } from "@scm-manager/ui-api";
|
||||
import BranchListItem from "./BranchListItem";
|
||||
|
||||
type Props = {
|
||||
baseUrl: string;
|
||||
repository: Repository;
|
||||
branches: Branch[];
|
||||
branchesDetails?: BranchDetails[];
|
||||
};
|
||||
|
||||
const BranchList: FC<Props> = ({ repository, baseUrl, branches, branchesDetails }) => {
|
||||
const { isLoading, error, remove } = useDeleteBranch(repository);
|
||||
const defaultBranchDetails = useMemo<extensionPoints.BranchListDetail["type"][]>(() => [], []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ErrorNotification error={error} />
|
||||
<CardList>
|
||||
{branches.map((branch) => (
|
||||
<BranchListItem
|
||||
key={branch.name}
|
||||
branch={branch}
|
||||
defaultBranchDetails={defaultBranchDetails}
|
||||
remove={remove}
|
||||
isLoading={isLoading}
|
||||
baseUrl={baseUrl}
|
||||
repository={repository}
|
||||
branchDetails={branchesDetails?.find(({ branchName }) => branchName === branch.name)}
|
||||
/>
|
||||
))}
|
||||
</CardList>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default BranchList;
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Dialog, Menu } from "@scm-manager/ui-overlays";
|
||||
import { Icon } from "@scm-manager/ui-buttons";
|
||||
import { CardList } from "@scm-manager/ui-layout";
|
||||
import { Link } from "react-router-dom";
|
||||
import { encodePart } from "../../sources/components/content/FileLink";
|
||||
import { useKeyboardIteratorTarget } from "@scm-manager/ui-shortcuts";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { DateFromNow, useGeneratedId } from "@scm-manager/ui-components";
|
||||
import { extensionPoints, useBinder } from "@scm-manager/ui-extensions";
|
||||
import React, { FC } from "react";
|
||||
import { Branch, BranchDetails, Repository } from "@scm-manager/ui-types";
|
||||
import styled from "styled-components";
|
||||
|
||||
const DetailsContainer = styled(CardList.Card.Row)`
|
||||
gap: 0.5rem 1rem;
|
||||
`;
|
||||
|
||||
const BranchDetail: FC<{
|
||||
branch: Branch;
|
||||
repository: Repository;
|
||||
detail: extensionPoints.BranchListDetail["type"];
|
||||
}> = ({ repository, detail, branch }) => {
|
||||
const labelId = useGeneratedId();
|
||||
const renderedDetail = detail.render({ branch, repository, labelId });
|
||||
if (!renderedDetail) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<span key={detail.name}>
|
||||
<span className="has-text-secondary mr-1" id={labelId}>
|
||||
{detail.name}
|
||||
</span>
|
||||
{renderedDetail}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
type Props = {
|
||||
branch: Branch;
|
||||
defaultBranchDetails: extensionPoints.BranchListDetail["type"][];
|
||||
remove: (branch: Branch) => void;
|
||||
isLoading: boolean;
|
||||
baseUrl: string;
|
||||
repository: Repository;
|
||||
branchDetails?: BranchDetails;
|
||||
};
|
||||
|
||||
const BranchListItem: FC<Props> = ({ branch, defaultBranchDetails, remove, isLoading, baseUrl, repository }) => {
|
||||
const binder = useBinder();
|
||||
const [t] = useTranslation("repos");
|
||||
|
||||
return (
|
||||
<CardList.Card
|
||||
key={branch.name}
|
||||
action={
|
||||
"delete" in branch._links ? (
|
||||
<Menu>
|
||||
<Menu.DialogButton
|
||||
title={t("branch.delete.confirmAlert.title")}
|
||||
description={t("branch.delete.confirmAlert.message", { branch: branch.name })}
|
||||
footer={[
|
||||
<Dialog.CloseButton key="yes" onClick={() => remove(branch)} isLoading={isLoading}>
|
||||
{t("branch.delete.confirmAlert.submit")}
|
||||
</Dialog.CloseButton>,
|
||||
<Dialog.CloseButton key="no" variant="primary" autoFocus>
|
||||
{t("branch.delete.confirmAlert.cancel")}
|
||||
</Dialog.CloseButton>,
|
||||
]}
|
||||
>
|
||||
<Icon>trash</Icon>
|
||||
{t("branch.delete.button")}
|
||||
</Menu.DialogButton>
|
||||
</Menu>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<CardList.Card.Row className="is-flex">
|
||||
<CardList.Card.Title>
|
||||
<Link to={`${baseUrl}/${encodePart(branch.name)}/info`} ref={useKeyboardIteratorTarget()}>
|
||||
{branch.name}
|
||||
</Link>
|
||||
</CardList.Card.Title>
|
||||
</CardList.Card.Row>
|
||||
<CardList.Card.Row className="is-flex is-flex-wrap-wrap is-size-7 has-text-secondary is-white-space-pre">
|
||||
<Trans
|
||||
t={t}
|
||||
i18nKey="branches.table.branches.subtitle"
|
||||
values={{
|
||||
author: branch.lastCommitter?.name,
|
||||
}}
|
||||
components={{
|
||||
date: <DateFromNow className="is-relative" date={branch.lastCommitDate} />,
|
||||
space: <span />,
|
||||
}}
|
||||
/>
|
||||
</CardList.Card.Row>
|
||||
<DetailsContainer className="is-flex is-flex-wrap-wrap is-align-items-center">
|
||||
{[
|
||||
...defaultBranchDetails,
|
||||
...binder.getExtensions<extensionPoints.BranchListDetail>("branches.list.detail", {
|
||||
branch,
|
||||
repository,
|
||||
}),
|
||||
].map((detail) => (
|
||||
<BranchDetail key={detail.name} branch={branch} detail={detail} repository={repository} />
|
||||
))}
|
||||
</DetailsContainer>
|
||||
</CardList.Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default BranchListItem;
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import { Link as ReactLink } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { Branch, BranchDetails, Link, Repository } from "@scm-manager/ui-types";
|
||||
import { Button, devices, SmallLoadingSpinner } from "@scm-manager/ui-components";
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import DefaultBranchTag from "./DefaultBranchTag";
|
||||
import AheadBehindTag from "./AheadBehindTag";
|
||||
import BranchCommitDateCommitter from "./BranchCommitDateCommitter";
|
||||
import { useKeyboardIteratorTarget } from "@scm-manager/ui-shortcuts";
|
||||
import { encodePart } from "../../sources/components/content/FileLink";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
baseUrl: string;
|
||||
branch: Branch;
|
||||
onDelete: (branch: Branch) => void;
|
||||
details?: BranchDetails;
|
||||
};
|
||||
|
||||
const AdaptTableFlow = styled.tr`
|
||||
@media screen and (max-width: ${devices.mobile.width}px) {
|
||||
td {
|
||||
display: block;
|
||||
border-left-width: 3px !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const MobileFlowSpan = styled.span`
|
||||
@media screen and (min-width: ${devices.tablet.width}px) {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
@media screen and (max-width: ${devices.mobile.width}px) {
|
||||
display: block;
|
||||
white-space: break-spaces;
|
||||
word-break: break-word;
|
||||
}
|
||||
`;
|
||||
|
||||
const BranchRow: FC<Props> = ({ repository, baseUrl, branch, onDelete, details }) => {
|
||||
const to = `${baseUrl}/${encodePart(branch.name)}/info`;
|
||||
const [t] = useTranslation("repos");
|
||||
const ref = useKeyboardIteratorTarget();
|
||||
|
||||
let deleteButton;
|
||||
if ((branch?._links?.delete as Link)?.href) {
|
||||
deleteButton = (
|
||||
<Button
|
||||
color="text"
|
||||
icon="trash"
|
||||
action={() => onDelete(branch)}
|
||||
title={t("branch.delete.button")}
|
||||
className="px-2"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const renderBranchTag = () => {
|
||||
if (branch.defaultBranch) {
|
||||
return <DefaultBranchTag defaultBranch={branch.defaultBranch} />;
|
||||
}
|
||||
if (details) {
|
||||
return <AheadBehindTag branch={branch} details={details} hiddenMobile={true} />;
|
||||
}
|
||||
return <SmallLoadingSpinner />;
|
||||
};
|
||||
|
||||
const extensionProps = { repository, branch, details };
|
||||
return (
|
||||
<AdaptTableFlow>
|
||||
<td className="is-vertical-align-middle">
|
||||
<ReactLink ref={ref} to={to} title={branch.name}>
|
||||
{branch.name}
|
||||
</ReactLink>
|
||||
{branch.lastCommitDate && (
|
||||
<MobileFlowSpan className={classNames("has-text-secondary", "is-ellipsis-overflow", "is-size-7")}>
|
||||
<BranchCommitDateCommitter branch={branch} />
|
||||
</MobileFlowSpan>
|
||||
)}
|
||||
</td>
|
||||
<td className="is-vertical-align-middle has-text-centered">{renderBranchTag()}</td>
|
||||
{binder.hasExtension("repos.branches.row.details", extensionProps)
|
||||
? binder
|
||||
.getExtensions("repos.branches.row.details", extensionProps)
|
||||
.map((e) => (
|
||||
<td className="is-vertical-align-middle has-text-centered">{React.createElement(e, extensionProps)}</td>
|
||||
))
|
||||
: null}
|
||||
<td className="is-vertical-align-middle has-text-centered">{deleteButton}</td>
|
||||
</AdaptTableFlow>
|
||||
);
|
||||
};
|
||||
|
||||
export default BranchRow;
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import BranchRow from "./BranchRow";
|
||||
import { Branch, BranchDetails, Repository } from "@scm-manager/ui-types";
|
||||
import { ConfirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { useDeleteBranch } from "@scm-manager/ui-api";
|
||||
|
||||
type Props = {
|
||||
baseUrl: string;
|
||||
repository: Repository;
|
||||
branches: Branch[];
|
||||
type: string;
|
||||
branchesDetails: BranchDetails[];
|
||||
};
|
||||
|
||||
const BranchTable: FC<Props> = ({ repository, baseUrl, branches, type, branchesDetails }) => {
|
||||
const { isLoading, error, remove, isDeleted } = useDeleteBranch(repository);
|
||||
const [t] = useTranslation("repos");
|
||||
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
|
||||
const [branchToBeDeleted, setBranchToBeDeleted] = useState<Branch | undefined>();
|
||||
useEffect(() => {
|
||||
if (isDeleted) {
|
||||
closeAndResetDialog();
|
||||
}
|
||||
}, [isDeleted]);
|
||||
|
||||
const closeAndResetDialog = () => {
|
||||
setBranchToBeDeleted(undefined);
|
||||
setShowConfirmAlert(false);
|
||||
};
|
||||
|
||||
const onDelete = (branch: Branch) => {
|
||||
setBranchToBeDeleted(branch);
|
||||
setShowConfirmAlert(true);
|
||||
};
|
||||
|
||||
const abortDelete = () => {
|
||||
closeAndResetDialog();
|
||||
};
|
||||
|
||||
const deleteBranch = () => {
|
||||
if (branchToBeDeleted) {
|
||||
remove(branchToBeDeleted);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{showConfirmAlert ? (
|
||||
<ConfirmAlert
|
||||
title={t("branch.delete.confirmAlert.title")}
|
||||
message={t("branch.delete.confirmAlert.message", { branch: branchToBeDeleted?.name })}
|
||||
buttons={[
|
||||
{
|
||||
label: t("branch.delete.confirmAlert.submit"),
|
||||
isLoading,
|
||||
onClick: () => deleteBranch()
|
||||
},
|
||||
{
|
||||
className: "is-info",
|
||||
label: t("branch.delete.confirmAlert.cancel"),
|
||||
onClick: () => abortDelete(),
|
||||
autofocus: true
|
||||
}
|
||||
]}
|
||||
close={() => abortDelete()}
|
||||
/>
|
||||
) : null}
|
||||
<ErrorNotification error={error} />
|
||||
<table className="card-table table is-hoverable is-fullwidth is-word-break">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t(`branches.table.branches.${type}`)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(branches || []).map(branch => (
|
||||
<BranchRow
|
||||
key={branch.name}
|
||||
repository={repository}
|
||||
baseUrl={baseUrl}
|
||||
branch={branch}
|
||||
onDelete={onDelete}
|
||||
details={branchesDetails?.filter((b: BranchDetails) => b.branchName === branch.name)[0]}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BranchTable;
|
||||
@@ -22,14 +22,23 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC } from "react";
|
||||
import React, { FC, useMemo, useState } from "react";
|
||||
import { Branch, HalRepresentation, Repository } from "@scm-manager/ui-types";
|
||||
import { CreateButton, ErrorNotification, Notification, Subtitle } from "@scm-manager/ui-components";
|
||||
import { ErrorNotification, Notification, Subtitle } from "@scm-manager/ui-components";
|
||||
import { orderBranches } from "../util/orderBranches";
|
||||
import BranchTable from "../components/BranchTable";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useBranchDetailsCollection } from "@scm-manager/ui-api";
|
||||
import { KeyboardIterator } from "@scm-manager/ui-shortcuts";
|
||||
import BranchList from "../components/BranchList";
|
||||
import { Collapsible } from "@scm-manager/ui-layout";
|
||||
import { LinkButton } from "@scm-manager/ui-buttons";
|
||||
import { Select } from "@scm-manager/ui-forms";
|
||||
import { SORT_OPTIONS, SortOption } from "../../tags/orderTags";
|
||||
import styled from "styled-components";
|
||||
|
||||
const BranchListWrapper = styled.div`
|
||||
gap: 1rem;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
@@ -39,8 +48,11 @@ type Props = {
|
||||
|
||||
const BranchTableWrapper: FC<Props> = ({ repository, baseUrl, data }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const branches: Branch[] = (data?._embedded?.branches as Branch[]) || [];
|
||||
orderBranches(branches);
|
||||
const [sort, setSort] = useState<SortOption | undefined>();
|
||||
const branches: Branch[] = useMemo(
|
||||
() => orderBranches((data?._embedded?.branches as Branch[]) || [], sort),
|
||||
[data, sort]
|
||||
);
|
||||
const staleBranches = branches.filter((b) => b.stale);
|
||||
const activeBranches = branches.filter((b) => !b.stale);
|
||||
const { error, data: branchesDetails } = useBranchDetailsCollection(repository, [
|
||||
@@ -55,29 +67,55 @@ const BranchTableWrapper: FC<Props> = ({ repository, baseUrl, data }) => {
|
||||
const showCreateButton = !!data._links.create;
|
||||
|
||||
return (
|
||||
<KeyboardIterator>
|
||||
<>
|
||||
<Subtitle subtitle={t("branches.overview.title")} />
|
||||
<ErrorNotification error={error} />
|
||||
{activeBranches.length > 0 ? (
|
||||
<BranchTable
|
||||
repository={repository}
|
||||
baseUrl={baseUrl}
|
||||
type="active"
|
||||
branches={activeBranches}
|
||||
branchesDetails={branchesDetails}
|
||||
/>
|
||||
) : null}
|
||||
{staleBranches.length > 0 ? (
|
||||
<BranchTable
|
||||
repository={repository}
|
||||
baseUrl={baseUrl}
|
||||
type="stale"
|
||||
branches={staleBranches}
|
||||
branchesDetails={branchesDetails}
|
||||
/>
|
||||
) : null}
|
||||
{showCreateButton ? <CreateButton label={t("branches.overview.createButton")} link="./create" /> : null}
|
||||
</KeyboardIterator>
|
||||
<div className="is-flex mb-3 is-justify-content-space-between">
|
||||
<div>
|
||||
<div className="is-flex is-align-items-center mb-3">
|
||||
<label className="mr-2" htmlFor="branches-overview-sort">
|
||||
{t("branches.overview.sort.label")}
|
||||
</label>
|
||||
<Select id="branches-overview-sort" onChange={(e) => setSort(e.target.value as SortOption)}>
|
||||
{SORT_OPTIONS.map((sortOption) => (
|
||||
<option key={sortOption} value={sortOption}>
|
||||
{t(`branches.overview.sort.option.${sortOption}`)}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
{showCreateButton ? (
|
||||
<LinkButton variant="primary" to="./create">
|
||||
{t("branches.overview.createButton")}
|
||||
</LinkButton>
|
||||
) : null}
|
||||
</div>
|
||||
<BranchListWrapper className="is-flex is-flex-direction-column">
|
||||
<KeyboardIterator>
|
||||
{activeBranches.length > 0 ? (
|
||||
<Collapsible header={t("branches.table.branches.active")} defaultOpen>
|
||||
<BranchList
|
||||
repository={repository}
|
||||
baseUrl={baseUrl}
|
||||
branches={activeBranches}
|
||||
branchesDetails={branchesDetails}
|
||||
/>
|
||||
</Collapsible>
|
||||
) : null}
|
||||
{staleBranches.length > 0 ? (
|
||||
<Collapsible header={t("branches.table.branches.stale")}>
|
||||
<BranchList
|
||||
repository={repository}
|
||||
baseUrl={baseUrl}
|
||||
branches={staleBranches}
|
||||
branchesDetails={branchesDetails}
|
||||
/>
|
||||
</Collapsible>
|
||||
) : null}
|
||||
</KeyboardIterator>
|
||||
</BranchListWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,33 +26,45 @@
|
||||
// followed by develop the rest should be ordered by its name
|
||||
import { Branch } from "@scm-manager/ui-types";
|
||||
|
||||
export function orderBranches(branches: Branch[]) {
|
||||
export const SORT_OPTIONS = ["default", "name_asc", "name_desc"] as const;
|
||||
|
||||
export type SortOption = typeof SORT_OPTIONS[number];
|
||||
|
||||
export function orderBranches(branches: Branch[], sort?: SortOption) {
|
||||
branches.sort((a, b) => {
|
||||
if (a.defaultBranch && !b.defaultBranch) {
|
||||
return -20;
|
||||
} else if (!a.defaultBranch && b.defaultBranch) {
|
||||
return 20;
|
||||
} else if (a.name === "main" && b.name !== "main") {
|
||||
return -10;
|
||||
} else if (a.name !== "main" && b.name === "main") {
|
||||
return 10;
|
||||
} else if (a.name === "master" && b.name !== "master") {
|
||||
return -9;
|
||||
} else if (a.name !== "master" && b.name === "master") {
|
||||
return 9;
|
||||
} else if (a.name === "default" && b.name !== "default") {
|
||||
return -10;
|
||||
} else if (a.name !== "default" && b.name === "default") {
|
||||
return 10;
|
||||
} else if (a.name === "develop" && b.name !== "develop") {
|
||||
return -5;
|
||||
} else if (a.name !== "develop" && b.name === "develop") {
|
||||
return 5;
|
||||
} else if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (a.name > b.name) {
|
||||
return 1;
|
||||
switch (sort) {
|
||||
case "name_asc":
|
||||
return a.name > b.name ? 1 : -1;
|
||||
case "name_desc":
|
||||
return a.name > b.name ? -1 : 1;
|
||||
default:
|
||||
if (a.defaultBranch && !b.defaultBranch) {
|
||||
return -20;
|
||||
} else if (!a.defaultBranch && b.defaultBranch) {
|
||||
return 20;
|
||||
} else if (a.name === "main" && b.name !== "main") {
|
||||
return -10;
|
||||
} else if (a.name !== "main" && b.name === "main") {
|
||||
return 10;
|
||||
} else if (a.name === "master" && b.name !== "master") {
|
||||
return -9;
|
||||
} else if (a.name !== "master" && b.name === "master") {
|
||||
return 9;
|
||||
} else if (a.name === "default" && b.name !== "default") {
|
||||
return -10;
|
||||
} else if (a.name !== "default" && b.name === "default") {
|
||||
return 10;
|
||||
} else if (a.name === "develop" && b.name !== "develop") {
|
||||
return -5;
|
||||
} else if (a.name !== "develop" && b.name === "develop") {
|
||||
return 5;
|
||||
} else if (a.name < b.name) {
|
||||
return -1;
|
||||
} else if (a.name > b.name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return branches;
|
||||
}
|
||||
|
||||
202
yarn.lock
202
yarn.lock
@@ -2717,6 +2717,13 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/primitive@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
||||
integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-arrow@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.1.tgz#5246adf79e97f89e819af68da51ddcf349ecf1c4"
|
||||
@@ -2725,23 +2732,30 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
|
||||
"@radix-ui/react-arrow@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.2.tgz#93b0ff95f65e2264a05b14ef1031ec798243dd6f"
|
||||
integrity sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==
|
||||
"@radix-ui/react-collapsible@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz#df0e22e7a025439f13f62d4e4a9e92c4a0df5b81"
|
||||
integrity sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/primitive" "1.0.1"
|
||||
"@radix-ui/react-compose-refs" "1.0.1"
|
||||
"@radix-ui/react-context" "1.0.1"
|
||||
"@radix-ui/react-id" "1.0.1"
|
||||
"@radix-ui/react-presence" "1.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||
|
||||
"@radix-ui/react-collection@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.2.tgz#d50da00bfa2ac14585319efdbbb081d4c5a29a97"
|
||||
integrity sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==
|
||||
"@radix-ui/react-collection@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.1.tgz#259506f97c6703b36291826768d3c1337edd1de5"
|
||||
integrity sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-context" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
"@radix-ui/react-slot" "1.0.1"
|
||||
|
||||
"@radix-ui/react-compose-refs@1.0.0":
|
||||
@@ -2765,6 +2779,34 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-context@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
|
||||
integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-dialog@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.2.tgz#2d7a0bfed478afc40ed6fc78d0f53242af55284e"
|
||||
integrity sha512-EKxxp2WNSmUPkx4trtWNmZ4/vAYEg7JkAfa1HKBUnaubw9eHzf1Orr9B472lJYaYz327RHDrd4R95fsw7VR8DA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.0"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-context" "1.0.0"
|
||||
"@radix-ui/react-dismissable-layer" "1.0.2"
|
||||
"@radix-ui/react-focus-guards" "1.0.0"
|
||||
"@radix-ui/react-focus-scope" "1.0.1"
|
||||
"@radix-ui/react-id" "1.0.0"
|
||||
"@radix-ui/react-portal" "1.0.1"
|
||||
"@radix-ui/react-presence" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
"@radix-ui/react-slot" "1.0.1"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.0"
|
||||
aria-hidden "^1.1.1"
|
||||
react-remove-scroll "2.5.5"
|
||||
|
||||
"@radix-ui/react-direction@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.0.tgz#a2e0b552352459ecf96342c79949dd833c1e6e45"
|
||||
@@ -2784,30 +2826,18 @@
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
"@radix-ui/react-use-escape-keydown" "1.0.2"
|
||||
|
||||
"@radix-ui/react-dismissable-layer@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.3.tgz#63844d8e6bbcd010a513e7176d051c3c4044e09e"
|
||||
integrity sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.0"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
"@radix-ui/react-use-escape-keydown" "1.0.2"
|
||||
|
||||
"@radix-ui/react-dropdown-menu@2.0.4":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.4.tgz#237909fb94622a4900b03fbbf75dd394f1ca6273"
|
||||
integrity sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==
|
||||
"@radix-ui/react-dropdown-menu@2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.1.tgz#dbc90676df7fb313d6b1eb204fdb434dbb724d15"
|
||||
integrity sha512-WDZqmwsNuxdBMkvgB85UeSPAT0wSBd+ojxtzX7lU7uYYh47gacCj6Spo0l9+X4TMe3JA1BBMN9c7OhIMaQeKbg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.0"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-context" "1.0.0"
|
||||
"@radix-ui/react-id" "1.0.0"
|
||||
"@radix-ui/react-menu" "2.0.4"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-menu" "2.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.0"
|
||||
|
||||
"@radix-ui/react-focus-guards@1.0.0":
|
||||
@@ -2817,14 +2847,14 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-focus-scope@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.2.tgz#5fe129cbdb5986d0a3ae16d14c473c243fe3bc79"
|
||||
integrity sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==
|
||||
"@radix-ui/react-focus-scope@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.1.tgz#faea8c25f537c5a5c38c50914b63722db0e7f951"
|
||||
integrity sha512-Ej2MQTit8IWJiS2uuujGUmxXjF/y5xZptIIQnyd2JHLwtV0R2j9NRVoRj/1j/gJ7e3REdaBw4Hjf4a1ImhkZcQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
|
||||
"@radix-ui/react-id@1.0.0":
|
||||
@@ -2835,26 +2865,34 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.0"
|
||||
|
||||
"@radix-ui/react-menu@2.0.4":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.0.4.tgz#0bf06f2ee76889ce9bdcf7fa920545f53060824f"
|
||||
integrity sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==
|
||||
"@radix-ui/react-id@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0"
|
||||
integrity sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||
|
||||
"@radix-ui/react-menu@2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.0.1.tgz#44ebfd45d8482db678b935c0b9d1102d683372d8"
|
||||
integrity sha512-I5FFZQxCl2fHoJ7R0m5/oWA9EX8/ttH4AbgneoCH7DAXQioFeb0XMAYnOVSp1GgJZ1Nx/mohxNQSeTMcaF1YPw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.0"
|
||||
"@radix-ui/react-collection" "1.0.2"
|
||||
"@radix-ui/react-collection" "1.0.1"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-context" "1.0.0"
|
||||
"@radix-ui/react-direction" "1.0.0"
|
||||
"@radix-ui/react-dismissable-layer" "1.0.3"
|
||||
"@radix-ui/react-dismissable-layer" "1.0.2"
|
||||
"@radix-ui/react-focus-guards" "1.0.0"
|
||||
"@radix-ui/react-focus-scope" "1.0.2"
|
||||
"@radix-ui/react-focus-scope" "1.0.1"
|
||||
"@radix-ui/react-id" "1.0.0"
|
||||
"@radix-ui/react-popper" "1.1.1"
|
||||
"@radix-ui/react-portal" "1.0.2"
|
||||
"@radix-ui/react-popper" "1.0.1"
|
||||
"@radix-ui/react-portal" "1.0.1"
|
||||
"@radix-ui/react-presence" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-roving-focus" "1.0.3"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
"@radix-ui/react-roving-focus" "1.0.1"
|
||||
"@radix-ui/react-slot" "1.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
aria-hidden "^1.1.1"
|
||||
@@ -2876,23 +2914,6 @@
|
||||
"@radix-ui/react-use-size" "1.0.0"
|
||||
"@radix-ui/rect" "1.0.0"
|
||||
|
||||
"@radix-ui/react-popper@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.1.tgz#54f060941c981e965ff5d6b64e152d6298d2326e"
|
||||
integrity sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@floating-ui/react-dom" "0.7.2"
|
||||
"@radix-ui/react-arrow" "1.0.2"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-context" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.0"
|
||||
"@radix-ui/react-use-rect" "1.0.0"
|
||||
"@radix-ui/react-use-size" "1.0.0"
|
||||
"@radix-ui/rect" "1.0.0"
|
||||
|
||||
"@radix-ui/react-portal@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.1.tgz#169c5a50719c2bb0079cf4c91a27aa6d37e5dd33"
|
||||
@@ -2901,14 +2922,6 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
|
||||
"@radix-ui/react-portal@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.2.tgz#102370b1027a767a371cab0243be4bc664f72330"
|
||||
integrity sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
|
||||
"@radix-ui/react-presence@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz#814fe46df11f9a468808a6010e3f3ca7e0b2e84a"
|
||||
@@ -2918,6 +2931,15 @@
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.0"
|
||||
|
||||
"@radix-ui/react-presence@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba"
|
||||
integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "1.0.1"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||
|
||||
"@radix-ui/react-primitive@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz#c1ebcce283dd2f02e4fbefdaa49d1cb13dbc990a"
|
||||
@@ -2926,14 +2948,6 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-slot" "1.0.1"
|
||||
|
||||
"@radix-ui/react-primitive@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz#54e22f49ca59ba88d8143090276d50b93f8a7053"
|
||||
integrity sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-slot" "1.0.1"
|
||||
|
||||
"@radix-ui/react-primitive@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
|
||||
@@ -2942,19 +2956,19 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-slot" "1.0.2"
|
||||
|
||||
"@radix-ui/react-roving-focus@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.3.tgz#0b4f4f9bd509f4510079e9e0734a734fd17cdce3"
|
||||
integrity sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==
|
||||
"@radix-ui/react-roving-focus@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.1.tgz#475621f63aee43faa183a5270f35d49e530de3d7"
|
||||
integrity sha512-TB76u5TIxKpqMpUAuYH2VqMhHYKa+4Vs1NHygo/llLvlffN6mLVsFhz0AnSFlSBAvTBYVHYAkHAyEt7x1gPJOA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.0"
|
||||
"@radix-ui/react-collection" "1.0.2"
|
||||
"@radix-ui/react-collection" "1.0.1"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-context" "1.0.0"
|
||||
"@radix-ui/react-direction" "1.0.0"
|
||||
"@radix-ui/react-id" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.0"
|
||||
|
||||
@@ -3000,6 +3014,13 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-callback-ref@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
|
||||
integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-controllable-state@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f"
|
||||
@@ -3008,6 +3029,14 @@
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
|
||||
"@radix-ui/react-use-controllable-state@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286"
|
||||
integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||
|
||||
"@radix-ui/react-use-escape-keydown@1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz#09ab6455ab240b4f0a61faf06d4e5132c4d639f6"
|
||||
@@ -3023,6 +3052,13 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-layout-effect@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399"
|
||||
integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-rect@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz#b040cc88a4906b78696cd3a32b075ed5b1423b3e"
|
||||
|
||||
Reference in New Issue
Block a user