feat: Created home page
1
.gitignore
vendored
|
@ -53,6 +53,7 @@ sites/org/authors.json
|
||||||
sites/org/showcase-tags.mjs
|
sites/org/showcase-tags.mjs
|
||||||
sites/org/design-examples.mjs
|
sites/org/design-examples.mjs
|
||||||
sites/org/docs/designs/*.mdx
|
sites/org/docs/designs/*.mdx
|
||||||
|
sites/org/recent-blog-posts.mjs
|
||||||
|
|
||||||
# sde auto-generated content
|
# sde auto-generated content
|
||||||
sites/sde/public/android-chrome-192x192.png
|
sites/sde/public/android-chrome-192x192.png
|
||||||
|
|
|
@ -101,6 +101,7 @@ packageJson:
|
||||||
"./components/Null": "./components/Null/index.mjs"
|
"./components/Null": "./components/Null/index.mjs"
|
||||||
"./components/Number": "./components/Number/index.mjs"
|
"./components/Number": "./components/Number/index.mjs"
|
||||||
"./components/Pattern": "./components/Pattern/index.mjs"
|
"./components/Pattern": "./components/Pattern/index.mjs"
|
||||||
|
"./components/Patrons": "./components/Patrons/index.mjs"
|
||||||
"./components/Popout": "./components/Popout/index.mjs"
|
"./components/Popout": "./components/Popout/index.mjs"
|
||||||
"./components/Role": "./components/Role/index.mjs"
|
"./components/Role": "./components/Role/index.mjs"
|
||||||
"./components/SignIn": "./components/SignIn/index.mjs"
|
"./components/SignIn": "./components/SignIn/index.mjs"
|
||||||
|
|
|
@ -55,12 +55,27 @@ export const SuccessLink = ({
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
|
|
||||||
export const CardLink = ({ href, title, Icon, children, Link }) => {
|
export const CardLink = ({
|
||||||
|
href,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
Link,
|
||||||
|
className = 'tw-bg-base-200 tw-text-base-content',
|
||||||
|
}) => {
|
||||||
if (!Link) Link = BaseLink
|
if (!Link) Link = BaseLink
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link className="">
|
<Link
|
||||||
|
href={href}
|
||||||
|
className={`tw-px-8 tw-py-10 tw-rounded-lg tw-block ${className}
|
||||||
|
hover:tw-bg-secondary hover:tw-bg-opacity-10 tw-shadow-lg
|
||||||
|
tw-transition-color tw-duration-300 grow hover:tw-no-underline hover:tw-text-base-content`}
|
||||||
|
>
|
||||||
|
<h2 className="tw-mb-4 tw-text-inherit tw-flex tw-flex-row tw-gap-4 tw-justify-between tw-items-center">
|
||||||
{title}
|
{title}
|
||||||
|
<span className="tw-shrink-0">{icon}</span>
|
||||||
|
</h2>
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
|
|
309
packages/react/components/Patrons/index.mjs
Normal file
|
@ -26,7 +26,7 @@ import { EmailInput } from '@freesewing/react/components/Input'
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
//import { Robot } from 'shared/components/robot/index.mjs'
|
//import { Robot } from 'shared/components/robot/index.mjs'
|
||||||
|
|
||||||
export const SignUp = () => {
|
export const SignUp = ({ embed = false }) => {
|
||||||
// State
|
// State
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
const [emailValid, setEmailValid] = useState(false)
|
const [emailValid, setEmailValid] = useState(false)
|
||||||
|
@ -56,22 +56,22 @@ export const SignUp = () => {
|
||||||
if (status === 201 && body.result === 'created') setResult('success')
|
if (status === 201 && body.result === 'created') setResult('success')
|
||||||
else {
|
else {
|
||||||
setModal(
|
setModal(
|
||||||
<ModalWrapper bg="base-100 lg:bg-base-300">
|
<ModalWrapper bg="tw-base-100 lg:tw-bg-base-300">
|
||||||
<div className="bg-base-100 rounded-lg p-4 lg:px-8 max-w-xl lg:shadow-lg">
|
<div className="tw-bg-base-100 tw-rounded-lg tw-p-4 lg:tw-px-8 tw-max-w-xl lg:tw-shadow-lg">
|
||||||
<h3>An error occured while trying to process your request</h3>
|
<h3>An error occured while trying to process your request</h3>
|
||||||
<p className="text-lg">
|
<p className="tw-text-lg">
|
||||||
Unfortunately, we cannot recover from this error, we need a human being to look into
|
Unfortunately, we cannot recover from this error, we need a human being to look into
|
||||||
this.
|
this.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-lg">
|
<p className="tw-text-lg">
|
||||||
Feel free to try again, or reach out to support so we can assist you.
|
Feel free to try again, or reach out to support so we can assist you.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-row gap-4 items-center justify-center p-8 flex-wrap">
|
<div className="tw-flex tw-flex-row tw-gap-4 tw-items-center tw-justify-center tw-p-8 tw-flex-wrap">
|
||||||
<IconButton onClick={() => setResult(false)}>
|
<IconButton onClick={() => setResult(false)}>
|
||||||
<LeftIcon />
|
<LeftIcon />
|
||||||
Back
|
Back
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton href="/support" className="daisy-btn-outline">
|
<IconButton href="/support" className="tw-daisy-btn-outline">
|
||||||
<HelpIcon />
|
<HelpIcon />
|
||||||
Contact support
|
Contact support
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -90,10 +90,13 @@ export const SignUp = () => {
|
||||||
window.location.href = result.data.authUrl
|
window.location.href = result.data.authUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const Heading = embed
|
||||||
|
? ({ children }) => <h2 className="tw-text-inherit">{children}</h2>
|
||||||
|
: ({ children }) => <h1 className="tw-text-inherit">{children}</h1>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="tw-w-full">
|
||||||
<h1 className="text-inherit">
|
<Heading className="tw-text-inherit">
|
||||||
{result ? (
|
{result ? (
|
||||||
result === 'success' ? (
|
result === 'success' ? (
|
||||||
<span>Now check your inbox</span>
|
<span>Now check your inbox</span>
|
||||||
|
@ -103,23 +106,23 @@ export const SignUp = () => {
|
||||||
) : (
|
) : (
|
||||||
<span>Create a FreeSewing account</span>
|
<span>Create a FreeSewing account</span>
|
||||||
)}
|
)}
|
||||||
</h1>
|
</Heading>
|
||||||
|
|
||||||
{result ? (
|
{result ? (
|
||||||
result === 'success' ? (
|
result === 'success' ? (
|
||||||
<>
|
<>
|
||||||
<p className="text-inherit text-lg">
|
<p className="tw-text-inherit tw-text-lg">
|
||||||
Go check your inbox for an email from <b>FreeSewing.org</b>
|
Go check your inbox for an email from <b>FreeSewing.org</b>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-inherit text-lg">
|
<p className="tw-text-inherit tw-text-lg">
|
||||||
Click your personal signup link in that email to create your FreeSewing account.
|
Click your personal signup link in that email to create your FreeSewing account.
|
||||||
</p>
|
</p>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-2">
|
||||||
<IconButton onClick={() => setResult(false)}>
|
<IconButton onClick={() => setResult(false)}>
|
||||||
<LeftIcon />
|
<LeftIcon />
|
||||||
Back
|
Back
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton href="/support" className="daisy-btn-outline">
|
<IconButton href="/support" className="tw-daisy-btn-outline">
|
||||||
<HelpIcon />
|
<HelpIcon />
|
||||||
Contact support
|
Contact support
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -128,18 +131,18 @@ export const SignUp = () => {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
robot here
|
robot here
|
||||||
<p className="text-inherit text-lg">
|
<p className="tw-text-inherit tw-text-lg">
|
||||||
Unfortunately, we cannot recover from this error, we need a human being to look into
|
Unfortunately, we cannot recover from this error, we need a human being to look into
|
||||||
this.
|
this.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-inherit text-lg">
|
<p className="tw-text-inherit tw-text-lg">
|
||||||
Feel free to try again, or reach out to support so we can assist you.
|
Feel free to try again, or reach out to support so we can assist you.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-row gap-4 items-center justify-center p-8">
|
<div className="tw-flex tw-flex-row tw-gap-4 tw-items-center tw-justify-center tw-p-8">
|
||||||
<button className="btn btn-ghost" onClick={() => setResult(false)}>
|
<button className="tw-daisy-btn tw-daisy-btn-ghost" onClick={() => setResult(false)}>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<Link href="/support" className="btn btn-ghost">
|
<Link href="/support" className="tw-daisy-btn tw-daisy-btn-ghost">
|
||||||
Contact support
|
Contact support
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
@ -147,7 +150,7 @@ export const SignUp = () => {
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p className="text-inherit">To receive a sign-up link, enter your email address</p>
|
<p className="tw-text-inherit">To receive a sign-up link, enter your email address</p>
|
||||||
<form onSubmit={signupHandler}>
|
<form onSubmit={signupHandler}>
|
||||||
<EmailInput
|
<EmailInput
|
||||||
id="signup-email"
|
id="signup-email"
|
||||||
|
@ -161,7 +164,7 @@ export const SignUp = () => {
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={signupHandler}
|
onClick={signupHandler}
|
||||||
btnProps={{ type: 'submit' }}
|
btnProps={{ type: 'submit' }}
|
||||||
className="lg:w-full grow"
|
className="lg:tw-w-full tw-grow tw-mt-2"
|
||||||
>
|
>
|
||||||
<EmailIcon />
|
<EmailIcon />
|
||||||
Email me a sign-up link
|
Email me a sign-up link
|
||||||
|
@ -169,7 +172,7 @@ export const SignUp = () => {
|
||||||
</form>
|
</form>
|
||||||
{showAll ? (
|
{showAll ? (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-1 items-center">
|
<div className="tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-1 tw-items-center tw-mt-1">
|
||||||
{['Google', 'GitHub'].map((provider) => (
|
{['Google', 'GitHub'].map((provider) => (
|
||||||
<IconButton
|
<IconButton
|
||||||
key={provider}
|
key={provider}
|
||||||
|
@ -182,22 +185,22 @@ export const SignUp = () => {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<IconButton color="neutral" href="/signin" className="daisy-btn-lg">
|
<IconButton color="neutral" href="/signin" className="tw-daisy-btn-lg tw-mt-1">
|
||||||
<span className="hidden md:block">
|
<span className="tw-hidden md:tw-block">
|
||||||
<KeyIcon className="h-10 w-10" />
|
<KeyIcon className="tw-h-10 tw-w-10" />
|
||||||
</span>
|
</span>
|
||||||
Sign in here
|
Sign in here
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<div className="flex flex-row justify-center mt-2">
|
<div className="tw-flex tw-flex-row tw-justify-center tw-mt-2">
|
||||||
<IconButton color="ghost" onClick={() => setShowAll(false)}>
|
<IconButton color="ghost" onClick={() => setShowAll(false)}>
|
||||||
<DownIcon className="w-6 h-6 rotate-180" />
|
<DownIcon className="tw-w-6 tw-h-6 tw-rotate-180" />
|
||||||
Fewer options
|
Fewer options
|
||||||
<DownIcon className="w-6 h-6 rotate-180" />
|
<DownIcon className="tw-w-6 tw-h-6 tw-rotate-180" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-row justify-center mt-2">
|
<div className="tw-flex tw-flex-row tw-justify-center tw-mt-2">
|
||||||
<IconButton color="ghost" onClick={() => setShowAll(true)}>
|
<IconButton color="ghost" onClick={() => setShowAll(true)}>
|
||||||
<DownIcon />
|
<DownIcon />
|
||||||
More options
|
More options
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
"./components/Null": "./components/Null/index.mjs",
|
"./components/Null": "./components/Null/index.mjs",
|
||||||
"./components/Number": "./components/Number/index.mjs",
|
"./components/Number": "./components/Number/index.mjs",
|
||||||
"./components/Pattern": "./components/Pattern/index.mjs",
|
"./components/Pattern": "./components/Pattern/index.mjs",
|
||||||
|
"./components/Patrons": "./components/Patrons/index.mjs",
|
||||||
"./components/Popout": "./components/Popout/index.mjs",
|
"./components/Popout": "./components/Popout/index.mjs",
|
||||||
"./components/Role": "./components/Role/index.mjs",
|
"./components/Role": "./components/Role/index.mjs",
|
||||||
"./components/SignIn": "./components/SignIn/index.mjs",
|
"./components/SignIn": "./components/SignIn/index.mjs",
|
||||||
|
|
|
@ -268,65 +268,41 @@ const config = {
|
||||||
{ to: '/showcase/', label: '📷 Showcase', position: 'left' },
|
{ to: '/showcase/', label: '📷 Showcase', position: 'left' },
|
||||||
{ to: '/blog/', label: '📰 Blog', position: 'left' },
|
{ to: '/blog/', label: '📰 Blog', position: 'left' },
|
||||||
{ to: '/new/', label: '➕ New...', position: 'right' },
|
{ to: '/new/', label: '➕ New...', position: 'right' },
|
||||||
{ to: '/account/', label: '📰 Account', position: 'right' },
|
{ to: '/account/', label: '🔒 Account', position: 'right' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
style: 'light',
|
style: 'light',
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
title: 'Docs',
|
title: 'Sections',
|
||||||
items: [
|
items: [
|
||||||
{ label: 'FreeSewing designs', to: '/docs/designs/' },
|
{ label: 'FreeSewing Designs', to: '/designs/' },
|
||||||
{ label: 'About FreeSewing', to: '/docs/about/' },
|
{ label: 'FreeSewing Showcase', to: '/showcase/' },
|
||||||
{ label: 'Measurements we use', to: '/docs/measurements/' },
|
{ label: 'FreeSewing Blog', to: '/blog/' },
|
||||||
{ label: 'Sewing terminology', to: '/docs/sewing/' },
|
{ label: 'FreeSewing Editor', to: '/editor/' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Help & Support',
|
title: 'Help & Support',
|
||||||
items: [
|
items: [
|
||||||
{
|
{ label: 'About FreeSewing', to: '/docs/about/' },
|
||||||
label: 'Discord',
|
{ label: 'Getting Started', to: '/docs/about/guide/' },
|
||||||
href: 'https://discord.freesewing.org/',
|
{ label: 'Frequently Asked Questions', href: '/docs/about/faq/' },
|
||||||
},
|
{ label: 'Need Help?', href: '/support' },
|
||||||
{
|
|
||||||
label: 'GitHub Issues',
|
|
||||||
href: 'https://github.com/freesewing/freesewing/issues',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'GitHub Discussions',
|
|
||||||
href: 'https://github.com/freesewing/freesewing/discussions',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'All Support Options',
|
|
||||||
href: 'https://freesewing.org/support',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'More',
|
title: 'More',
|
||||||
items: [
|
items: [
|
||||||
{
|
{ label: 'FreeSewing.dev', to: 'https://freesewing.dev/' },
|
||||||
label: 'FreeSewing.org',
|
{ label: 'FreeSewing.social', to: 'https://freesewing.social/' },
|
||||||
to: 'https://freesewing.org/',
|
{ label: 'Code on GitHub', to: 'https://github.com/freesewing/freesewing' },
|
||||||
},
|
{ label: 'FreeSewing Revenue Pledge 💜', href: '/docs/about/pledge/' },
|
||||||
{
|
|
||||||
label: 'Blog',
|
|
||||||
to: 'https://freesewing.org/blog/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Showcase',
|
|
||||||
to: 'https://freesewing.org/showccase/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Code on GitHub',
|
|
||||||
href: 'https://github.com/freesewing/freesewing',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
copyright: `<a href="https://freesewing.org/">FreeSewing</a> is brought to you by <a href="https://github.com/joostdecock">Joost De Cock</a> and <a href="https://github.com/freesewing/freesewing/blob/develop/CONTRIBUTORS.md">contributors</a> with the financial support of <a href="https://freesewing.org/patrons/join">our patrons</a>`,
|
copyright: `<a href="https://freesewing.org/">FreeSewing</a> is brought to you by <a href="https://github.com/joostdecock">Joost De Cock</a> and <a href="https://github.com/freesewing/freesewing/blob/develop/CONTRIBUTORS.md">contributors</a> with the financial support of <a href="/patrons/join">our patrons</a>`,
|
||||||
},
|
},
|
||||||
prism: {
|
prism: {
|
||||||
theme: prismThemes.dracula,
|
theme: prismThemes.dracula,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { glob } from 'glob'
|
import { glob } from 'glob'
|
||||||
import matter from 'gray-matter'
|
import matter from 'gray-matter'
|
||||||
|
import { orderBy } from '../../../packages/utils/src/index.mjs'
|
||||||
|
|
||||||
const loadExamplesTagsAndAuthors = async () => {
|
const loadExamplesTagsAndAuthors = async () => {
|
||||||
const lists = {
|
const lists = {
|
||||||
|
@ -12,6 +13,7 @@ const loadExamplesTagsAndAuthors = async () => {
|
||||||
const tags = []
|
const tags = []
|
||||||
const authors = []
|
const authors = []
|
||||||
const examples = {}
|
const examples = {}
|
||||||
|
const recentBlogPosts = []
|
||||||
for (const type of Object.keys(lists)) {
|
for (const type of Object.keys(lists)) {
|
||||||
for (const file of lists[type]) {
|
for (const file of lists[type]) {
|
||||||
const content = await fs.readFileSync(file, 'utf-8')
|
const content = await fs.readFileSync(file, 'utf-8')
|
||||||
|
@ -32,7 +34,12 @@ const loadExamplesTagsAndAuthors = async () => {
|
||||||
if (typeof examples[tag] === 'undefined') examples[tag] = []
|
if (typeof examples[tag] === 'undefined') examples[tag] = []
|
||||||
examples[tag].push({ title: data.data.title, id: file.split('/')[1] })
|
examples[tag].push({ title: data.data.title, id: file.split('/')[1] })
|
||||||
}
|
}
|
||||||
}
|
} else if (type === 'blog')
|
||||||
|
recentBlogPosts.push({
|
||||||
|
title: data.data.title,
|
||||||
|
slug: file.split('/')[1],
|
||||||
|
date: new Date(data.data.date).getTime(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +47,7 @@ const loadExamplesTagsAndAuthors = async () => {
|
||||||
authors: [...new Set(authors.sort())], // Make them unique
|
authors: [...new Set(authors.sort())], // Make them unique
|
||||||
tags: [...new Set(tags.sort())], // Make them unique
|
tags: [...new Set(tags.sort())], // Make them unique
|
||||||
examples,
|
examples,
|
||||||
|
recentBlogPosts: orderBy(recentBlogPosts, 'date', 'desc').slice(0, 2),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +77,7 @@ const loadUser = async (id) => {
|
||||||
|
|
||||||
async function prebuild() {
|
async function prebuild() {
|
||||||
const all = {}
|
const all = {}
|
||||||
const { authors, examples, tags } = await loadExamplesTagsAndAuthors()
|
const { authors, examples, tags, recentBlogPosts } = await loadExamplesTagsAndAuthors()
|
||||||
for (const author of authors) {
|
for (const author of authors) {
|
||||||
const user = await loadUser(author)
|
const user = await loadUser(author)
|
||||||
if (user.profile.id) all[user.profile.id] = userAsAuthor(user)
|
if (user.profile.id) all[user.profile.id] = userAsAuthor(user)
|
||||||
|
@ -80,6 +88,10 @@ async function prebuild() {
|
||||||
`export const tags = ${JSON.stringify([...new Set(tags.sort())])}`
|
`export const tags = ${JSON.stringify([...new Set(tags.sort())])}`
|
||||||
)
|
)
|
||||||
fs.writeFileSync(`./design-examples.mjs`, `export const examples = ${JSON.stringify(examples)}`)
|
fs.writeFileSync(`./design-examples.mjs`, `export const examples = ${JSON.stringify(examples)}`)
|
||||||
|
fs.writeFileSync(
|
||||||
|
`./recent-blog-posts.mjs`,
|
||||||
|
`export const recentBlogPosts = ${JSON.stringify(recentBlogPosts, 0, 2)}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
prebuild()
|
prebuild()
|
||||||
|
|
|
@ -1,84 +1,386 @@
|
||||||
|
// Dependencies
|
||||||
|
import { recentBlogPosts } from '@site/recent-blog-posts.mjs'
|
||||||
|
// Hooks
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
// Components
|
||||||
|
import Link from '@docusaurus/Link'
|
||||||
|
import { DocusaurusPage } from '@freesewing/react/components/Docusaurus'
|
||||||
import Layout from '@theme/Layout'
|
import Layout from '@theme/Layout'
|
||||||
import MDXContent from '@theme/MDXContent'
|
import {
|
||||||
|
ChatIcon,
|
||||||
|
DesignIcon,
|
||||||
|
DocsIcon,
|
||||||
|
FreeSewingIcon,
|
||||||
|
HelpIcon,
|
||||||
|
NewsletterIcon,
|
||||||
|
NoIcon,
|
||||||
|
OkIcon,
|
||||||
|
ShowcaseIcon,
|
||||||
|
} from '@freesewing/react/components/Icon'
|
||||||
|
import { SignUp } from '@freesewing/react/components/SignUp'
|
||||||
|
import {
|
||||||
|
HortensiaFront,
|
||||||
|
HiFront,
|
||||||
|
TeaganFront,
|
||||||
|
AaronFront,
|
||||||
|
AlbertFront,
|
||||||
|
BruceBack,
|
||||||
|
BruceFront,
|
||||||
|
SimonBack,
|
||||||
|
SimonFront,
|
||||||
|
WahidBack,
|
||||||
|
WahidFront,
|
||||||
|
} from '@freesewing/react/components/LineDrawing'
|
||||||
|
import { BlogPostTeaser } from '@site/src/theme/BlogPostItems/index.js'
|
||||||
|
import { CardLink } from '@freesewing/react/components/Link'
|
||||||
|
import { PleaseSubscribe } from '@freesewing/react/components/Patrons'
|
||||||
|
|
||||||
const styles = {
|
const Card = ({ title, children, icon }) => (
|
||||||
top: {
|
<div className={`tw-px-8 tw-bg-primary/5 tw-py-10 tw-rounded-lg tw-block tw-shadow-lg tw-grow`}>
|
||||||
margin: 'auto',
|
<h2 className="tw-mb-4 tw-text-inherit tw-flex tw-flex-row tw-gap-4 tw-justify-between tw-items-center tw-font-medium">
|
||||||
textAlign: 'center',
|
{title}
|
||||||
paddingTop: '2rem',
|
{icon}
|
||||||
},
|
</h2>
|
||||||
logo: {
|
|
||||||
maxWidth: '140px',
|
|
||||||
},
|
|
||||||
cards: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
maxWidth: '1600px',
|
|
||||||
margin: 'auto',
|
|
||||||
padding: '1rem',
|
|
||||||
gap: '1.5rem',
|
|
||||||
justifyContent: 'center',
|
|
||||||
marginBottom: '3rem',
|
|
||||||
},
|
|
||||||
card: {
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '600px',
|
|
||||||
boxShadow: '1px 1px 5px #0004',
|
|
||||||
borderRadius: '0.5rem',
|
|
||||||
padding: '1rem 1.5rem',
|
|
||||||
backgroundColor: 'var(--ifm-footer-background-color)',
|
|
||||||
},
|
|
||||||
cardheading: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
width: '100%',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
cardicon: {
|
|
||||||
fontSize: '2rem',
|
|
||||||
},
|
|
||||||
cardp: {
|
|
||||||
fontSize: '1.15rem',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const Card = ({ title, icon, children, href }) => (
|
|
||||||
<a style={styles.card} href={href}>
|
|
||||||
<h3 style={styles.cardheading}>
|
|
||||||
<span>{title}</span>
|
|
||||||
<span style={styles.cardicon}>{icon}</span>
|
|
||||||
</h3>
|
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Free Bespoke Sewing Patterns',
|
||||||
|
description:
|
||||||
|
'FreeSewing is open source software to generate bespoke sewing ' +
|
||||||
|
'patterns, loved by home sewers and fashion entrepreneurs alike.',
|
||||||
|
}
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<Layout
|
<DocusaurusPage DocusaurusLayout={Layout} {...meta} Layout={false}>
|
||||||
title={`FreeSewing documentation for developers and contributors`}
|
<div className="tw-max-w-7xl tw-mx-auto tw-my-12 tw-px-4">
|
||||||
description="FreeSewing is an open source Javascript library for parametric sewing patterns"
|
<div className="tw-text-center">
|
||||||
|
<FreeSewingIcon className="tw-w-48 tw-h-48 tw-mx-auto tw-pr-3" />
|
||||||
|
<h1 className="tw-font-black tw-text-5xl lg:tw-text-7xl tw-tracking-tighter tw-mb-0 tw-pb-0">
|
||||||
|
FreeSewing
|
||||||
|
</h1>
|
||||||
|
<h2 className="tw-text-xl lg:tw-text-3xl tw-font-medium tw-tracking-tighter tw-mt-0 tw-pt-0">
|
||||||
|
Free Bespoke Sewing Patterns
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tw-flex tw-flex-col tw-gap-8 md:tw-grid md:tw-grid-cols-2 md:tw-gap-4 tw-mt-12 md:tw-mt-20 md:tw-px-4">
|
||||||
|
<Card
|
||||||
|
title="What is FreeSewing?"
|
||||||
|
icon={<OkIcon className="tw-w-12 tw-h-12 tw-text-success" stroke={4} />}
|
||||||
>
|
>
|
||||||
<MDXContent>
|
<p className="tw-font-medium tw-text-lg tw-mb-4">
|
||||||
<div style={styles.top}>
|
FreeSewing is open source software to generate bespoke sewing patterns, loved by home
|
||||||
<h1>Fixme: Build home page</h1>
|
sewers and fashion entrepreneurs alike.
|
||||||
</div>
|
</p>
|
||||||
<div style={styles.cards}>
|
<p className="tw-font-medium tw-text-lg tw-mb-4">
|
||||||
<Card title="FreeSewing Designs" icon="🤓" href="/docs/designs/">
|
Industry sizing is a bunch of lies. Join the slow fashion revolution and enjoy clothes
|
||||||
<p style={styles.cardp}>Documentation for all our designs.</p>
|
that fit you.
|
||||||
|
</p>
|
||||||
|
<p className="tw-font-medium tw-text-lg">
|
||||||
|
Speaking of revolution, we do not tolerate nazis here.
|
||||||
|
</p>
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="About FreeSewing" icon="👕" href="/docs/about/">
|
<Card
|
||||||
<p style={styles.cardp}>Documentation about FreeSewing.org and how to use it.</p>
|
title="What is FreeSewing not?"
|
||||||
</Card>
|
icon={<NoIcon className="tw-w-12 tw-h-12 tw-text-error" stroke={3} />}
|
||||||
<Card title="Measurements we use" icon="🚀" href="/docs/measurements/">
|
>
|
||||||
<p style={styles.cardp}>Documentation for all the measurements we use.</p>
|
<p className="tw-font-medium tw-text-lg tw-mb-4">
|
||||||
</Card>
|
FreeSewing is not a company. We do not sell anything. We do not have staff or
|
||||||
<Card title="Sewing terminology" icon="🧑🤝🧑" href="/docs/sewing/">
|
employees. We do not have an office. We do not get paid.
|
||||||
<p style={styles.cardp}>Documentation for various terms that are used in sewing.</p>
|
</p>
|
||||||
|
<p className="tw-font-medium tw-text-lg">
|
||||||
|
Our website does not contain any advertising. We do not track you. We do not sell your
|
||||||
|
personal data, or use it to train AI algorithms. We do not violate your privacy.
|
||||||
|
</p>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</MDXContent>
|
|
||||||
</Layout>
|
<div className="tw-text-center tw-mt-20 md:tw-mt-20">
|
||||||
|
<HowDoesItWorkAnimation />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tw-p-1 tw--mx-4 tw-bg-primary tw-bg-opacity-10 tw-mt-12 tw-rounded-none md:tw-rounded-lg lg:tw-rounded-xl md:tw-shadow-lg md:tw-mx-4 tw-p-8 lg:tw-px-12 md:tw-py-0">
|
||||||
|
<div className="tw-flex tw-flex-col md:tw-gap-8 lg:tw-gap-12 md:tw-flex md:tw-flex-row tw-m-auto">
|
||||||
|
<div className="tw--mx-4 md:tw-mx-0 md:tw-pt-8 tw-pb-8 lg:tw-py-12 tw-grow tw-m-auto tw-max-w-prose">
|
||||||
|
<SignUp embed />
|
||||||
|
</div>
|
||||||
|
<div className="tw--mx-4 md:tw-mx-0 md:tw-mt-0 tw-pt-0 md:tw-pt-8 tw-pb-8 lg:tw-py-12 tw-max-w-prose tw-m-auto tw-m-auto">
|
||||||
|
<h2 className="tw-text-inherit tw-mb-4 tw-hidden md:tw-block">Reasons to join</h2>
|
||||||
|
<ul>
|
||||||
|
{[0, 1, 2, 3].map((i) => (
|
||||||
|
<li className="tw-flex tw-flex-row tw-gap-2 tw-my-2" key={i}>
|
||||||
|
<OkIcon stroke={4} /> {reasonsToJoin[i]}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tw-grid tw-grid-cols-1 tw-gap-2 lg:tw-grid-cols-2 tw-max-w-7xl tw-my-16">
|
||||||
|
{recentBlogPosts.map((post) => (
|
||||||
|
<BlogPostTeaser
|
||||||
|
key={post.slug}
|
||||||
|
post={{
|
||||||
|
content: {
|
||||||
|
metadata: {
|
||||||
|
permalink: `/blog/${post.slug}`,
|
||||||
|
title: post.title,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tw-flex tw-flex-col md:tw-grid md:tw-grid-cols-2 tw-gap-4 tw-max-w-7xl tw-m-auto tw-mb-24">
|
||||||
|
<CardLink
|
||||||
|
Link={Link}
|
||||||
|
href="/designs"
|
||||||
|
title="Designs"
|
||||||
|
icon={<DesignIcon className="tw-w-12 tw-h-12 tw-shrink-0" />}
|
||||||
|
>
|
||||||
|
<p className="tw-font-medium tw-text-inherit tw-italic tw-text-lg">
|
||||||
|
Browse our collection of designs, and turn them into sewing patterns that are
|
||||||
|
made-to-measure just for you.
|
||||||
|
</p>
|
||||||
|
</CardLink>
|
||||||
|
<CardLink
|
||||||
|
Link={Link}
|
||||||
|
href="/showcase"
|
||||||
|
title="Showcase"
|
||||||
|
icon={<ShowcaseIcon className="tw-w-12 tw-h-12 tw-shrink-0" />}
|
||||||
|
>
|
||||||
|
<p className="tw-font-medium tw-text-inherit tw-italic tw-text-lg">
|
||||||
|
Get inspiration from the FreeSewing community, and see how others have applied their
|
||||||
|
creativity to our designs.
|
||||||
|
</p>
|
||||||
|
</CardLink>
|
||||||
|
<CardLink
|
||||||
|
Link={Link}
|
||||||
|
href="/docs/about/guide"
|
||||||
|
title="Getting Started"
|
||||||
|
icon={<DocsIcon className="tw-w-12 tw-h-12 tw-shrink-0" />}
|
||||||
|
>
|
||||||
|
<p className="tw-font-medium tw-text-inherit tw-italic tw-text-lg">
|
||||||
|
FreeSewing.org is unlike any sewing pattern website you know. Read this short guide to
|
||||||
|
get the most our of our platform.
|
||||||
|
</p>
|
||||||
|
</CardLink>
|
||||||
|
<CardLink
|
||||||
|
Link={Link}
|
||||||
|
href="/docs/about/faq"
|
||||||
|
title="Frequently Asked Questions"
|
||||||
|
icon={<HelpIcon className="tw-w-12 tw-h-12 tw-shrink-0" />}
|
||||||
|
>
|
||||||
|
<p className="tw-font-medium tw-text-inherit tw-italic tw-text-lg">
|
||||||
|
Some of the questions that come up often when people discover our platform are
|
||||||
|
answered here.
|
||||||
|
</p>
|
||||||
|
</CardLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="lg:px-4 max-w-7xl mx-auto">
|
||||||
|
<PleaseSubscribe />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="tw-flex tw-flex-col md:tw-grid md:tw-grid-cols-2 tw-gap-4 tw-max-w-7xl tw-m-auto tw-mb-24">
|
||||||
|
<CardLink
|
||||||
|
href="/newsletter"
|
||||||
|
title="FreeSewing Newsletter"
|
||||||
|
icon={<NewsletterIcon className="tw-w-12 tw-h-12 tw-shrink-0" />}
|
||||||
|
>
|
||||||
|
<p className="tw-font-medium tw-text-inherit tw-italic tw-text-lg">
|
||||||
|
Subscribe to our newsletter and once every 3 months you'll receive an email from us
|
||||||
|
with honest wholesome content. No tracking, no ads, no nonsense.
|
||||||
|
</p>
|
||||||
|
</CardLink>
|
||||||
|
<CardLink
|
||||||
|
href="/support"
|
||||||
|
title="Need Help?"
|
||||||
|
icon={<ChatIcon className="tw-w-12 tw-h-12 tw-shrink-0" />}
|
||||||
|
>
|
||||||
|
<p className="tw-font-medium tw-text-inherit tw-italic tw-text-lg">
|
||||||
|
While we are all volunteers, we have a good track record of helping people. So don't
|
||||||
|
be shy to reach out.
|
||||||
|
</p>
|
||||||
|
</CardLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DocusaurusPage>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HowDoesItWorkAnimation = () => {
|
||||||
|
const [step, setStep] = useState(0)
|
||||||
|
const [halfStep, setHalfStep] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (step > 6) setStep(0)
|
||||||
|
else setStep(step + 1)
|
||||||
|
if (halfStep > 7) setHalfStep(0)
|
||||||
|
else setHalfStep(halfStep + 0.5)
|
||||||
|
}, 800)
|
||||||
|
}, [step])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="tw-flex tw-flex-col md:tw-grid md:tw-grid-cols-3 tw-my-12">
|
||||||
|
<div className="tw-relative tw-w-full">
|
||||||
|
<div className="tw-relative tw-h-72 md:tw-h-96 tw-overflow-hidden">
|
||||||
|
{slides.map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`tw-duration-700 tw-ease-in-out tw-transition-all ${
|
||||||
|
step === i ? 'tw-opacity-1' : 'tw-opacity-0'
|
||||||
|
} tw-absolute tw-top-0 tw-text-center tw-w-full`}
|
||||||
|
>
|
||||||
|
<div className="tw-w-full tw-flex tw-flex-row tw-items-center tw-h-72 md:tw-h-96 tw-w-full tw-justify-center">
|
||||||
|
{lineDrawings[i]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Nr nr={1} />
|
||||||
|
<Title txt="Pick Any Design" />
|
||||||
|
</div>
|
||||||
|
<div className="tw-relative tw-w-full">
|
||||||
|
<div className="tw-relative tw-h-72 md:tw-h-96 tw-overflow-hidden">
|
||||||
|
{slides.map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`tw-duration-700 tw-ease-in-out tw-transition-all ${
|
||||||
|
Math.floor(halfStep) === i ? 'tw-opacity-1' : 'tw-opacity-0'
|
||||||
|
} tw-absolute tw-top-0 tw-text-center tw-w-full`}
|
||||||
|
>
|
||||||
|
<div className="tw-w-full tw-flex tw-flex-row tw-items-center tw-h-72 md:tw-h-96 tw-w-full tw-justify-center">
|
||||||
|
<img
|
||||||
|
src={`/img/models/model-${i}.png`}
|
||||||
|
className="tw-h-72 md:tw-h-96 tw-shrink-0 tw-px-8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<Nr nr={2} />
|
||||||
|
<Title txt="Add a set of measurements" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="tw-relative tw-w-full">
|
||||||
|
<div className="tw-relative tw-h-96 tw-overflow-hidden">
|
||||||
|
<div className="tw-w-full tw-flex tw-flex-row tw-items-center tw-h-72 md:tw-h-96 tw-w-full tw-justify-center">
|
||||||
|
<Pattern key={step} i={step} />
|
||||||
|
</div>
|
||||||
|
<Nr nr={3} />
|
||||||
|
<Title txt="Customize your pattern" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const reasonsToJoin = [
|
||||||
|
'Generate bespoke sewing patterns.',
|
||||||
|
'Store your patterns & measurements sets.',
|
||||||
|
'Share your creations with the community.',
|
||||||
|
'Open source. No ads. No nonsense.',
|
||||||
|
]
|
||||||
|
|
||||||
|
const lineDrawings = [
|
||||||
|
<AaronFront key={1} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
<HiFront key={2} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
<TeaganFront key={3} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
<WahidFront key={4} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
<AlbertFront key={5} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
<BruceFront key={6} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
<SimonFront key={7} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
<HortensiaFront key={8} className="tw-h-72 md:tw-h-96" />,
|
||||||
|
]
|
||||||
|
|
||||||
|
const patternTweaks = [
|
||||||
|
<path
|
||||||
|
key={1}
|
||||||
|
d="M 0,121.4 L 0,705.1 L 253.46,705.1 C 253.46,474.02 281.12,307.05 281.12,307.05 C 187.15,307.05 128.12,163.24 163.07,19.43 L 119.46,8.83 C 92.11,121.4 92.11,121.4 0,121.4 z"
|
||||||
|
/>,
|
||||||
|
<path
|
||||||
|
key={2}
|
||||||
|
d="M 0,121.4 L 0,705.1 L 253.46,705.1 C 253.46,481 279.96,321 279.96,321 C 184.87,321 126.42,170.22 163.07,19.43 L 119.46,8.83 C 92.11,121.4 92.11,121.4 0,121.4 z"
|
||||||
|
/>,
|
||||||
|
<path
|
||||||
|
key={3}
|
||||||
|
d="M 0,121.4 L 0,705.1 L 253.46,705.1 C 253.46,481 273.47,321 273.47,321 C 181.62,321 126.42,170.22 163.07,19.43 L 119.46,8.83 C 92.11,121.4 92.11,121.4 0,121.4 z"
|
||||||
|
/>,
|
||||||
|
<path
|
||||||
|
key={4}
|
||||||
|
d="M 0,121.4 L 0,742.92 L 253.46,742.92 C 253.46,481 273.47,321 273.47,321 C 181.62,321 126.42,170.22 163.07,19.43 L 119.46,8.83 C 92.11,121.4 92.11,121.4 0,121.4 z"
|
||||||
|
/>,
|
||||||
|
<path
|
||||||
|
key={5}
|
||||||
|
d="M 0,121.4 L 0,742.92 L 253.46,742.92 C 253.46,481 273.47,321 273.47,321 C 181.62,321 126.42,170.22 163.07,19.43 L 119.46,8.83 C 95.69,106.65 80.04,121.4 0,121.4 z"
|
||||||
|
/>,
|
||||||
|
<path
|
||||||
|
key={6}
|
||||||
|
d="M 0,152.02 L 0,742.92 L 253.46,742.92 C 253.46,481 273.47,321 273.47,321 C 181.62,321 126.42,170.22 163.07,19.43 L 119.46,8.83 C 89.22,133.26 73.57,152.02 0,152.02 z"
|
||||||
|
/>,
|
||||||
|
<path
|
||||||
|
key={7}
|
||||||
|
d="M 0,152.02 L 0,742.92 L 253.46,742.92 C 253.46,481 273.47,321 273.47,321 C 183.55,321 130.16,170.66 166.7,20.31 L 123.1,9.71 C 93.04,133.38 76.92,152.02 0,152.02 z"
|
||||||
|
/>,
|
||||||
|
<path
|
||||||
|
key={8}
|
||||||
|
d="M 0,152.02 L 0,742.92 L 253.46,742.92 C 253.46,481 273.47,321 273.47,321 C 181.55,321 126.27,170.2 162.92,19.39 L 126.88,10.63 C 97.02,133.5 80.4,152.02 0,152.02 z"
|
||||||
|
/>,
|
||||||
|
]
|
||||||
|
|
||||||
|
const Pattern = ({ i }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="-300 -20 850 850"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="tw-fill-primary tw-h-72 md:tw-h-96"
|
||||||
|
strokeWidth="4"
|
||||||
|
fillOpacity="0.25"
|
||||||
|
>
|
||||||
|
{patternTweaks[i]}
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Nr = ({ nr }) => (
|
||||||
|
<div className="tw-absolute tw-top-8 tw-w-full tw--ml-20">
|
||||||
|
<span className="tw-bg-primary tw-text-primary-content tw-font-bold tw-rounded-full tw-w-12 tw-h-12 tw-flex tw-items-center tw-justify-center tw-align-center tw-m-auto tw-text-3xl">
|
||||||
|
{nr}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Title = ({ txt }) => {
|
||||||
|
const shadow = `var(--ifm-background-color)`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="tw-absolute tw-top-28 tw-left-0 tw-w-full">
|
||||||
|
<h3
|
||||||
|
className="tw-text-2xl tw--rotate-12 tw-w-48 tw-text-center tw-m-auto"
|
||||||
|
style={{
|
||||||
|
textShadow: `1px 1px 1px ${shadow}, -1px -1px 1px ${shadow}, -1px 1px 1px ${shadow}, 1px -1px 1px ${shadow}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{txt}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const slides = [0, 1, 2, 3, 4, 5, 6, 7]
|
||||||
|
|
||||||
|
const RecentBlogPosts = () => (
|
||||||
|
<div>
|
||||||
|
{recentBlogPosts.map((post) => (
|
||||||
|
<p key={post.slug}>{post.title}</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
24
sites/org/src/pages/patrons/index.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Components
|
||||||
|
import Layout from '@theme/Layout'
|
||||||
|
import { DocusaurusPage } from '@freesewing/react/components/Docusaurus'
|
||||||
|
import { PleaseSubscribe } from '@freesewing/react/components/Patrons'
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Join the FreeSewing Patrons',
|
||||||
|
description:
|
||||||
|
'If you think FreeSewing is worthwhile, and if you can spare ' +
|
||||||
|
'a few coins each month without hardship, please support our work.',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PatronsPage() {
|
||||||
|
return (
|
||||||
|
<DocusaurusPage DocusaurusLayout={Layout} {...meta} Layout={false}>
|
||||||
|
<div className="tw-max-w-7xl tw-mx-auto tw-my-12 tw-px-4">
|
||||||
|
<h1>Join the FreeSewing Patrons</h1>
|
||||||
|
<div className="tw--mx-4">
|
||||||
|
<PleaseSubscribe />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DocusaurusPage>
|
||||||
|
)
|
||||||
|
}
|
24
sites/org/src/pages/patrons/join.mjs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Components
|
||||||
|
import Layout from '@theme/Layout'
|
||||||
|
import { DocusaurusPage } from '@freesewing/react/components/Docusaurus'
|
||||||
|
import { PleaseSubscribe } from '@freesewing/react/components/Patrons'
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Join the FreeSewing Patrons',
|
||||||
|
description:
|
||||||
|
'If you think FreeSewing is worthwhile, and if you can spare ' +
|
||||||
|
'a few coins each month without hardship, please support our work.',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PatronsPage() {
|
||||||
|
return (
|
||||||
|
<DocusaurusPage DocusaurusLayout={Layout} {...meta} Layout={false}>
|
||||||
|
<div className="tw-max-w-7xl tw-mx-auto tw-my-12 tw-px-4">
|
||||||
|
<h1>Join the FreeSewing Patrons</h1>
|
||||||
|
<div className="tw--mx-4">
|
||||||
|
<PleaseSubscribe />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DocusaurusPage>
|
||||||
|
)
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ const BlogPostHeader = ({ type }) => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<h1>
|
<h1>
|
||||||
<span className="block text-sm capitalize">{type}:</span>
|
<span className="tw-block tw-text-sm tw-capitalize">{type}:</span>
|
||||||
{metadata.title}
|
{metadata.title}
|
||||||
</h1>
|
</h1>
|
||||||
<BlogPostItemHeaderInfo />
|
<BlogPostItemHeaderInfo />
|
||||||
|
|
|
@ -13,9 +13,9 @@ const textShadow = {
|
||||||
|
|
||||||
const teaserClasses = `tw-absolute tw-bottom-4 tw-right-0 tw-ml-3
|
const teaserClasses = `tw-absolute tw-bottom-4 tw-right-0 tw-ml-3
|
||||||
tw-rounded-l md:tw-rounded-l-lg tw-bg-neutral tw-bg-opacity-80 tw-p-1 tw-px-4 tw-font-medium
|
tw-rounded-l md:tw-rounded-l-lg tw-bg-neutral tw-bg-opacity-80 tw-p-1 tw-px-4 tw-font-medium
|
||||||
tw-text-neutral-content tw-text-right tw-text-sm md:tw-text-lg`
|
tw-text-neutral-content tw-text-right tw-text-sm md:tw-text-lg lg:tw-text-xl`
|
||||||
|
|
||||||
const BlogPostTeaser = ({ post }) => (
|
export const BlogPostTeaser = ({ post }) => (
|
||||||
<Link
|
<Link
|
||||||
className="tw-aspect-video tw-relative tw-shadow tw-rounded-lg"
|
className="tw-aspect-video tw-relative tw-shadow tw-rounded-lg"
|
||||||
href={post.content.metadata.permalink}
|
href={post.content.metadata.permalink}
|
||||||
|
|
BIN
sites/org/static/img/models/model-0.png
Normal file
After Width: | Height: | Size: 267 KiB |
BIN
sites/org/static/img/models/model-1.png
Normal file
After Width: | Height: | Size: 298 KiB |
BIN
sites/org/static/img/models/model-2.png
Normal file
After Width: | Height: | Size: 271 KiB |
BIN
sites/org/static/img/models/model-3.png
Normal file
After Width: | Height: | Size: 188 KiB |
BIN
sites/org/static/img/models/model-4.png
Normal file
After Width: | Height: | Size: 298 KiB |
BIN
sites/org/static/img/models/model-5.png
Normal file
After Width: | Height: | Size: 261 KiB |
BIN
sites/org/static/img/models/model-6.png
Normal file
After Width: | Height: | Size: 288 KiB |
BIN
sites/org/static/img/models/model-7.png
Normal file
After Width: | Height: | Size: 227 KiB |