Skip to content

Commit

Permalink
Merge pull request #467 from digirati-co-uk/feature/iiif-hero
Browse files Browse the repository at this point in the history
Added IIIF hero image
  • Loading branch information
stephenwf authored Oct 27, 2021
2 parents a2fe772 + 98b2b7e commit 51490b9
Show file tree
Hide file tree
Showing 9 changed files with 427 additions and 14 deletions.
4 changes: 1 addition & 3 deletions .env
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# Gateway
GATEWAY_SECRET=y@@-rwzsqsqTRcuJjp6L8

# Postgres
POSTGRES_DB=postgres
POSTGRES_PORT=5432
POSTGRES_USER=postgres
Expand Down
1 change: 1 addition & 0 deletions services/madoc-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"bullmq": "^1.8.7",
"canvas-panel-beta": "2.0.2",
"color-hash": "^1.0.3",
"colorthief": "^2.3.2",
"content-type": "^1.0.4",
"contrast": "^1.0.1",
"cookie-parser": "^1.4.5",
Expand Down
230 changes: 230 additions & 0 deletions services/madoc-ts/src/frontend/shared/components/IIIFHero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import { InternationalString } from '@hyperion-framework/types';
import { useRef } from 'react';
import * as React from 'react';
import styled, { css } from 'styled-components';
import { Button } from '../navigation/Button';
import { HrefLink } from '../utility/href-link';
import { LocaleString } from './LocaleString';

type IIIFHeroProps = {
title: InternationalString;
description?: InternationalString;
button?: {
title: InternationalString;
link: string;
isExternal?: boolean;
};
backgroundImage?: string | null;
asset?: {
label: InternationalString;
attribution: InternationalString;
backgroundColor?: string;
thumbnails: string[];
link?: string;
};
};

const HeroContainer = styled.div<{ $noAsset?: boolean }>`
height: 600px;
display: flex;
position: relative;
overflow: hidden;
${props =>
props.$noAsset &&
css`
height: 400px;
`}
`;

const HeroBackground = styled.div<{ $image?: string }>`
background-image: url("${props => props.$image}");
background-size: cover;
background-position: 50% 50%;
transform: scale(1.1);
filter: blur(20px);
opacity: 0.6;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 1;
`;

const HeroAssetContainer = styled.div`
z-index: 2;
width: 520px;
min-width: 0;
overflow: hidden;
margin: 4em 2em;
background-image: linear-gradient(146deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0) 62%);
box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.08), 0 4px 21px 0 rgba(0, 0, 0, 0.23);
border-radius: 15px;
display: flex;
flex-direction: column;
align-self: flex-start;
padding-bottom: 2.5em;
text-decoration: none;
`;

const HeroAssetThumbnails = styled.div<{ $background: string }>`
display: flex;
flex: 1 1 0px;
flex-basis: fit-content;
margin: 3em;
margin-bottom: 1.5em;
position: relative;
&:after {
content: '';
position: absolute;
width: 100%;
height: 30px;
background: #000;
filter: blur(20px);
bottom: 0;
z-index: 1;
}
&:before {
content: '';
position: absolute;
right: -3.5em;
top: -0.5em;
bottom: -0.5em;
filter: blur(2px);
background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, ${props => props.$background} 80%);
width: 5em;
z-index: 3;
pointer-events: none;
}
`;

const HeroAssetLargeThumbnail = styled.div`
z-index: 2;
img {
height: 280px;
object-fit: contain;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.53);
transition: transform 0.3s;
&:hover {
transform: scale(1.02);
}
}
`;

const HeroAssetThumbnail = styled.div`
z-index: 2;
margin-left: 10px;
img {
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.53);
&:first-child {
margin-top: 0;
}
display: block;
margin: 10px 0;
height: 135px;
transition: transform 0.3s;
&:hover {
transform: scale(1.05);
}
}
`;

const HeroAssetLabel = styled(LocaleString)`
text-align: center;
color: #fff;
font-weight: 600;
font-size: 1.4em;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0 2em;
`;

const HeroAssetAttribution = styled(LocaleString)`
color: rgba(255, 255, 255, 0.7);
text-align: center;
font-size: 0.85em;
a {
color: #fff;
}
`;

const HeroContent = styled.div<{ $noAsset?: boolean }>`
z-index: 2;
flex: 1 1 0px;
align-self: center;
padding: 3em 6em 3em 10em;
max-width: 600px;
margin-right: auto;
${props =>
props.$noAsset &&
css`
max-width: 900px;
`}
`;

const HeroTitle = styled(LocaleString)`
display: block;
font-size: 3.8em;
font-weight: 600;
`;

const HeroDescription = styled(LocaleString)`
display: block;
font-size: 1.4em;
font-weight: 600;
`;

const HeroButton = styled(Button).attrs({ $primary: true })`
margin-top: 1em;
font-size: 1.4em;
padding: 0.5em 2em;
`;

export const IIIFHero = (props: IIIFHeroProps) => {
const bigImage = useRef<HTMLImageElement>(null);

return (
<HeroContainer $noAsset={!props.asset}>
<HeroContent $noAsset={!props.asset}>
<HeroTitle>{props.title}</HeroTitle>
<HeroDescription>{props.description}</HeroDescription>
{props.button ? (
<HeroButton as={props.button.isExternal ? 'a' : HrefLink} href={props.button.link}>
<LocaleString>{props.button.title}</LocaleString>
</HeroButton>
) : null}
</HeroContent>
{props.asset ? (
<HeroAssetContainer
as={props.asset.link ? HrefLink : undefined}
href={props.asset.link}
style={{ backgroundColor: props.asset.backgroundColor || '#342145' }}
>
<HeroAssetThumbnails $background={props.asset.backgroundColor || '#342145'}>
<HeroAssetLargeThumbnail>
<img src={props.asset.thumbnails[0]} alt="" ref={bigImage} />
</HeroAssetLargeThumbnail>
<HeroAssetThumbnail>
<img src={props.asset.thumbnails[1]} alt="" />
<img src={props.asset.thumbnails[2]} alt="" />
</HeroAssetThumbnail>
<HeroAssetThumbnail style={{ opacity: 0.5 }}>
<img src={props.asset.thumbnails[3]} alt="" />
<img src={props.asset.thumbnails[4]} alt="" />
</HeroAssetThumbnail>
<HeroAssetThumbnail style={{ opacity: 0.5 }}>
<img src={props.asset.thumbnails[3]} alt="" />
<img src={props.asset.thumbnails[4]} alt="" />
</HeroAssetThumbnail>
</HeroAssetThumbnails>
<HeroAssetLabel>{props.asset.label}</HeroAssetLabel>
<HeroAssetAttribution enableDangerouslySetInnerHTML>{props.asset.attribution}</HeroAssetAttribution>
</HeroAssetContainer>
) : null}
<HeroBackground
$image={props.backgroundImage ? props.backgroundImage : props.asset ? props.asset?.thumbnails[0] : ''}
/>
</HeroContainer>
);
};
1 change: 1 addition & 0 deletions services/madoc-ts/src/frontend/shared/plugins/use-atoms.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Grid } from '@capture-models/editor';
import { ManifestHero } from '../../site/features/ManifestHero';
import AdminPageTitle from '../typography/AdminPageTitle';
import { Breadcrumbs } from '../navigation/Breadcrumbs';
import { Button } from '../navigation/Button';
Expand Down
52 changes: 52 additions & 0 deletions services/madoc-ts/src/frontend/site/features/ManifestHero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import { blockEditorFor } from '../../../extensions/page-blocks/block-editor-react';
import { IIIFHero } from '../../shared/components/IIIFHero';
import { useManifest } from '../hooks/use-manifest';
import { useRelativeLinks } from '../hooks/use-relative-links';

export const ManifestHero: React.FC<{ backgroundColor?: string; hideAsset?: boolean }> = ({
backgroundColor,
hideAsset,
}) => {
const { data } = useManifest();
const manifest = data?.manifest;
const createLink = useRelativeLinks();

if (!manifest) {
return null;
}

return (
<IIIFHero
title={manifest.label}
description={manifest.summary}
backgroundImage={manifest.items.length ? manifest.items[0].thumbnail : ''}
asset={
hideAsset || manifest.items.length === 0
? undefined
: {
label: manifest.label,
attribution: manifest.requiredStatement?.value || { none: [''] },
backgroundColor,
thumbnails: manifest.items.slice(0, 5).map(i => i.thumbnail as string),
link: createLink({ canvasId: manifest.items[0].id }),
}
}
/>
);
};

blockEditorFor(ManifestHero, {
type: 'default.ManifestHero',
label: 'Manifest hero image',
anyContext: ['manifest'],
requiredContext: ['manifest'],
defaultProps: {
backgroundColor: '#000',
hideAsset: false,
},
editor: {
backgroundColor: { type: 'text-field', label: 'Background color (right side)' },
hideAsset: { type: 'checkbox-field', label: 'Hide asset', inlineLabel: 'Check this to hide asset on the right' },
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useSiteConfiguration } from '../features/SiteConfigurationContext';
import { useManifest } from '../hooks/use-manifest';
import { useRelativeLinks } from '../hooks/use-relative-links';
import { Redirect } from 'react-router-dom';
import '../features/ManifestHero';

export const ViewManifest: React.FC<{
project?: ProjectFull;
Expand Down
1 change: 1 addition & 0 deletions services/madoc-ts/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
declare module 'koa2-connect';
declare module 'make-color-accessible';
declare module 'contrast';
declare module 'colorthief';
40 changes: 40 additions & 0 deletions services/madoc-ts/stories/components/iiif-hero.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from 'react';
import { IIIFHero } from '../../src/frontend/shared/components/IIIFHero';

export default {
title: 'Components / IIIF Hero image',
component: IIIFHero,
args: {
title: { en: ['Discover our collection'] },
description: { en: ['From Libraries around the world'] },
button: {
title: { en: ['Explore'] },
link: '#',
isExternal: true,
},
backgroundImage: 'https://iiif.wellcomecollection.org/thumbs/b18035723_0001.JP2/full/290,400/0/default.jpg',
asset: {
label: { de: ['Wunder der Vererbung'] },
attribution: { en: ['Wellcome Collection'] },
backgroundColor: '#342145',
thumbnails: [
'https://iiif.wellcomecollection.org/thumbs/b18035723_0001.JP2/full/290,400/0/default.jpg',
'https://iiif.wellcomecollection.org/thumbs/b18035723_0003.JP2/full/145,200/0/default.jpg',
'https://iiif.wellcomecollection.org/thumbs/b18035723_0004.JP2/full/145,200/0/default.jpg',
'https://iiif.wellcomecollection.org/thumbs/b18035723_0005.JP2/full/145,200/0/default.jpg',
'https://iiif.wellcomecollection.org/thumbs/b18035723_0006.JP2/full/145,200/0/default.jpg',
],
},
},
};

const Template = (props: any) => <IIIFHero {...props} />;

export const DefaultHeroImage = Template.bind({});
DefaultHeroImage.args = {};

export const HeroNoAssets = Template.bind({});
HeroNoAssets.args = {
backgroundImage: 'https://iiif.wellcomecollection.org/thumbs/b18035723_0001.JP2/full/290,400/0/default.jpg',
asset: null,
};
Loading

0 comments on commit 51490b9

Please sign in to comment.