Skip to content

Commit

Permalink
🏫 Support for author affiliations (#215)
Browse files Browse the repository at this point in the history
* 🏫 Support for author affiliations
* πŸš΄β€β™€οΈ Refactor Root --> GenericParent
* 🏫 Affiliations in BookTheme
  • Loading branch information
rowanc1 authored Aug 11, 2023
1 parent 56430d7 commit 1948a7a
Show file tree
Hide file tree
Showing 19 changed files with 1,203 additions and 1,157 deletions.
6 changes: 6 additions & 0 deletions .changeset/eight-islands-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@myst-theme/jupyter': patch
'@myst-theme/common': patch
---

Move to GenericParent instead of Root
8 changes: 8 additions & 0 deletions .changeset/five-zebras-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@myst-theme/frontmatter': patch
'myst-demo': patch
'@myst-theme/providers': patch
'@myst-theme/jupyter': patch
---

Package updates for myst-frontmatter
5 changes: 5 additions & 0 deletions .changeset/silent-trees-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@myst-theme/frontmatter': patch
---

Support affiliation and author information popovers
2,044 changes: 950 additions & 1,094 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions packages/common/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Root } from 'mdast';
import type { Dependency, SourceFileKind } from 'myst-spec-ext';
import type { References } from 'myst-common';
import type { GenericParent, References } from 'myst-common';
import type { SiteManifest } from 'myst-config';
import type { PageFrontmatter } from 'myst-frontmatter';

Expand Down Expand Up @@ -53,7 +52,7 @@ export type PageLoader = {
domain: string; // This is written in at render time in the site
project: string; // This is written in at render time in the site
frontmatter: PageFrontmatter;
mdast: Root;
mdast: GenericParent;
references: References;
footer?: FooterLinks;
// This may not be defined
Expand Down
1 change: 1 addition & 0 deletions packages/frontmatter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"dependencies": {
"@headlessui/react": "^1.7.15",
"@heroicons/react": "^2.0.18",
"@radix-ui/react-popover": "^1.0.6",
"@scienceicons/react": "^0.0.5",
"classnames": "^2.3.2",
"myst-common": "*",
Expand Down
35 changes: 35 additions & 0 deletions packages/frontmatter/src/Affiliations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { RorIcon } from '@scienceicons/react/24/solid';
import type { PageFrontmatter } from 'myst-frontmatter';

type Affiliations = Required<PageFrontmatter>['affiliations'];
type Affiliation = Affiliations[0];

export function Affiliation({
affiliations,
affiliationId,
}: {
affiliationId: string;
affiliations?: Affiliations;
}) {
if (!affiliations || affiliations.length === 0) return null;
const affiliationsLookup = Object.fromEntries(
affiliations?.map(({ id, ...rest }: any) => [id, rest]) ?? [],
);
const affiliation = affiliationsLookup[affiliationId] ?? { name: affiliationId };
return (
<>
{affiliation.name || affiliation.institution}{' '}
{affiliation.ror && (
<a
className="ml-1"
href={`https://ror.org/${affiliation.ror.replace(/(https?:\/\/)?ror\.org\//, '')}`}
target="_blank"
rel="noopener noreferrer"
title="Research Organization Registry"
>
<RorIcon width="1rem" height="1rem" className="inline-block" />
</a>
)}
</>
);
}
130 changes: 130 additions & 0 deletions packages/frontmatter/src/AuthorPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React from 'react';
import * as Popover from '@radix-ui/react-popover';
import type { PageFrontmatter } from 'myst-frontmatter';
import { Affiliation } from './Affiliations';

type Author = Required<PageFrontmatter>['authors'][0];
type Affiliations = Required<PageFrontmatter>['affiliations'];

function Definition({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt className="text-sm font-medium leading-6 text-gray-900">{title}</dt>
<dd className="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{children}</dd>
</div>
);
}

export const AuthorPopover = ({
author,
affiliations,
children,
}: {
author?: Author;
affiliations?: Affiliations;
children: React.ReactNode;
}) => {
if (!author) return <>{children}</>;
return (
<Popover.Root>
<Popover.Trigger asChild>
<button
className="focus:shadow-[0_0_0_2px] focus:shadow-black outline-none hover:underline"
aria-label="Update dimensions"
>
{children}
</button>
</Popover.Trigger>
<Popover.Portal>
<Popover.Content
className="rounded p-5 w-[400px] bg-white shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)] focus:shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2),0_0_0_2px_theme(colors.violet7)] will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
sideOffset={5}
>
<div className="flex flex-col gap-2.5">
<p className="text-mauve12 text-[15px] leading-[19px] font-medium mb-2.5">
{author.name}
</p>
<p className="text-mauve12 text-[15px] leading-[19px] font-medium mb-2.5">
{author.affiliations?.map((affiliationId) => (
<Affiliation
key={affiliationId}
affiliations={affiliations}
affiliationId={affiliationId}
/>
))}
</p>
<dl className="divide-y divide-gray-100">
{author.email && (
<Definition title="Email">
<a
className="ml-1"
href={`mailto:${author.email}`}
title={`${author.name} <${author.email}>`}
target="_blank"
rel="noopener noreferrer"
>
{author.email}
</a>
</Definition>
)}
{author.orcid && (
<Definition title="ORCID">
<a
className="ml-1"
href={`https://orcid.org/${author.orcid}`}
target="_blank"
rel="noopener noreferrer"
title="ORCID (Open Researcher and Contributor ID)"
>
{author.orcid}
</a>
</Definition>
)}
{author.github && (
<Definition title="GitHub">
<a
className="ml-1"
href={`https://github.com/${author.github}`}
target="_blank"
rel="noopener noreferrer"
title={`GitHub: ${author.github}`}
>
@{author.github}
</a>
</Definition>
)}
{author.twitter && (
<Definition title="Twitter">
<a
className="ml-1"
href={`https://twitter.com/${author.twitter}`}
target="_blank"
rel="noopener noreferrer"
title={`Twitter: ${author.twitter}`}
>
@{author.twitter}
</a>
</Definition>
)}
{author.url && (
<Definition title="Website">
<a
className="ml-1"
href={author.url}
target="_blank"
rel="noopener noreferrer"
title={`Author Website`}
>
{author.url}
</a>
</Definition>
)}
{author.roles && <Definition title="Roles">{author.roles.join(', ')}</Definition>}
</dl>
</div>
<Popover.Arrow className="fill-white" />
</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
};
62 changes: 31 additions & 31 deletions packages/frontmatter/src/Authors.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import React from 'react';
import classNames from 'classnames';
import type { PageFrontmatter } from 'myst-frontmatter';
import { OrcidIcon, EmailIcon, RorIcon, TwitterIcon } from '@scienceicons/react/24/solid';
import { OrcidIcon, EmailIcon, TwitterIcon } from '@scienceicons/react/24/solid';
import { AuthorPopover } from './AuthorPopover';
import { Affiliation } from './Affiliations';

export function Author({
author,
affiliations,
className,
}: {
author: Required<PageFrontmatter>['authors'][0];
affiliations: PageFrontmatter['affiliations'];
className?: string;
}) {
return (
<span className={classNames('font-semibold text-sm', className)}>
{author.name}
<AuthorPopover author={author} affiliations={affiliations}>
{author.name}
</AuthorPopover>
{author.email && author.corresponding && (
<a
className="ml-1"
Expand Down Expand Up @@ -62,14 +68,21 @@ export function Author({
);
}

export function AuthorsList({ authors }: { authors: PageFrontmatter['authors'] }) {
export function AuthorsList({
authors,
affiliations,
}: {
authors: PageFrontmatter['authors'];
affiliations: PageFrontmatter['affiliations'];
}) {
if (!authors || authors.length === 0) return null;
return (
<div>
{authors.map((a, i) => (
<Author
key={a.name}
author={a}
affiliations={affiliations}
className={classNames('inline-block', {
'text-comma': i < authors.length - 1,
})}
Expand All @@ -79,7 +92,13 @@ export function AuthorsList({ authors }: { authors: PageFrontmatter['authors'] }
);
}

export function AuthorAndAffiliations({ authors }: { authors: PageFrontmatter['authors'] }) {
export function AuthorAndAffiliations({
authors,
affiliations,
}: {
authors: PageFrontmatter['authors'];
affiliations: PageFrontmatter['affiliations'];
}) {
if (!authors || authors.length === 0) return null;
const hasAffliations = authors.reduce(
(r, { affiliations: a }) => r || (!!a && a?.length > 0),
Expand All @@ -88,7 +107,7 @@ export function AuthorAndAffiliations({ authors }: { authors: PageFrontmatter['a
if (!hasAffliations) {
return (
<header className="mt-4 not-prose">
<AuthorsList authors={authors} />
<AuthorsList authors={authors} affiliations={affiliations} />
</header>
);
}
Expand All @@ -104,34 +123,15 @@ export function AuthorAndAffiliations({ authors }: { authors: PageFrontmatter['a
{authors.map((author) => (
<React.Fragment key={author.name}>
<div>
<Author author={author} />
<Author author={author} affiliations={affiliations} />
</div>
<div className="text-sm">
{author.affiliations?.map((affil, i) => {
if (typeof affil === 'string') {
return <div key={i}>{affil}</div>;
}
const { name, ror } = affil as unknown as {
name: string;
ror?: string;
};
if (ror) {
return (
<div key={i}>
{name}
<a
href={`https://ror.org/${ror}`}
target="_blank"
rel="noopener noreferrer"
title="ROR (Research Organization Registry)"
className="text-inherit hover:text-inherit"
>
<RorIcon className="ml-2 inline-block h-[2em] w-[2em] grayscale hover:grayscale-0 -translate-y-[1px]" />
</a>
</div>
);
}
return <div key={i}>{name}</div>;
{author.affiliations?.map((affiliationId) => {
return (
<div key={affiliationId}>
<Affiliation affiliations={affiliations} affiliationId={affiliationId} />
</div>
);
})}
</div>
</React.Fragment>
Expand Down
9 changes: 7 additions & 2 deletions packages/frontmatter/src/FrontmatterBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,14 @@ export function FrontmatterBlock({
)}
{title && <h1 className="mb-0">{title}</h1>}
{subtitle && <p className="mt-2 mb-0 lead text-zinc-600 dark:text-zinc-400">{subtitle}</p>}
{hasAuthors && authorStyle === 'list' && <AuthorsList authors={frontmatter.authors} />}
{hasAuthors && authorStyle === 'list' && (
<AuthorsList authors={frontmatter.authors} affiliations={frontmatter.affiliations} />
)}
{hasAuthors && authorStyle === 'block' && (
<AuthorAndAffiliations authors={frontmatter.authors} />
<AuthorAndAffiliations
authors={frontmatter.authors}
affiliations={frontmatter.affiliations}
/>
)}
{hasDateOrDoi && (
<div className="flex mt-2 text-sm font-light">
Expand Down
1 change: 1 addition & 0 deletions packages/frontmatter/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './FrontmatterBlock';
export * from './licenses';
export * from './downloads';
export * from './Authors';
export * from './Affiliations';
8 changes: 4 additions & 4 deletions packages/jupyter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
"ansi-to-react": "^6.1.6",
"buffer": "^6.0.3",
"classnames": "^2.3.2",
"myst-spec-ext": "^1.1.1",
"myst-common": "^1.1.1",
"myst-config": "^1.1.1",
"myst-frontmatter": "^1.1.1",
"myst-spec-ext": "^1.1.3",
"myst-common": "^1.1.3",
"myst-config": "^1.1.3",
"myst-frontmatter": "^1.1.3",
"myst-spec": "^0.0.4",
"myst-to-react": "^0.5.2",
"nanoid": "^4.0.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/jupyter/src/controls/Buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function SpinnerStatusButton({
</button>
{busy && (
<span className="absolute top-0 left-0 z-10 opacity-100">
<Spinner size={22} />
<Spinner size={24} />
</span>
)}
</div>
Expand Down Expand Up @@ -94,7 +94,7 @@ function SpinnerButton({
</button>
{busy && (
<span className="absolute top-0 left-0 z-10 opacity-100">
<Spinner size={22} />
<Spinner size={24} />
</span>
)}
</div>
Expand Down
Loading

0 comments on commit 1948a7a

Please sign in to comment.