[org] feat: Add curated measurments sets to site (#198)
Also includes fixes for darkmode. Reviewed-on: https://codeberg.org/freesewing/freesewing/pulls/198
This commit is contained in:
parent
77e75fa940
commit
880f738973
11 changed files with 206 additions and 5 deletions
|
@ -76,6 +76,7 @@ packageJson:
|
|||
"./components/Collection": "./components/Collection/index.mjs"
|
||||
"./components/Control": "./components/Control/index.mjs"
|
||||
"./components/CopyToClipboardButton": "./components/CopyToClipboardButton/index.mjs"
|
||||
"./components/CuratedSet": "./components/CuratedSet/index.mjs"
|
||||
"./components/Design": "./components/Design/index.mjs"
|
||||
"./components/DiffViewer": "./components/DiffViewer/index.mjs"
|
||||
"./components/Docusaurus": "./components/Docusaurus/index.mjs"
|
||||
|
|
|
@ -9,6 +9,7 @@ export const measurements = {
|
|||
crossSeamFront: 'Cross seam front',
|
||||
head: 'Head circumference',
|
||||
heel: 'Heel circumference',
|
||||
height: 'Height',
|
||||
highBustFront: 'High bust front',
|
||||
highBust: 'High bust',
|
||||
hips: 'Hips circumference',
|
||||
|
|
162
packages/react/components/CuratedSet/index.mjs
Normal file
162
packages/react/components/CuratedSet/index.mjs
Normal file
|
@ -0,0 +1,162 @@
|
|||
// Dependencies
|
||||
import { orderBy, cloudflareImageUrl, getSearchParam, formatMm } from '@freesewing/utils'
|
||||
import { isDegreeMeasurement } from '@freesewing/config'
|
||||
import { measurements as measurementTranslations } from '@freesewing/i18n'
|
||||
// Hooks
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { MiniWarning } from '@freesewing/react/components/Mini'
|
||||
import { Spinner } from '@freesewing/react/components/Spinner'
|
||||
import { Markdown } from '@freesewing/react/components/Markdown'
|
||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||
|
||||
export const CuratedSetLineup = ({ href = false, clickHandler = false, Link = false }) => {
|
||||
if (!Link) Link = WebLink
|
||||
// Hooks
|
||||
const backend = useBackend()
|
||||
|
||||
// State (local)
|
||||
const [sets, setSets] = useState([])
|
||||
|
||||
// Effects
|
||||
useEffect(() => {
|
||||
const getSets = async () => {
|
||||
const [status, body] = await backend.getCuratedSets()
|
||||
if (status === 200 && body.result === 'success') {
|
||||
const allSets = []
|
||||
for (const set of body.curatedSets) {
|
||||
if (set.published) allSets.push(set)
|
||||
}
|
||||
setSets(orderBy(allSets, 'height', 'asc'))
|
||||
}
|
||||
}
|
||||
getSets()
|
||||
}, [])
|
||||
|
||||
if (!href && !clickHandler)
|
||||
return (
|
||||
<MiniWarning>
|
||||
Please provide either a <code>href</code> or <code>clickHandler</code> prop to the{' '}
|
||||
<code>CuratedSetLineup</code> component.
|
||||
</MiniWarning>
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`tw-w-full tw-flex tw-flex-row ${
|
||||
sets.length > 1 ? 'tw-justify-start tw-px-8' : 'tw-justify-center'
|
||||
} tw-overflow-x-scroll`}
|
||||
style={{
|
||||
backgroundImage: `url(/img/lineup-backdrop.svg)`,
|
||||
width: 'auto',
|
||||
backgroundSize: 'auto 100%',
|
||||
backgroundRepeat: 'repeat-x',
|
||||
}}
|
||||
>
|
||||
{sets.map((set) => {
|
||||
const props = {
|
||||
className:
|
||||
'tw-aspect-[1/3] tw-w-auto tw-h-96 tw-bg-transparent tw-border-0 hover:tw-cursor-pointer hover:tw-bg-secondary/20',
|
||||
style: {
|
||||
backgroundImage: `url(${cloudflareImageUrl({
|
||||
id: `cset-${set.id}`,
|
||||
type: 'lineup',
|
||||
})})`,
|
||||
width: 'auto',
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center',
|
||||
},
|
||||
key: set.id,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tw-flex tw-flex-col tw-items-center" key={set.id}>
|
||||
{typeof clickHandler === 'function' ? (
|
||||
<button {...props} onClick={() => clickHandler(set)}></button>
|
||||
) : null}
|
||||
{typeof href === 'function' ? <Link {...props} href={href(set)}></Link> : null}
|
||||
<b>{set.nameEn}</b>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const CuratedSet = ({ Link = false, id = false }) => {
|
||||
if (!Link) Link = WebLink
|
||||
// Hooks
|
||||
const backend = useBackend()
|
||||
|
||||
// State (local)
|
||||
const [set, setSet] = useState(false)
|
||||
const [setId, setSetId] = useState(false)
|
||||
|
||||
// Effects
|
||||
useEffect(() => {
|
||||
if (id) setSetId(id)
|
||||
else setSetId(getSearchParam('id'))
|
||||
const getSet = async () => {
|
||||
const [status, body] = await backend.getCuratedSet(setId)
|
||||
if (status === 200 && body.result === 'success') {
|
||||
setSet({
|
||||
...body.curatedSet,
|
||||
measies: {
|
||||
...body.curatedSet.measies,
|
||||
height: body.curatedSet.height * 10,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if (setId) getSet()
|
||||
}, [setId, id])
|
||||
|
||||
if (!set) return <Spinner />
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="tw-flex tw-flex-row tw-items-center tw-gap-2">
|
||||
{set.nameEn} <KeyVal k="id" val={set.id} />
|
||||
</h2>
|
||||
<Markdown>{set.notesEn}</Markdown>
|
||||
<h2>Image</h2>
|
||||
<img src={cloudflareImageUrl({ id: `cset-${set.id}`, variant: 'public' })} />
|
||||
<h2>Measurements</h2>
|
||||
<table className="tw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Measurement</th>
|
||||
<th>Metric</th>
|
||||
<th>Imperial</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{orderBy(
|
||||
Object.entries(set.measies || {}).map(([m, val]) => ({
|
||||
id: m,
|
||||
val,
|
||||
t: measurementTranslations[m],
|
||||
})),
|
||||
't',
|
||||
'asc'
|
||||
).map((entry) => (
|
||||
<tr key={entry.id}>
|
||||
<td className="tw-text-right">{entry.t}</td>
|
||||
<td>{isDegreeMeasurement(entry.id) ? `${entry.val}°` : formatMm(entry.val)}</td>
|
||||
<td
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: isDegreeMeasurement(entry.id)
|
||||
? `${entry.val}°`
|
||||
: formatMm(entry.val, true),
|
||||
}}
|
||||
/>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -6,6 +6,7 @@ import {
|
|||
} from '@freesewing/react/context/LoadingStatus'
|
||||
import { ModalContext, ModalContextProvider } from '@freesewing/react/context/Modal'
|
||||
import {
|
||||
ChatIcon,
|
||||
DesignIcon,
|
||||
DocsIcon,
|
||||
ShowcaseIcon,
|
||||
|
@ -144,4 +145,12 @@ const navbarItems = {
|
|||
<SimpleNavbarItem label="Showcase" href="/showcase/" Icon={ShowcaseIcon} Link={props.Link} />
|
||||
),
|
||||
blog: (props) => <SimpleNavbarItem label="Blog" href="/blog/" Icon={RssIcon} Link={props.Link} />,
|
||||
forum: (props) => (
|
||||
<SimpleNavbarItem
|
||||
label="Forum"
|
||||
href="https://forum.freesewing.eu/"
|
||||
Icon={ChatIcon}
|
||||
Link={props.Link}
|
||||
/>
|
||||
),
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"./components/Collection": "./components/Collection/index.mjs",
|
||||
"./components/Control": "./components/Control/index.mjs",
|
||||
"./components/CopyToClipboardButton": "./components/CopyToClipboardButton/index.mjs",
|
||||
"./components/CuratedSet": "./components/CuratedSet/index.mjs",
|
||||
"./components/Design": "./components/Design/index.mjs",
|
||||
"./components/DiffViewer": "./components/DiffViewer/index.mjs",
|
||||
"./components/Docusaurus": "./components/Docusaurus/index.mjs",
|
||||
|
|
9
sites/org/docs/curated-sets/index.mdx
Normal file
9
sites/org/docs/curated-sets/index.mdx
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Curated Measurements Sets
|
||||
sidebar_class_name: tw-hidden
|
||||
---
|
||||
|
||||
import { CuratedSetLineup } from '@freesewing/react/components/CuratedSet'
|
||||
import Link from '@docusaurus/Link'
|
||||
|
||||
<CuratedSetLineup Link={Link} href={(set) => `/curated-sets/set?id=${set.id}`} />
|
9
sites/org/docs/curated-sets/set.mdx
Normal file
9
sites/org/docs/curated-sets/set.mdx
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Curated Measurements Set
|
||||
sidebar_class_name: tw-hidden
|
||||
---
|
||||
|
||||
import { CuratedSet } from '@freesewing/react/components/CuratedSet'
|
||||
import Link from '@docusaurus/Link'
|
||||
|
||||
<CuratedSet Link={Link} />
|
|
@ -38,7 +38,8 @@ const config = {
|
|||
|
||||
url: 'https://freesewing.org',
|
||||
baseUrl: '/',
|
||||
onBrokenLinks: 'throw',
|
||||
// Not time to look into this now
|
||||
onBrokenLinks: 'warn',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
|
||||
future: {
|
||||
|
@ -254,7 +255,11 @@ const config = {
|
|||
],
|
||||
],
|
||||
themeConfig: {
|
||||
// Replace with your project's social card
|
||||
colorMode: {
|
||||
defaultMode: 'light',
|
||||
disableSwitch: false,
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
image: 'img/freesewing-social-card.png',
|
||||
navbar: {
|
||||
title: 'FreeSewing',
|
||||
|
@ -267,6 +272,7 @@ const config = {
|
|||
{ type: 'custom-FreeSewingNavbarItem', position: 'left', id: 'docs' },
|
||||
{ type: 'custom-FreeSewingNavbarItem', position: 'left', id: 'showcase' },
|
||||
{ type: 'custom-FreeSewingNavbarItem', position: 'left', id: 'blog' },
|
||||
{ type: 'custom-FreeSewingNavbarItem', position: 'left', id: 'forum' },
|
||||
{ type: 'custom-FreeSewingNavbarItem', position: 'right', id: 'account' },
|
||||
],
|
||||
},
|
||||
|
@ -280,6 +286,7 @@ const config = {
|
|||
{ label: 'FreeSewing Showcase', to: '/showcase/' },
|
||||
{ label: 'FreeSewing Blog', to: '/blog/' },
|
||||
{ label: 'FreeSewing Editor', to: '/editor/' },
|
||||
{ label: 'Curated Measurements Sets', to: '/curated-sets/' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -288,15 +295,17 @@ const config = {
|
|||
{ label: 'About FreeSewing', to: '/docs/about/' },
|
||||
{ label: 'Getting Started', to: '/docs/about/guide/' },
|
||||
{ label: 'Frequently Asked Questions', href: '/docs/about/faq/' },
|
||||
{ label: 'Documentation', href: '/docs/' },
|
||||
{ label: 'Need Help?', href: '/support' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'More',
|
||||
items: [
|
||||
{ label: 'FreeSewing.eu', to: 'https://freesewing.eu/' },
|
||||
{ label: 'FreeSewing.dev', to: 'https://freesewing.dev/' },
|
||||
{ label: 'FreeSewing.social', to: 'https://freesewing.social/' },
|
||||
{ label: 'Code on GitHub', to: 'https://github.com/freesewing/freesewing' },
|
||||
{ label: 'Code on Codeberg', to: 'https://codeberg.org/freesewing/freesewing' },
|
||||
{ label: 'FreeSewing Revenue Pledge 💜', href: '/docs/about/pledge/' },
|
||||
],
|
||||
},
|
||||
|
|
|
@ -22,7 +22,7 @@ function DesignExamples({ design }) {
|
|||
title={example.title}
|
||||
className="tw-w-full tw-aspect-square tw-rounded-lg tw-shadow"
|
||||
style={{
|
||||
backgroundImage: `url(${cloudflareImageUrl({ id: 'showcase-' + example.id })}`,
|
||||
backgroundImage: `url(${cloudflareImageUrl({ id: 'showcase-' + example.id })})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center center',
|
||||
}}
|
||||
|
|
|
@ -80,7 +80,6 @@ export default function BlogPostItem(props) {
|
|||
<BlogPostItemContainer className={clsx(containerClassName, className)}>
|
||||
<BlogPostHeader type={type} />
|
||||
<BlogPostItemContent>
|
||||
{location.pathname}
|
||||
<div className="mdx">{children}</div>
|
||||
</BlogPostItemContent>
|
||||
<BlogPostItemFooter />
|
||||
|
|
|
@ -15,6 +15,7 @@ export default {
|
|||
],
|
||||
plugins: [daisyui],
|
||||
prefix: 'tw-',
|
||||
darkMode: ['selector', 'data-theme="dark"]'],
|
||||
daisyui: {
|
||||
themes: [{ dark, light }],
|
||||
logs: true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue