mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
Add keyboard navigation for users, groups, branches, tags, sources, changesets and plugins (#2153)
Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
committed by
GitHub
parent
da71004dd0
commit
eea60deadb
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import React, { ReactNode, useCallback } from "react";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { Link } from "react-router-dom";
|
||||
@@ -55,69 +55,72 @@ const InvisibleButton = styled.button`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const CardColumn: FC<Props> = ({
|
||||
link,
|
||||
avatar,
|
||||
title,
|
||||
description,
|
||||
contentRight,
|
||||
footerLeft,
|
||||
footerRight,
|
||||
action,
|
||||
className,
|
||||
}) => {
|
||||
const renderAvatar = avatar ? <figure className="media-left mt-3 ml-4">{avatar}</figure> : null;
|
||||
const renderDescription = description ? <p className="shorten-text">{description}</p> : null;
|
||||
const renderContentRight = contentRight ? <div className="ml-auto">{contentRight}</div> : null;
|
||||
const CardColumn = React.forwardRef<HTMLElement, Props>(
|
||||
({ link, avatar, title, description, contentRight, footerLeft, footerRight, action, className }, ref) => {
|
||||
const renderAvatar = avatar ? <figure className="media-left mt-3 ml-4">{avatar}</figure> : null;
|
||||
const renderDescription = description ? <p className="shorten-text">{description}</p> : null;
|
||||
const renderContentRight = contentRight ? <div className="ml-auto">{contentRight}</div> : null;
|
||||
const executeRef = useCallback(
|
||||
(el: HTMLButtonElement | HTMLAnchorElement | null) => {
|
||||
if (typeof ref === "function") {
|
||||
ref(el);
|
||||
} else if (ref) {
|
||||
ref.current = el;
|
||||
}
|
||||
},
|
||||
[ref]
|
||||
);
|
||||
|
||||
let createLink = null;
|
||||
if (link) {
|
||||
createLink = <Link className="overlay-column" to={link} />;
|
||||
} else if (action) {
|
||||
createLink = (
|
||||
<InvisibleButton
|
||||
className="overlay-column"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
action();
|
||||
}}
|
||||
tabIndex={0}
|
||||
/>
|
||||
let createLink = null;
|
||||
if (link) {
|
||||
createLink = <Link ref={executeRef} className="overlay-column" to={link} />;
|
||||
} else if (action) {
|
||||
createLink = (
|
||||
<InvisibleButton
|
||||
ref={executeRef}
|
||||
className="overlay-column"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
action();
|
||||
}}
|
||||
tabIndex={0}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{createLink}
|
||||
<NoEventWrapper className={classNames("media", className)}>
|
||||
{renderAvatar}
|
||||
<div
|
||||
className={classNames(
|
||||
"media-content",
|
||||
"text-box",
|
||||
"is-flex",
|
||||
"is-flex-direction-column",
|
||||
"is-justify-content-space-around",
|
||||
"is-align-self-stretch"
|
||||
)}
|
||||
>
|
||||
<div className="is-flex">
|
||||
<div className="is-clipped mb-0">
|
||||
<p className="shorten-text m-0">{title}</p>
|
||||
{renderDescription}
|
||||
</div>
|
||||
{renderContentRight}
|
||||
</div>
|
||||
<div className={classNames("level", "is-flex", "pb-4")}>
|
||||
<div className={classNames("level-left", "is-hidden-mobile", "mr-2")}>{footerLeft}</div>
|
||||
<InheritFlexShrinkDiv className="level-right is-block is-mobile m-0 shorten-text">
|
||||
{footerRight}
|
||||
</InheritFlexShrinkDiv>
|
||||
</div>
|
||||
</div>
|
||||
</NoEventWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{createLink}
|
||||
<NoEventWrapper className={classNames("media", className)}>
|
||||
{renderAvatar}
|
||||
<div
|
||||
className={classNames(
|
||||
"media-content",
|
||||
"text-box",
|
||||
"is-flex",
|
||||
"is-flex-direction-column",
|
||||
"is-justify-content-space-around",
|
||||
"is-align-self-stretch"
|
||||
)}
|
||||
>
|
||||
<div className="is-flex">
|
||||
<div className="is-clipped mb-0">
|
||||
<p className="shorten-text m-0">{title}</p>
|
||||
{renderDescription}
|
||||
</div>
|
||||
{renderContentRight}
|
||||
</div>
|
||||
<div className={classNames("level", "is-flex", "pb-4")}>
|
||||
<div className={classNames("level-left", "is-hidden-mobile", "mr-2")}>{footerLeft}</div>
|
||||
<InheritFlexShrinkDiv className="level-right is-block is-mobile m-0 shorten-text">
|
||||
{footerRight}
|
||||
</InheritFlexShrinkDiv>
|
||||
</div>
|
||||
</div>
|
||||
</NoEventWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
export default CardColumn;
|
||||
|
||||
@@ -41,13 +41,13 @@ class CardColumnGroup extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
collapsed: false
|
||||
collapsed: false,
|
||||
};
|
||||
}
|
||||
|
||||
toggleCollapse = () => {
|
||||
this.setState(prevState => ({
|
||||
collapsed: !prevState.collapsed
|
||||
this.setState((prevState) => ({
|
||||
collapsed: !prevState.collapsed,
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -75,7 +75,10 @@ class CardColumnGroup extends React.Component<Props, State> {
|
||||
const fullColumnWidth = this.isFullSize(elements, index);
|
||||
const sizeClass = fullColumnWidth ? "is-full" : "is-half";
|
||||
return (
|
||||
<div className={classNames("box", "box-link-shadow", "column", "is-clipped", sizeClass)} key={index}>
|
||||
<div
|
||||
className={classNames("box", "box-link-shadow", "column", "is-relative", "is-clipped", sizeClass)}
|
||||
key={index}
|
||||
>
|
||||
{entry}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -934,6 +934,24 @@ exports[`Storyshots Buttons/Button Loading 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Buttons/Button Ref Default 1`] = `
|
||||
<div
|
||||
className="indexstories__Spacing-sc-bpoict-0 jXUBTh"
|
||||
>
|
||||
<button
|
||||
className="button is-default"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
|
||||
|
||||
Focus me!
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Buttons/CreateButton Default 1`] = `
|
||||
<div
|
||||
className="indexstories__Spacing-sc-bpoict-0 jXUBTh"
|
||||
@@ -20096,7 +20114,7 @@ exports[`Storyshots Repositories/Changesets Co-Authors with avatar 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/b6c6f8fbd0d490936fae7d26ffdd4695cc2a0930"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20116,7 +20134,7 @@ exports[`Storyshots Repositories/Changesets Co-Authors with avatar 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/b6c6f8fbd0d490936fae7d26ffdd4695cc2a0930"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20253,7 +20271,7 @@ exports[`Storyshots Repositories/Changesets Commiter and Co-Authors with avatar
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/a88567ef1e9528a700555cad8c4576b72fc7c6dd"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20273,7 +20291,7 @@ exports[`Storyshots Repositories/Changesets Commiter and Co-Authors with avatar
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/a88567ef1e9528a700555cad8c4576b72fc7c6dd"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20368,7 +20386,7 @@ exports[`Storyshots Repositories/Changesets Default 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20388,7 +20406,371 @@ exports[`Storyshots Repositories/Changesets Default 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className="fas fa-fw fa-code has-text-inherit is-medium pr-5"
|
||||
onKeyPress={[Function]}
|
||||
/>
|
||||
|
||||
changeset.buttons.sources
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Repositories/Changesets List with navigation 1`] = `
|
||||
<div
|
||||
className="Changesetsstories__Wrapper-sc-122npan-0 bEvXuM box box-link-shadow"
|
||||
>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-sc-tkpti5-0 fyJvMU"
|
||||
>
|
||||
<div
|
||||
className="columns is-variable is-1-mobile is-0-tablet"
|
||||
>
|
||||
<div
|
||||
className="column is-three-fifths is-full-mobile"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless"
|
||||
>
|
||||
<div
|
||||
className="column is-four-fifths"
|
||||
>
|
||||
<div
|
||||
className="media"
|
||||
>
|
||||
<div
|
||||
className="SingleChangeset__FullWidthDiv-sc-ytpqp9-1 gydPEK media-right ml-0"
|
||||
>
|
||||
<h4
|
||||
className="has-text-weight-bold is-ellipsis-overflow"
|
||||
>
|
||||
|
||||
The starship Heart of Gold was the first spacecraft to make use of the Infinite Improbability Drive. The craft was stolen by then-President Zaphod Beeblebrox at the official launch of the ship, as he was supposed to be officiating the launch. Later, during the use of the Infinite Improbability Drive, the ship picked up Arthur Dent and Ford Prefect, who were floating unprotected in deep space in the same star sector, having just escaped the destruction of the same planet.
|
||||
|
||||
</h4>
|
||||
<p
|
||||
className="is-hidden-touch"
|
||||
/>
|
||||
<p
|
||||
className="is-hidden-desktop"
|
||||
/>
|
||||
<div
|
||||
className="is-flex"
|
||||
>
|
||||
<p
|
||||
className="is-size-7 is-ellipsis-overflow mt-2"
|
||||
>
|
||||
changeset.contributors.authoredBy
|
||||
|
||||
<a
|
||||
href="mailto:scm-admin@scm-manager.org"
|
||||
title="changeset.contributors.mailto scm-admin@scm-manager.org"
|
||||
>
|
||||
SCM Administrator
|
||||
</a>
|
||||
,
|
||||
changeset.contributors.committedBy
|
||||
|
||||
<a
|
||||
href="mailto:zaphod.beeblebrox@hitchhiker.cm"
|
||||
title="changeset.contributors.mailto zaphod.beeblebrox@hitchhiker.cm"
|
||||
>
|
||||
Zaphod Beeblebrox
|
||||
</a>
|
||||
|
||||
commaSeparatedList.lastDivider
|
||||
|
||||
changeset.contributors.coAuthoredBy
|
||||
|
||||
<a
|
||||
href="mailto:ford.prefect@hitchhiker.com"
|
||||
title="changeset.contributors.mailto ford.prefect@hitchhiker.com"
|
||||
>
|
||||
Ford Prefect
|
||||
</a>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="column is-align-self-center"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="column is-flex is-justify-content-flex-end is-align-items-center"
|
||||
>
|
||||
<div
|
||||
className="ButtonAddons__Flex-sc-182golj-0 dcwwZy field has-addons m-0"
|
||||
>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/a88567ef1e9528a700555cad8c4576b72fc7c6dd"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className="fas fa-fw fa-exchange-alt has-text-inherit is-medium pr-5"
|
||||
onKeyPress={[Function]}
|
||||
/>
|
||||
|
||||
changeset.buttons.details
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/a88567ef1e9528a700555cad8c4576b72fc7c6dd"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className="fas fa-fw fa-code has-text-inherit is-medium pr-5"
|
||||
onKeyPress={[Function]}
|
||||
/>
|
||||
|
||||
changeset.buttons.sources
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-sc-tkpti5-0 fyJvMU"
|
||||
>
|
||||
<div
|
||||
className="columns is-variable is-1-mobile is-0-tablet"
|
||||
>
|
||||
<div
|
||||
className="column is-three-fifths is-full-mobile"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless"
|
||||
>
|
||||
<div
|
||||
className="column is-four-fifths"
|
||||
>
|
||||
<div
|
||||
className="media"
|
||||
>
|
||||
<div
|
||||
className="SingleChangeset__FullWidthDiv-sc-ytpqp9-1 gydPEK media-right ml-0"
|
||||
>
|
||||
<h4
|
||||
className="has-text-weight-bold is-ellipsis-overflow"
|
||||
>
|
||||
|
||||
Change heading to "Heart Of Gold"
|
||||
|
||||
</h4>
|
||||
<p
|
||||
className="is-hidden-touch"
|
||||
/>
|
||||
<p
|
||||
className="is-hidden-desktop"
|
||||
/>
|
||||
<div
|
||||
className="is-flex"
|
||||
>
|
||||
<p
|
||||
className="is-size-7 is-ellipsis-overflow mt-2"
|
||||
>
|
||||
changeset.contributors.authoredBy
|
||||
|
||||
<a
|
||||
href="mailto:scm-admin@scm-manager.org"
|
||||
title="changeset.contributors.mailto scm-admin@scm-manager.org"
|
||||
>
|
||||
SCM Administrator
|
||||
</a>
|
||||
|
||||
commaSeparatedList.lastDivider
|
||||
|
||||
changeset.contributors.committedBy
|
||||
|
||||
<a
|
||||
href="mailto:zaphod.beeblebrox@hitchhiker.cm"
|
||||
title="changeset.contributors.mailto zaphod.beeblebrox@hitchhiker.cm"
|
||||
>
|
||||
Zaphod Beeblebrox
|
||||
</a>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="column is-align-self-center"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="column is-flex is-justify-content-flex-end is-align-items-center"
|
||||
>
|
||||
<div
|
||||
className="ButtonAddons__Flex-sc-182golj-0 dcwwZy field has-addons m-0"
|
||||
>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/d21cc6c359270aef2196796f4d96af65f51866dc"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className="fas fa-fw fa-exchange-alt has-text-inherit is-medium pr-5"
|
||||
onKeyPress={[Function]}
|
||||
/>
|
||||
|
||||
changeset.buttons.details
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/d21cc6c359270aef2196796f4d96af65f51866dc"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className="fas fa-fw fa-code has-text-inherit is-medium pr-5"
|
||||
onKeyPress={[Function]}
|
||||
/>
|
||||
|
||||
changeset.buttons.sources
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="ChangesetRow__Wrapper-sc-tkpti5-0 fyJvMU"
|
||||
>
|
||||
<div
|
||||
className="columns is-variable is-1-mobile is-0-tablet"
|
||||
>
|
||||
<div
|
||||
className="column is-three-fifths is-full-mobile"
|
||||
>
|
||||
<div
|
||||
className="columns is-gapless"
|
||||
>
|
||||
<div
|
||||
className="column is-four-fifths"
|
||||
>
|
||||
<div
|
||||
className="media"
|
||||
>
|
||||
<div
|
||||
className="SingleChangeset__FullWidthDiv-sc-ytpqp9-1 gydPEK media-right ml-0"
|
||||
>
|
||||
<h4
|
||||
className="has-text-weight-bold is-ellipsis-overflow"
|
||||
>
|
||||
|
||||
initialize repository
|
||||
|
||||
</h4>
|
||||
<p
|
||||
className="is-hidden-touch"
|
||||
/>
|
||||
<p
|
||||
className="is-hidden-desktop"
|
||||
/>
|
||||
<div
|
||||
className="is-flex"
|
||||
>
|
||||
<p
|
||||
className="is-size-7 is-ellipsis-overflow mt-2"
|
||||
>
|
||||
changeset.contributors.authoredBy
|
||||
|
||||
<a
|
||||
href="mailto:scm-admin@scm-manager.org"
|
||||
title="changeset.contributors.mailto scm-admin@scm-manager.org"
|
||||
>
|
||||
SCM Administrator
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="column is-align-self-center"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="column is-flex is-justify-content-flex-end is-align-items-center"
|
||||
>
|
||||
<div
|
||||
className="ButtonAddons__Flex-sc-182golj-0 dcwwZy field has-addons m-0"
|
||||
>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className="fas fa-fw fa-exchange-alt has-text-inherit is-medium pr-5"
|
||||
onKeyPress={[Function]}
|
||||
/>
|
||||
|
||||
changeset.buttons.details
|
||||
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<p
|
||||
className="control"
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20493,7 +20875,7 @@ exports[`Storyshots Repositories/Changesets Replacements 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/d21cc6c359270aef2196796f4d96af65f51866dc"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20513,7 +20895,7 @@ exports[`Storyshots Repositories/Changesets Replacements 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/d21cc6c359270aef2196796f4d96af65f51866dc"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20620,7 +21002,7 @@ exports[`Storyshots Repositories/Changesets With Committer 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/d21cc6c359270aef2196796f4d96af65f51866dc"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20640,7 +21022,7 @@ exports[`Storyshots Repositories/Changesets With Committer 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/d21cc6c359270aef2196796f4d96af65f51866dc"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20756,7 +21138,7 @@ exports[`Storyshots Repositories/Changesets With Committer and Co-Author 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/a88567ef1e9528a700555cad8c4576b72fc7c6dd"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20776,7 +21158,7 @@ exports[`Storyshots Repositories/Changesets With Committer and Co-Author 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/a88567ef1e9528a700555cad8c4576b72fc7c6dd"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20884,7 +21266,7 @@ exports[`Storyshots Repositories/Changesets With avatar 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -20904,7 +21286,7 @@ exports[`Storyshots Repositories/Changesets With avatar 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21008,7 +21390,7 @@ exports[`Storyshots Repositories/Changesets With contactless signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21028,7 +21410,7 @@ exports[`Storyshots Repositories/Changesets With contactless signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21132,7 +21514,7 @@ exports[`Storyshots Repositories/Changesets With invalid signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21152,7 +21534,7 @@ exports[`Storyshots Repositories/Changesets With invalid signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21260,7 +21642,7 @@ exports[`Storyshots Repositories/Changesets With multiple Co-Authors 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/b6c6f8fbd0d490936fae7d26ffdd4695cc2a0930"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21280,7 +21662,7 @@ exports[`Storyshots Repositories/Changesets With multiple Co-Authors 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/b6c6f8fbd0d490936fae7d26ffdd4695cc2a0930"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21384,7 +21766,7 @@ exports[`Storyshots Repositories/Changesets With multiple signatures and invalid
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21404,7 +21786,7 @@ exports[`Storyshots Repositories/Changesets With multiple signatures and invalid
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21508,7 +21890,7 @@ exports[`Storyshots Repositories/Changesets With multiple signatures and not fou
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21528,7 +21910,7 @@ exports[`Storyshots Repositories/Changesets With multiple signatures and not fou
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21632,7 +22014,7 @@ exports[`Storyshots Repositories/Changesets With multiple signatures and valid s
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21652,7 +22034,7 @@ exports[`Storyshots Repositories/Changesets With multiple signatures and valid s
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21756,7 +22138,7 @@ exports[`Storyshots Repositories/Changesets With unknown signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21776,7 +22158,7 @@ exports[`Storyshots Repositories/Changesets With unknown signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21880,7 +22262,7 @@ exports[`Storyshots Repositories/Changesets With unowned signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -21900,7 +22282,7 @@ exports[`Storyshots Repositories/Changesets With unowned signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -22004,7 +22386,7 @@ exports[`Storyshots Repositories/Changesets With valid signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.details"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/changeset/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -22024,7 +22406,7 @@ exports[`Storyshots Repositories/Changesets With valid signature 1`] = `
|
||||
>
|
||||
<a
|
||||
aria-label="changeset.buttons.sources"
|
||||
className="button is-default is-reduced-mobile ChangesetButtonGroup__SwitcherButton-sc-19ag5s8-0 GkUQw"
|
||||
className="button is-default is-reduced-mobile px-3"
|
||||
href="/repo/hitchhiker/heartOfGold/code/sources/e163c8f632db571c9aa51a8eb440e37cf550b825"
|
||||
onClick={[Function]}
|
||||
>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC, MouseEvent, ReactNode, KeyboardEvent } from "react";
|
||||
import React, { KeyboardEvent, MouseEvent, ReactNode, useCallback } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
import Icon from "../Icon";
|
||||
@@ -40,7 +40,6 @@ export type ButtonProps = {
|
||||
reducedMobile?: boolean;
|
||||
children?: ReactNode;
|
||||
testId?: string;
|
||||
ref?: React.ForwardedRef<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
type Props = ButtonProps & {
|
||||
@@ -48,85 +47,95 @@ type Props = ButtonProps & {
|
||||
color?: string;
|
||||
};
|
||||
|
||||
type InnerProps = Props & {
|
||||
innerRef: React.Ref<HTMLButtonElement>;
|
||||
};
|
||||
|
||||
const Button: FC<InnerProps> = ({
|
||||
link,
|
||||
className,
|
||||
icon,
|
||||
fullWidth,
|
||||
reducedMobile,
|
||||
testId,
|
||||
children,
|
||||
label,
|
||||
type = "button",
|
||||
title,
|
||||
loading,
|
||||
disabled,
|
||||
action,
|
||||
color = "default",
|
||||
innerRef
|
||||
}) => {
|
||||
const renderIcon = () => {
|
||||
return (
|
||||
<>
|
||||
{icon ? (
|
||||
<Icon name={icon} color="inherit" className={classNames("is-medium", { "pr-5": label || children })} />
|
||||
) : null}
|
||||
</>
|
||||
const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, Props>(
|
||||
(
|
||||
{
|
||||
link,
|
||||
className,
|
||||
icon,
|
||||
fullWidth,
|
||||
reducedMobile,
|
||||
testId,
|
||||
children,
|
||||
label,
|
||||
type = "button",
|
||||
title,
|
||||
loading,
|
||||
disabled,
|
||||
action,
|
||||
color = "default",
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const executeRef = useCallback(
|
||||
(el: HTMLButtonElement | HTMLAnchorElement | null) => {
|
||||
if (typeof ref === "function") {
|
||||
ref(el);
|
||||
} else if (ref) {
|
||||
ref.current = el;
|
||||
}
|
||||
},
|
||||
[ref]
|
||||
);
|
||||
};
|
||||
|
||||
const classes = classNames(
|
||||
"button",
|
||||
"is-" + color,
|
||||
{ "is-loading": loading },
|
||||
{ "is-fullwidth": fullWidth },
|
||||
{ "is-reduced-mobile": reducedMobile },
|
||||
className
|
||||
);
|
||||
|
||||
const content = (
|
||||
<span>
|
||||
{renderIcon()}{" "}
|
||||
{(label || children) && (
|
||||
<>
|
||||
{label} {children}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
if (link && !disabled) {
|
||||
if (link.includes("://")) {
|
||||
const renderIcon = () => {
|
||||
return (
|
||||
<a className={classes} href={link} aria-label={label}>
|
||||
<>
|
||||
{icon ? (
|
||||
<Icon name={icon} color="inherit" className={classNames("is-medium", { "pr-5": label || children })} />
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const classes = classNames(
|
||||
"button",
|
||||
"is-" + color,
|
||||
{ "is-loading": loading },
|
||||
{ "is-fullwidth": fullWidth },
|
||||
{ "is-reduced-mobile": reducedMobile },
|
||||
className
|
||||
);
|
||||
|
||||
const content = (
|
||||
<span>
|
||||
{renderIcon()}{" "}
|
||||
{(label || children) && (
|
||||
<>
|
||||
{label} {children}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
if (link && !disabled) {
|
||||
if (link.includes("://")) {
|
||||
return (
|
||||
<a ref={executeRef} className={classes} href={link} aria-label={label}>
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Link ref={executeRef} className={classes} to={link} aria-label={label}>
|
||||
{content}
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link className={classes} to={link} aria-label={label}>
|
||||
<button
|
||||
type={type}
|
||||
title={title}
|
||||
disabled={disabled}
|
||||
onClick={(event) => action && action(event)}
|
||||
className={classes}
|
||||
ref={executeRef}
|
||||
{...createAttributesForTesting(testId)}
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
title={title}
|
||||
disabled={disabled}
|
||||
onClick={event => action && action(event)}
|
||||
className={classes}
|
||||
ref={innerRef}
|
||||
{...createAttributesForTesting(testId)}
|
||||
>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.forwardRef<HTMLButtonElement, Props>((props, ref) => <Button {...props} innerRef={ref} />);
|
||||
export default Button;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { ReactElement, ReactNode } from "react";
|
||||
import React, { ReactElement, ReactNode, useEffect, useState } from "react";
|
||||
import Button from "./Button";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import styled from "styled-components";
|
||||
@@ -85,3 +85,8 @@ buttonStory("DownloadButton", () => <DownloadButton displayName="Download" disab
|
||||
);
|
||||
buttonStory("EditButton", () => <EditButton>Edit</EditButton>);
|
||||
buttonStory("SubmitButton", () => <SubmitButton>Submit</SubmitButton>);
|
||||
buttonStory("Button Ref", () => {
|
||||
const [ref, setRef] = useState<HTMLButtonElement | HTMLAnchorElement | null>();
|
||||
useEffect(() => ref?.focus(), [ref]);
|
||||
return <Button ref={setRef}>Focus me!</Button>;
|
||||
});
|
||||
|
||||
@@ -37,16 +37,22 @@ export default function useRegisterModal(active: boolean, initialValue: boolean
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
previousActiveState.current = true;
|
||||
increment();
|
||||
if (increment) {
|
||||
increment();
|
||||
}
|
||||
} else {
|
||||
if (previousActiveState.current !== null) {
|
||||
decrement();
|
||||
if (decrement) {
|
||||
decrement();
|
||||
}
|
||||
}
|
||||
previousActiveState.current = false;
|
||||
}
|
||||
return () => {
|
||||
if (previousActiveState.current) {
|
||||
decrement();
|
||||
if (decrement) {
|
||||
decrement();
|
||||
}
|
||||
previousActiveState.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,12 +21,11 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import React from "react";
|
||||
import { Changeset, File, Repository } from "@scm-manager/ui-types";
|
||||
import { Button, ButtonAddons } from "../../buttons";
|
||||
import { createChangesetLink, createSourcesLink } from "./changesets";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
@@ -34,26 +33,31 @@ type Props = {
|
||||
file?: File;
|
||||
};
|
||||
|
||||
const SwitcherButton = styled(Button)`
|
||||
padding-right: 0.75rem;
|
||||
padding-left: 0.75rem;
|
||||
`;
|
||||
|
||||
const ChangesetButtonGroup: FC<Props> = ({ repository, changeset, file }) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const changesetLink = createChangesetLink(repository, changeset);
|
||||
const sourcesLink = createSourcesLink(repository, changeset, file);
|
||||
return (
|
||||
<ButtonAddons className="m-0">
|
||||
<SwitcherButton
|
||||
link={changesetLink}
|
||||
icon="exchange-alt"
|
||||
label={t("changeset.buttons.details")}
|
||||
reducedMobile={true}
|
||||
/>
|
||||
<SwitcherButton link={sourcesLink} icon="code" label={t("changeset.buttons.sources")} reducedMobile={true} />
|
||||
</ButtonAddons>
|
||||
);
|
||||
};
|
||||
const ChangesetButtonGroup = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, Props>(
|
||||
({ repository, changeset, file }, ref) => {
|
||||
const [t] = useTranslation("repos");
|
||||
const changesetLink = createChangesetLink(repository, changeset);
|
||||
const sourcesLink = createSourcesLink(repository, changeset, file);
|
||||
return (
|
||||
<ButtonAddons className="m-0">
|
||||
<Button
|
||||
className="px-3"
|
||||
ref={ref}
|
||||
link={changesetLink}
|
||||
icon="exchange-alt"
|
||||
label={t("changeset.buttons.details")}
|
||||
reducedMobile={true}
|
||||
/>
|
||||
<Button
|
||||
className="px-3"
|
||||
link={sourcesLink}
|
||||
icon="code"
|
||||
label={t("changeset.buttons.sources")}
|
||||
reducedMobile={true}
|
||||
/>
|
||||
</ButtonAddons>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default ChangesetButtonGroup;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import ChangesetRow from "./ChangesetRow";
|
||||
import React, { FC } from "react";
|
||||
import { Changeset, File, Repository } from "@scm-manager/ui-types";
|
||||
import { KeyboardIterator } from "@scm-manager/ui-shortcuts";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
@@ -32,10 +33,13 @@ type Props = {
|
||||
};
|
||||
|
||||
const ChangesetList: FC<Props> = ({ repository, changesets, file }) => {
|
||||
const content = changesets.map(changeset => {
|
||||
return <ChangesetRow key={changeset.id} repository={repository} changeset={changeset} file={file} />;
|
||||
});
|
||||
return <>{content}</>;
|
||||
return (
|
||||
<KeyboardIterator>
|
||||
{changesets.map((changeset) => {
|
||||
return <ChangesetRow key={changeset.id} repository={repository} changeset={changeset} file={file} />;
|
||||
})}
|
||||
</KeyboardIterator>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangesetList;
|
||||
|
||||
@@ -28,6 +28,7 @@ import { ExtensionPoint, extensionPoints } from "@scm-manager/ui-extensions";
|
||||
import { Changeset, File, Repository } from "@scm-manager/ui-types";
|
||||
import ChangesetButtonGroup from "./ChangesetButtonGroup";
|
||||
import SingleChangeset from "./SingleChangeset";
|
||||
import { useKeyboardIteratorTarget } from "@scm-manager/ui-shortcuts";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
@@ -46,6 +47,7 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const ChangesetRow: FC<Props> = ({ repository, changeset, file }) => {
|
||||
const ref = useKeyboardIteratorTarget();
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className={classNames("columns", "is-variable", "is-1-mobile", "is-0-tablet")}>
|
||||
@@ -53,12 +55,12 @@ const ChangesetRow: FC<Props> = ({ repository, changeset, file }) => {
|
||||
<SingleChangeset repository={repository} changeset={changeset} />
|
||||
</div>
|
||||
<div className={classNames("column", "is-flex", "is-justify-content-flex-end", "is-align-items-center")}>
|
||||
<ChangesetButtonGroup repository={repository} changeset={changeset} file={file} />
|
||||
<ChangesetButtonGroup ref={ref} repository={repository} changeset={changeset} file={file} />
|
||||
<ExtensionPoint<extensionPoints.ChangesetRight>
|
||||
name="changeset.right"
|
||||
props={{
|
||||
repository,
|
||||
changeset
|
||||
changeset,
|
||||
}}
|
||||
renderAll={true}
|
||||
/>
|
||||
|
||||
@@ -28,13 +28,15 @@ import styled from "styled-components";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import repository from "../../__resources__/repository";
|
||||
import ChangesetRow from "./ChangesetRow";
|
||||
import { one, two, three, four, five } from "../../__resources__/changesets";
|
||||
import { five, four, one, three, two } from "../../__resources__/changesets";
|
||||
import { Binder, BinderContext } from "@scm-manager/ui-extensions";
|
||||
// @ts-ignore
|
||||
import hitchhiker from "../../__resources__/hitchhiker.png";
|
||||
import { Person } from "../../avatar/Avatar";
|
||||
import { Changeset } from "@scm-manager/ui-types";
|
||||
import { Replacement } from "@scm-manager/ui-text";
|
||||
import ChangesetList from "./ChangesetList";
|
||||
import { ShortcutDocsContextProvider } from "@scm-manager/ui-shortcuts";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 25rem 4rem;
|
||||
@@ -287,4 +289,9 @@ storiesOf("Repositories/Changesets", module)
|
||||
},
|
||||
];
|
||||
return <ChangesetRow repository={repository} changeset={changeset} />;
|
||||
});
|
||||
})
|
||||
.add("List with navigation", () => (
|
||||
<ShortcutDocsContextProvider>
|
||||
<ChangesetList repository={repository} changesets={[copy(one), copy(two), copy(three)]} />
|
||||
</ShortcutDocsContextProvider>
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user