-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from jonaschlegel/add-illustration-resources-page
Add literature page and resources for illustration literature
- Loading branch information
Showing
106 changed files
with
5,451 additions
and
834 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import LiteratureListLayout from '@/components/LiteratureListLayout' | ||
import { genPageMetadata } from 'app/seo' | ||
import fs from 'fs' | ||
import matter from 'gray-matter' | ||
import path from 'path' | ||
|
||
export const metadata = genPageMetadata({ title: 'Literature Resources' }) | ||
|
||
const getLiteratureData = () => { | ||
const dirPath = path.join(process.cwd(), 'data/resources/illustrations') | ||
const files = fs.readdirSync(dirPath) | ||
return files.map((filename) => { | ||
const markdownWithMeta = fs.readFileSync(path.join(dirPath, filename), 'utf-8') | ||
const { data, content } = matter(markdownWithMeta) | ||
|
||
return { | ||
title: data.title, | ||
authors: data.authors, | ||
year: data.year, | ||
publisher: data.publisher, | ||
externalLink: data.externalLink || '', | ||
reviewsLink: data.reviewsLink || '', | ||
literatureType: data.type, | ||
category: data.category, | ||
tags: data.tags, | ||
isbn: data.isbn || '', | ||
doi: data.doi || '', | ||
coverImage: data.coverImage, | ||
abstract: content.match(/## Abstract\s([\s\S]*?)##/)?.[1].trim() || 'No abstract available.', | ||
tableOfContents: | ||
content.match(/## Table of Contents\s([\s\S]*?)##/)?.[1].trim() || | ||
'No table of contents available.', | ||
hidden: data.hidden || false, | ||
purposeAndAudience: | ||
content.match(/## Purpose and Audience\s([\s\S]*?)##/)?.[1].trim() || | ||
'No information available.', | ||
reviews: content.match(/## Reviews\s([\s\S]*?)##/)?.[1].trim() || 'No reviews available.', | ||
keyExcerpt: | ||
content.match(/## Key Excerpt\s([\s\S]*?)##/)?.[1].trim() || 'No key excerpt available.', | ||
} | ||
}) | ||
} | ||
|
||
export default function Literature() { | ||
const literatureData = getLiteratureData() | ||
|
||
return ( | ||
<div className="pb-8 pt-6"> | ||
<h1 className="mb-6 text-4xl font-bold">Archaeological Illustration Resources</h1> | ||
<div className="mb-6"> | ||
A collection of literature resources related to archaeological illustration, including | ||
books, articles, and other publications. This list is a work in progress and will be updated | ||
as new resources are discovered. If you have a resource you would like to add to this list, | ||
please contact me. | ||
</div> | ||
<LiteratureListLayout initialLiteratureData={literatureData} /> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
{"archink":2,"inktober":2,"archaeological-illustration":1,"creative-challenge":1,"learn-in-public":1,"learning":1,"public":1,"illustration":2,"archaeology":2,"archaeological-illustratioon":2,"ipad":1,"digital":1,"digital-drawing":1,"definition":1,"scientific-communication":1,"scicomm":1,"scicom":2,"science-communication":2,"archaeoink":1} | ||
{"visual-storytelling":1,"conceptual-illustration":2,"women-archaeologists":1,"ann-axtell-morris":1,"archaeological-illustration":8,"public-archaeology":1,"visual-communication":2,"science-communication":5,"social-media-engagement":1,"archink":2,"inktober":3,"creative-challenge":1,"archaeological-communication":1,"stippling-in-archaeology":1,"drawing-techniques":1,"material-culture-documentation":1,"visual-archaeology":1,"100daysofdrawing":1,"skill-development":1,"observational-practice":1,"artefact-engagement":1,"polychromy":1,"uncertainty":1,"reconstruction":1,"ancient-greek-art":1,"illustration":3,"skin-colour":1,"eurocentric-bias":1,"learn-in-public":1,"learning":1,"public":1,"archaeology":2,"archaeological-illustratioon":2,"ipad":1,"digital":1,"digital-drawing":1,"definition":1,"scientific-communication":1,"scicomm":1,"scicom":2,"archaeoink":1} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
import NextImage, { ImageProps } from 'next/image' | ||
import NextImage from 'next/image' | ||
import { ComponentProps } from 'react' | ||
|
||
const Image = ({ ...rest }: ImageProps) => <NextImage {...rest} /> | ||
type NextImageProps = ComponentProps<typeof NextImage> | ||
|
||
const Image = ({ ...rest }: NextImageProps) => <NextImage {...rest} /> | ||
|
||
export default Image |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,37 @@ | ||
import type { LinkProps } from 'next/link' | ||
/* eslint-disable jsx-a11y/anchor-has-content */ | ||
import Link from 'next/link' | ||
import { AnchorHTMLAttributes } from 'react' | ||
|
||
const CustomLink = ({ href, ...rest }: LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>) => { | ||
type LinkProps = Parameters<typeof Link>[0] | ||
|
||
const CustomLink = ({ | ||
href, | ||
children, | ||
...rest | ||
}: LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>) => { | ||
const isInternalLink = href && href.startsWith('/') | ||
const isAnchorLink = href && href.startsWith('#') | ||
|
||
if (isInternalLink) { | ||
return <Link href={href} {...rest} /> | ||
return ( | ||
<Link href={href} {...rest}> | ||
{children} | ||
</Link> | ||
) | ||
} | ||
|
||
if (isAnchorLink) { | ||
return <a href={href} {...rest} /> | ||
return ( | ||
<a href={href} {...rest}> | ||
{children} | ||
</a> | ||
) | ||
} | ||
|
||
return <a target="_blank" rel="noopener noreferrer" href={href} {...rest} /> | ||
return ( | ||
<a target="_blank" rel="noopener noreferrer" href={href} {...rest}> | ||
{children} | ||
</a> | ||
) | ||
} | ||
|
||
export default CustomLink |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import { startTransition, useState } from 'react' | ||
import Image from './Image' | ||
import LiteratureTag from './LiteratureTag' | ||
|
||
interface Author { | ||
lastName: string | ||
firstName: string | ||
} | ||
|
||
interface LiteratureProps { | ||
title: string | ||
authors?: Author[] | ||
year: string | ||
publisher: string | ||
externalLink?: string | ||
reviewsLink?: string | ||
literatureType: string | ||
category: string | ||
tags: string[] | ||
isbn?: string | ||
doi?: string | ||
abstract: string | ||
tableOfContents: string | ||
coverImage?: string | ||
hidden?: boolean | ||
purposeAndAudience: string | ||
reviews: string | ||
keyExcerpt: string | ||
onTagClick: (tag: string) => void | ||
} | ||
|
||
const LiteratureCard = ({ | ||
title, | ||
authors = [], | ||
year, | ||
publisher, | ||
externalLink, | ||
reviewsLink, | ||
literatureType, | ||
category, | ||
tags, | ||
isbn, | ||
doi, | ||
abstract, | ||
tableOfContents, | ||
coverImage, | ||
hidden, | ||
purposeAndAudience, | ||
reviews, | ||
keyExcerpt, | ||
onTagClick, | ||
}: LiteratureProps) => { | ||
const [isExpanded, setIsExpanded] = useState(false) | ||
|
||
if (hidden) return null | ||
|
||
const formattedAuthors = authors.length | ||
? authors.map((author) => `${author.firstName} ${author.lastName}`).join(', ') | ||
: 'No authors listed' | ||
|
||
const toggleExpand = () => setIsExpanded(!isExpanded) | ||
|
||
return ( | ||
<div | ||
role="button" | ||
tabIndex={0} | ||
className={`flex w-full cursor-pointer rounded-md border-2 border-gray-200 border-opacity-60 p-4 shadow-lg transition-all duration-300 hover:shadow-xl dark:border-gray-700 ${ | ||
isExpanded ? 'max-h-[600px]' : 'max-h-[180px]' | ||
}`} | ||
onClick={toggleExpand} | ||
onKeyDown={(e) => { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
toggleExpand() | ||
} | ||
}} | ||
> | ||
{/* Cover Image Section */} | ||
{coverImage ? ( | ||
<Image | ||
src={coverImage} | ||
alt={`Cover of ${title}`} | ||
width={96} | ||
height={128} | ||
className="mr-4 rounded-md object-cover" | ||
/> | ||
) : ( | ||
<div className="mr-4 h-32 w-24 rounded-md bg-gray-200"></div> | ||
)} | ||
|
||
{/* Content Section */} | ||
<div className="flex-1 space-y-2 overflow-hidden transition-all duration-300"> | ||
<div className="flex items-start justify-between"> | ||
<div> | ||
<h3 className="mb-2 text-2xl font-bold leading-8 tracking-tight dark:text-white"> | ||
{title} | ||
</h3> | ||
<p className="text-md text-gray-500 dark:text-gray-400"> | ||
By {formattedAuthors} • {year} | ||
</p> | ||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">{publisher}</p> | ||
</div> | ||
</div> | ||
|
||
{/* Tags Section */} | ||
<div className="mt-2 flex flex-wrap"> | ||
{tags.map((tag) => ( | ||
<LiteratureTag key={tag} text={tag} onClick={onTagClick} /> | ||
))} | ||
</div> | ||
|
||
{/* Expandable section */} | ||
{isExpanded && ( | ||
<div className="mt-4 max-h-[300px] space-y-4 overflow-y-auto text-gray-700 dark:text-gray-300"> | ||
<div> | ||
<h4 className="font-semibold">Abstract</h4> | ||
<p className="text-sm">{abstract}</p> | ||
</div> | ||
<div> | ||
<h4 className="font-semibold">Purpose and Audience</h4> | ||
<p className="text-sm">{purposeAndAudience}</p> | ||
</div> | ||
{/* <div> | ||
<h4 className="font-semibold">Table of Contents</h4> | ||
<p className="text-sm">{tableOfContents}</p> | ||
</div> */} | ||
<div> | ||
<h4 className="font-semibold">Key Excerpt</h4> | ||
<p className="text-sm">{keyExcerpt}</p> | ||
</div> | ||
<div> | ||
<h4 className="font-semibold">Reviews</h4> | ||
<p className="text-sm">{reviews}</p> | ||
{reviewsLink && ( | ||
<a | ||
href={reviewsLink} | ||
className="text-sm hover:text-primary-600 dark:hover:text-primary-400" | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
—Read futher Reviews | ||
</a> | ||
)} | ||
</div> | ||
<div className="mt-2 flex space-x-4 text-sm"> | ||
{externalLink && ( | ||
<a | ||
href={externalLink} | ||
className="hover:text-primary-600 dark:hover:text-primary-400" | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
Get it here | ||
</a> | ||
)} | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export default LiteratureCard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
'use client' | ||
|
||
import { useState, useMemo } from 'react' | ||
import LiteratureList from './LiteratureList' | ||
import SearchBar from './SearchBar' | ||
import TagFilter from './TagFilter' | ||
import { LiteratureData } from '../types/types' | ||
|
||
interface LiteratureFilterProps { | ||
initialLiteratureData: LiteratureData[] | ||
} | ||
|
||
const LiteratureFilter = ({ initialLiteratureData }: LiteratureFilterProps) => { | ||
const [searchQuery, setSearchQuery] = useState('') | ||
const [selectedTags, setSelectedTags] = useState<string[]>([]) | ||
const [displayLimit, setDisplayLimit] = useState(10) | ||
|
||
const handleSearch = (query: string) => setSearchQuery(query) | ||
const handleTagChange = (tags: string[]) => setSelectedTags(tags) | ||
|
||
const onTagClick = (tag: string) => { | ||
setSelectedTags((prevTags) => | ||
prevTags.includes(tag) ? prevTags.filter((t) => t !== tag) : [...prevTags, tag] | ||
) | ||
} | ||
|
||
const filteredLiteratureData = useMemo(() => { | ||
return initialLiteratureData.filter((item) => { | ||
const matchesSearch = | ||
item.title.toLowerCase().includes(searchQuery.toLowerCase()) || | ||
item.authors.some((author) => | ||
`${author.firstName} ${author.lastName}`.toLowerCase().includes(searchQuery.toLowerCase()) | ||
) | ||
const matchesTags = | ||
selectedTags.length === 0 || selectedTags.every((tag) => item.tags.includes(tag)) | ||
return matchesSearch && matchesTags | ||
}) | ||
}, [initialLiteratureData, searchQuery, selectedTags]) | ||
|
||
const allTags = Array.from(new Set(initialLiteratureData.flatMap((item) => item.tags))) | ||
|
||
const displayedLiteratureData = filteredLiteratureData.slice(0, displayLimit) | ||
|
||
const handleLoadMore = () => { | ||
setDisplayLimit((prevLimit) => prevLimit + 10) | ||
} | ||
|
||
return ( | ||
<div className="flex space-x-6"> | ||
<div className="w-1/4 rounded-lg bg-gray-100 p-4 shadow-md"> | ||
<h2 className="mb-4 text-lg font-semibold">Filter by Keywords</h2> | ||
<TagFilter | ||
availableTags={allTags} | ||
selectedTags={selectedTags} | ||
onTagChange={handleTagChange} | ||
/> | ||
</div> | ||
|
||
<div className="w-3/4 space-y-5"> | ||
<SearchBar onSearch={handleSearch} /> | ||
<LiteratureList literatureData={displayedLiteratureData} onTagClick={onTagClick} /> | ||
|
||
{displayLimit < filteredLiteratureData.length && ( | ||
<div className="mt-4 flex"> | ||
<button | ||
onClick={handleLoadMore} | ||
className="rounded-lg bg-primary-500 px-4 py-2 text-white hover:bg-primary-600" | ||
> | ||
Load More | ||
</button> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export default LiteratureFilter |
Oops, something went wrong.