diff --git a/scm-ui/ui-components/src/Annotate.stories.tsx b/scm-ui/ui-components/src/Annotate.stories.tsx index 582c7545d0..d572a52bcd 100644 --- a/scm-ui/ui-components/src/Annotate.stories.tsx +++ b/scm-ui/ui-components/src/Annotate.stories.tsx @@ -26,6 +26,8 @@ import { storiesOf } from "@storybook/react"; import * as React from "react"; import styled from "styled-components"; import Annotate, { AnnotatedSource } from "./Annotate"; +import { MemoryRouter } from "react-router-dom"; +import repository from "./__resources__/repository"; const Wrapper = styled.div` margin: 2rem; @@ -108,5 +110,6 @@ const source: AnnotatedSource = { }; storiesOf("Annotate", module) - .addDecorator(storyFn => {storyFn()}) - .add("Default", () => ); + .addDecorator(storyFn => {storyFn()}) + .addDecorator(storyFn => {storyFn()}) + .add("Default", () => ); diff --git a/scm-ui/ui-components/src/Annotate.tsx b/scm-ui/ui-components/src/Annotate.tsx index e0e1768611..7ea1a85b43 100644 --- a/scm-ui/ui-components/src/Annotate.tsx +++ b/scm-ui/ui-components/src/Annotate.tsx @@ -23,7 +23,7 @@ */ import React, { FC } from "react"; -import { Person } from "@scm-manager/ui-types"; +import { Person, Repository } from "@scm-manager/ui-types"; // @ts-ignore import { LightAsync as ReactSyntaxHighlighter, createElement } from "react-syntax-highlighter"; @@ -32,6 +32,9 @@ import { LightAsync as ReactSyntaxHighlighter, createElement } from "react-synta import { arduinoLight } from "react-syntax-highlighter/dist/cjs/styles/hljs"; import styled from "styled-components"; import DateShort from "./DateShort"; +import { SingleContributor } from "./repos/changesets"; +import DateFromNow from "./DateFromNow"; +import { Link } from "react-router-dom"; // TODO move types to ui-types @@ -51,13 +54,14 @@ export type AnnotatedLine = { type Props = { source: AnnotatedSource; + repository: Repository; }; type LineElementProps = { newAnnotation: boolean; }; -const Author = styled.a` +const Author = styled.span` width: 8em; overflow: hidden; text-overflow: ellipsis; @@ -93,7 +97,72 @@ const LineNumber = styled.span` padding: 0 0.5em; `; -const Annotate: FC = ({ source }) => { +const Popover = styled.div` + position: absolute; + left: -16.5em; + bottom: 0.1em; + + z-index: 100; + visibility: hidden; + overflow: visible; + + width: 35em; + + &:before { + position: absolute; + content: ""; + border-style: solid; + pointer-events: none; + height: 0; + width: 0; + top: 100%; + /*left: 50%;*/ + border-color: transparent; + border-bottom-color: white; + border-left-color: white; + border-width: 0.4rem; + margin-left: -0.4rem; + margin-top: -0.4rem; + -webkit-transform-origin: center; + transform-origin: center; + box-shadow: -1px 1px 2px rgba(10, 10, 10, 0.2); + transform: rotate(-45deg); + } +`; + +const Line = styled.span` + position: relative; + z-index: 10; + + &:hover .changeset-details { + visibility: visible !important; + } +`; + +const PreTag = styled.pre` + overflow-x: visible !important; +`; + +const SmallHr = styled.hr` + margin: 0.5em 0; +`; + +const PopoverHeading = styled.div` + height: 1.5em; +`; + +const PopoverDescription = styled.p` + margin-top: 0.5em; +`; + +const shortRevision = (revision: string) => { + if (revision.length > 7) { + return revision.substring(0, 7); + } + return revision; +}; + +const Annotate: FC = ({ source, repository }) => { // @ts-ignore const defaultRenderer = ({ rows, stylesheet, useInlineStyles }) => { let lastRevision = ""; @@ -111,13 +180,26 @@ const Annotate: FC = ({ source }) => { const newAnnotation = annotation.revision !== lastRevision; lastRevision = annotation.revision; return ( - - {annotation.author.name}{" "} + + + + + + + +

Changeset {shortRevision(annotation.revision)}

+ {annotation.description} +
+ + + {annotation.author.name} + + {" "} {" "} {i + 1} {line} -
+ ); } @@ -136,6 +218,7 @@ const Annotate: FC = ({ source }) => { language={source.language} style={arduinoLight} renderer={defaultRenderer} + PreTag={PreTag} > {code} diff --git a/scm-ui/ui-components/src/DateFromNow.tsx b/scm-ui/ui-components/src/DateFromNow.tsx index eda6efdcc7..b01cfcdc7b 100644 --- a/scm-ui/ui-components/src/DateFromNow.tsx +++ b/scm-ui/ui-components/src/DateFromNow.tsx @@ -49,6 +49,7 @@ type Props = WithTranslation & { * ci server. */ baseDate?: DateInput; + className?: string; }; type Options = { @@ -100,13 +101,17 @@ class DateFromNow extends React.Component { }; render() { - const { date } = this.props; + const { date, className } = this.props; if (date) { const isoDate = toDate(date); const options = this.createOptions(); const distance = formatDistance(isoDate, this.getBaseDate(), options); const formatted = format(isoDate, FullDateFormat, options); - return {distance}; + return ( + + {distance} + + ); } return null; } diff --git a/scm-ui/ui-components/src/repos/changesets/ChangesetAuthor.tsx b/scm-ui/ui-components/src/repos/changesets/ChangesetAuthor.tsx index 017d468888..2af17643bc 100644 --- a/scm-ui/ui-components/src/repos/changesets/ChangesetAuthor.tsx +++ b/scm-ui/ui-components/src/repos/changesets/ChangesetAuthor.tsx @@ -36,6 +36,7 @@ type Props = { type PersonProps = { person: Person; + className?: string; displayTextOnly?: boolean; }; @@ -70,7 +71,7 @@ const ContributorWithAvatar: FC = ({ person, avatar }) => { return ; }; -const SingleContributor: FC = ({ person, displayTextOnly }) => { +export const SingleContributor: FC = ({ person, className, displayTextOnly }) => { const [t] = useTranslation("repos"); const avatar = useAvatar(person); if (!displayTextOnly && avatar) { @@ -78,12 +79,16 @@ const SingleContributor: FC = ({ person, displayTextOnly }) => { } if (person.mail) { return ( - + {person.name} ); } - return <>{person.name}; + return {person.name}; }; type PersonsProps = { diff --git a/scm-ui/ui-components/src/repos/changesets/index.ts b/scm-ui/ui-components/src/repos/changesets/index.ts index 68e483cde8..b4c83dd4c3 100644 --- a/scm-ui/ui-components/src/repos/changesets/index.ts +++ b/scm-ui/ui-components/src/repos/changesets/index.ts @@ -25,7 +25,7 @@ import * as changesets from "./changesets"; export { changesets }; -export { default as ChangesetAuthor } from "./ChangesetAuthor"; +export { default as ChangesetAuthor, SingleContributor } from "./ChangesetAuthor"; export { default as ChangesetButtonGroup } from "./ChangesetButtonGroup"; export { default as ChangesetDiff } from "./ChangesetDiff"; export { default as ChangesetId } from "./ChangesetId";