1
0
Fork 0

feat: Upgrade to TailwindCSS 4 & DaisyUI 5 (#263)

Also fixes #251

Reviewed-on: https://codeberg.org/freesewing/freesewing/pulls/263
Co-authored-by: joostdecock <joost@joost.at>
Co-committed-by: joostdecock <joost@joost.at>
This commit is contained in:
joostdecock 2025-04-18 08:07:13 +00:00 committed by Joost De Cock
parent a2863e5158
commit 44e04a4cef
164 changed files with 2361 additions and 2658 deletions

View file

@ -102,8 +102,8 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
return (
<>
<div className="tw-max-w-7xl tw-m-auto">
<div className="tw-flex tw-flex-row tw-flex-wrap tw-gap-1 tw-justify-center tw-font-medium tw-mb-2">
<div className="tw:max-w-7xl tw:m-auto">
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 tw:justify-center tw:font-medium tw:mb-2">
{Object.keys(filtered)
.sort()
.map((d) =>
@ -111,7 +111,7 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
<button
key={d}
onClick={() => onClick(d)}
className="tw-text-secondary tw-decoration-2 tw-underline tw-capitalize hover:tw-decoration-4 hover:tw-text-secondary tw-bg-transparent tw-border-0 tw-font-medium tw-p-0 tw-text-base hover:tw-cursor-pointer"
className="tw:text-secondary tw:decoration-2 tw:underline tw:capitalize tw:hover:decoration-4 tw:hover:text-secondary tw:bg-transparent tw:border-0 tw:font-medium tw:p-0 tw:text-base tw:hover:cursor-pointer"
>
{d}
</button>
@ -119,7 +119,7 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
<Link
key={d}
href={linkBuilders[linkTo](d)}
className="tw-text-secondary tw-decoration-2 tw-underline tw-capitalize hover:tw-decoration-4 hover:tw-text-secondary"
className="tw:text-secondary tw:decoration-2 tw:underline tw:capitalize tw:hover:decoration-4 tw:hover:text-secondary"
>
{d}
</Link>
@ -128,19 +128,19 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
</div>
{showFilters ? (
<>
<h6 className="tw-text-center tw-mb-0 tw-mt-4">
<h6 className="tw:text-center tw:mb-0 tw:mt-4">
Filters ({Object.keys(filtered).length}/{collection.length})
</h6>
<div className="tw-flex tw-flex-row tw-gap-1 tw-items-center tw-justify-center tw-flex-wrap tw-my-2">
<div className="tw:flex tw:flex-row tw:gap-1 tw:items-center tw:justify-center tw:flex-wrap tw:my-2">
<b>Tags:</b>
{tags.map((tag) => (
<button
key={tag}
className={`tw-daisy-badge tw-font-medium hover:tw-shadow hover:tw-cursor-pointer
className={`tw:daisy-badge tw:font-medium tw:hover:shadow tw:hover:cursor-pointer
${
filter?.tag && Array.isArray(filter.tag) && filter.tag.includes(tag)
? 'tw-daisy-badge-success hover:tw-daisy-badge-error'
: 'tw-daisy-badge-primary hover:tw-daisy-badge-success'
? 'tw:daisy-badge-success hover:tw:daisy-badge-error'
: 'tw:daisy-badge-primary hover:tw:daisy-badge-success'
}`}
onClick={() => toggle('tag', tag)}
>
@ -148,16 +148,16 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
</button>
))}
</div>
<div className="tw-flex tw-flex-row tw-gap-1 tw-items-center tw-justify-center tw-flex-wrap tw-my-4">
<div className="tw:flex tw:flex-row tw:gap-1 tw:items-center tw:justify-center tw:flex-wrap tw:my-4">
<b>Techniques</b>
{techniques.sort().map((tech) => (
<button
key={tech}
className={`tw-daisy-badge tw-font-medium hover:tw-shadow
className={`tw:daisy-badge tw:font-medium tw:hover:shadow
${
filter?.tech && Array.isArray(filter.tech) && filter.tech.includes(tech)
? 'tw-daisy-badge tw-daisy-badge-success hover:tw-daisy-badge-error'
: 'tw-daisy-badge tw-daisy-badge-accent hover:tw-daisy-badge-success'
? 'tw:daisy-badge tw:daisy-badge-success hover:tw:daisy-badge-error'
: 'tw:daisy-badge tw:daisy-badge-accent hover:tw:daisy-badge-success'
}`}
onClick={() => toggle('tech', tech)}
>
@ -165,39 +165,39 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
</button>
))}
</div>
<div className="tw-flex tw-flex-row tw-gap-2 tw-items-center tw-justify-center tw-flex-wrap tw-my-4">
<div className="tw:flex tw:flex-row tw:gap-2 tw:items-center tw:justify-center tw:flex-wrap tw:my-4">
<b>Difficulty:</b>
{[1, 2, 3, 4, 5].map((score) => (
<button
onClick={() => updateFilter('difficulty', score)}
key={score}
className={`tw-daisy-btn tw-daisy-btn-sm ${
className={`tw:daisy-btn tw:daisy-btn-sm ${
filter.difficulty === score
? 'tw-daisy-btn-secondary tw-daisy-btn-outline'
: 'tw-daisy-btn-ghost'
? 'tw:daisy-btn-secondary tw:daisy-btn-outline'
: 'tw:daisy-btn-ghost'
}`}
>
<Difficulty score={score} />
</button>
))}
</div>
<div className="tw-flex tw-flex-row tw-gap-4 tw-items-center tw-justify-center tw-flex-wrap tw-my-2">
<div className="tw:flex tw:flex-row tw:gap-4 tw:items-center tw:justify-center tw:flex-wrap tw:my-2">
<button
className="tw-daisy-btn tw-daisy-btn-secondary tw-daisy-btn-outline"
className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
onClick={() => updateFilter('example', !filter.example)}
>
{filter.example ? <CisFemaleIcon /> : <ShowcaseIcon />}
{filter.example ? 'Show Line Drawings' : 'Show Examples'}
</button>
<button
className="tw-daisy-btn tw-daisy-btn-secondary tw-daisy-btn-outline"
className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
onClick={() => setFilter({ example: 1 })}
>
<ResetIcon />
Clear Filter
</button>
<button
className="tw-daisy-btn tw-daisy-btn-secondary tw-daisy-btn-outline"
className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
onClick={() => setShowFilters(false)}
>
<FilterIcon />
@ -206,16 +206,16 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
</div>
</>
) : (
<div className="tw-flex tw-flex-row tw-gap-4 tw-items-center tw-justify-center tw-flex-wrap tw-my-2">
<div className="tw:flex tw:flex-row tw:gap-4 tw:items-center tw:justify-center tw:flex-wrap tw:my-2">
<button
className="tw-daisy-btn tw-daisy-btn-secondary tw-daisy-btn-outline"
className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
onClick={() => updateFilter('example', !filter.example)}
>
{filter.example ? <CisFemaleIcon /> : <ShowcaseIcon />}
{filter.example ? 'Show Line Drawings' : 'Show Examples'}
</button>
<button
className="tw-daisy-btn tw-daisy-btn-secondary tw-daisy-btn-outline"
className="tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline"
onClick={() => setShowFilters(true)}
>
<FilterIcon />
@ -225,7 +225,7 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
)}
</div>
<div
className={`tw-grid tw-grid-cols-2 tw-gap-2 tw-mt-4 tw-justify-center sm:tw-grid-cols-3 md:tw-grid-cols-4 ${editor ? 'lg:tw-grid-cols-6 2xl:tw-grid-cols-12' : ''} tw-mb-8`}
className={`tw:grid tw:grid-cols-2 tw:gap-2 tw:mt-4 tw:justify-center tw:sm:grid-cols-3 tw:md:grid-cols-4 ${editor ? 'tw:lg:grid-cols-6 tw:2xl:grid-cols-12' : ''} tw:mb-8`}
>
{Object.keys(filtered)
.sort()
@ -253,7 +253,7 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
const Technique = ({ Link = WebLink, technique }) => (
<Link
href={`/designs/techniques/${technique}`}
className="tw-daisy-badge tw-daisy-badge-accent hover:tw-daisy-badge-secondary hover:tw-shadow tw-font-medium"
className="tw:daisy-badge tw:daisy-badge-accent hover:tw:daisy-badge-secondary tw:hover:shadow tw:font-medium"
>
{technique}
</Link>
@ -269,7 +269,7 @@ const Technique = ({ Link = WebLink, technique }) => (
const Tag = ({ Link = WebLink, technique }) => (
<Link
href={`/designs/tags/${tag}`}
className="tw-daisy-badge tw-daisy-badge-primary hover:tw-daisy-badge-secondary hover:tw-shadow tw-font-medium"
className="tw:daisy-badge tw:daisy-badge-primary hover:tw:daisy-badge-secondary tw:hover:shadow tw:font-medium"
>
{tag}
</Link>
@ -292,28 +292,28 @@ const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
const inner = (
<div
className={`tw-flex tw-flex-col tw-flex-nowrap tw-items-start tw-justify-between tw-gap-2 tw-border-neutral-500 group-hover:tw-border-secondary
tw-w-full tw-h-full tw-border tw-border-2 tw-border-solid tw-p-0 tw-relative tw-rounded-lg tw-rounded-lg`}
className={`tw:flex tw:flex-col tw:flex-nowrap tw:items-start tw:justify-between tw:gap-2 tw:border-neutral-500 tw:group-hover:border-secondary
tw:w-full tw:h-full tw:border tw:border-2 tw:border-solid tw:p-0 tw:relative tw:rounded-lg tw:rounded-lg`}
style={bg}
>
<h5
className={`tw-text-center tw-py-2 tw-px-4 tw-rounded-t tw-m-0 tw-w-full group-hover:tw-no-underline group-hover:tw-bg-secondary group-hover:tw-bg-opacity-70
${lineDrawing ? '' : 'tw-bg-neutral tw-text-neutral-content tw-bg-opacity-80'}`}
className={`tw:text-center tw:py-2 tw:px-4 tw:rounded-t tw:m-0 tw:w-full tw:group-hover:no-underline tw:group-hover:bg-secondary/70
${lineDrawing ? '' : 'tw:bg-neutral/80 tw:text-neutral-content'}`}
>
{about[name].name}
</h5>
{lineDrawing ? (
<div className="tw-p-1 tw-grow tw-w-full tw-h-auto tw-square tw-text-center">
<LineDrawing className="tw-max-w-full tw-m-auto tw-my-4 tw-text-base-content" />
<div className="tw:p-1 tw:grow tw:w-full tw:h-auto tw:square tw:text-center">
<LineDrawing className="tw:max-w-full tw:m-auto tw:my-4 tw:text-base-content" />
</div>
) : (
<span />
)}
<div
className={`tw-flex tw-flex-row tw-items-center tw-justify-center tw-py-1 tw-px-2 tw-rounded-b tw-m-0 tw-w-full
${lineDrawing ? '' : `tw-text-neutral-content`}`}
className={`tw:flex tw:flex-row tw:items-center tw:justify-center tw:py-1 tw:px-2 tw:rounded-b tw:m-0 tw:w-full
${lineDrawing ? '' : `tw:text-neutral-content`}`}
>
<Difficulty score={about[name].difficulty} className="group-hover:tw-text-secondary" />
<Difficulty score={about[name].difficulty} className="tw:group-hover:text-secondary" />
</div>
</div>
)
@ -321,7 +321,7 @@ const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
return onClick ? (
<button
onClick={() => onClick(name)}
className="hover:tw-bg-secondary hover:tw-bg-opacity-10 tw-rounded-lg tw-group hover:tw-no-underline tw-bg-transparent tw-border-0 hover:tw-cursor-pointer tw-p-0"
className="tw:hover:bg-secondary/10 tw:rounded-lg tw:group tw:hover:no-underline tw:bg-transparent tw:border-0 tw:hover:cursor-pointer tw:p-0"
title={about[name].description}
>
{inner}
@ -329,7 +329,7 @@ const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
) : (
<Link
href={linkBuilders[linkTo](name)}
className="hover:tw-bg-secondary hover:tw-bg-opacity-10 tw-rounded-lg tw-group hover:tw-no-underline"
className="tw:hover:bg-secondary/10 tw:rounded-lg tw:group tw:hover:no-underline"
title={about[name].description}
>
{inner}
@ -344,9 +344,9 @@ const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
* @param {number} props.score - The difficulty score of the design (1-5)
*/
const Difficulty = ({ score = 0, className = '' }) => (
<div className={`tw-flex tw-flex-row tw-items-center ${className}`}>
<div className={`tw:flex tw:flex-row tw:items-center ${className}`}>
{[0, 1, 2, 3, 4].map((i) => (
<CircleIcon key={i} fill={i < score ? true : false} className={`tw-w-4 tw-h-4`} />
<CircleIcon key={i} fill={i < score ? true : false} className={`tw:w-4 tw:h-4`} />
))}
</div>
)
@ -400,9 +400,9 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
}
const makeButton = (
<div className={`tw-grid tw-grid-cols-1 tw-gap-2 tw-mb-4`}>
<div className={`tw:grid tw:grid-cols-1 tw:gap-2 tw:mb-4`}>
<IconButton href={`/editor/#s={"design"%3A"${design}"%2C"view"%3A"draft"}`} color="primary">
<NewPatternIcon className="tw-w-8 tw-h-8" />
<NewPatternIcon className="tw:w-8 tw:h-8" />
New {capitalize(design)} pattern
</IconButton>
</div>
@ -410,9 +410,9 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
const buttons = noDocsLink ? (
makeButton
) : (
<div className={`tw-grid tw-grid-cols-1 lg:tw-grid-cols-2 tw-gap-2 tw-mb-4`}>
<div className={`tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2 tw:mb-4`}>
<IconButton href={`/docs/designs/${design}`} color="secondary">
<DocsIcon className="tw-w-8 tw-h-8" />
<DocsIcon className="tw:w-8 tw:h-8" />
Documentation
</IconButton>
{makeButton}
@ -421,15 +421,15 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
return (
<>
<div className="lg:tw-hidden">{buttons}</div>
<div className={`tw-grid tw-grid-cols-1 lg:tw-grid-cols-2 tw-gap-2`}>
<div className="tw-relative">
<div className="tw tw-top-0 tw-left-0">
<div className="tw:lg:hidden">{buttons}</div>
<div className={`tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2`}>
<div className="tw:relative">
<div className="tw tw:top-0 tw:left-0">
{back ? <LineDrawingBack /> : <LineDrawing />}
</div>
{LineDrawingBack ? (
<button
className="tw-absolute tw-top-2 tw-right-4 tw-start-auto tw-daisy-btn tw-daisy-btn-neutral tw-daisy-btn-outline tw-daisy-btn-xs"
className="tw:absolute tw:top-2 tw:right-4 tw:start-auto tw:daisy-btn tw:daisy-btn-neutral tw:daisy-btn-outline tw:daisy-btn-xs"
onClick={() => setBack(!back)}
>
{back ? 'Front' : 'Back'} view
@ -437,11 +437,11 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
) : null}
</div>
<div className="">
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">Description</div>
<span className="tw-text-xl">{about[design].description}</span>
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Description</div>
<span className="tw:text-xl">{about[design].description}</span>
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">By</div>
<div className="tw-flex tw-flex-row tw-flex-wrap tw-gap-1 items-center">
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">By</div>
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
{codeBy.map((code) => (
<KeyVal key={code} k="code" val={code} color="secondary" />
))}
@ -450,12 +450,12 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
))}
</div>
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">Difficulty</div>
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Difficulty</div>
<Difficulty score={about[design].difficulty} />
{optionalMeasurements[design].length > 0 ? (
<>
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">
Optional Measurements
</div>
<div className="">
@ -477,7 +477,7 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
{requiredMeasurements[design].length > 0 ? (
<>
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">
Required Measurements
</div>
<div className="">
@ -497,24 +497,24 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
</>
) : null}
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">Tags</div>
<div className="tw-flex tw-flex-row tw-flex-wrap tw-gap-1 items-center">
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Tags</div>
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
{tags.map((tag) => (
<Link
key={tag}
className="tw-daisy-badge tw-daisy-badge-primary tw-font-medium hover:tw-shadow hover:tw-cursor-pointer"
className="tw:daisy-badge tw:daisy-badge-primary tw:font-medium tw:hover:shadow tw:hover:cursor-pointer"
href={`/designs/#filter={"example"%3Atrue%2C"tag"%3A["${tag}"]}`}
>
{tag}
</Link>
))}
</div>
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">Techniques</div>
<div className="tw-flex tw-flex-row tw-flex-wrap tw-gap-1 items-center">
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Techniques</div>
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
{techniques.map((tech) => (
<Link
key={tech}
className="tw-daisy-badge tw-daisy-badge-accent tw-font-medium hover:tw-shadow hover:tw-cursor-pointer"
className="tw:daisy-badge tw:daisy-badge-accent tw:font-medium tw:hover:shadow tw:hover:cursor-pointer"
href={`/designs/#filter={"example"%3Atrue%2C"tag"%3A["${tech}"]}`}
>
{tech}
@ -522,8 +522,8 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
))}
</div>
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">Examples</div>
<div className="tw-flex tw-flex-row tw-flex-wrap tw-gap-1 items-center">
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Examples</div>
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
<KeyVal
k="FreeSewing"
val="showcase"
@ -539,8 +539,8 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
/>
</div>
<div className="tw-mt-2 tw-text-sm tw-opacity-70 tw-font-medium">Documentation</div>
<div className="tw-flex tw-flex-row tw-flex-wrap tw-gap-1 items-center">
<div className="tw:mt-2 tw:text-sm tw:opacity-70 tw:font-medium">Documentation</div>
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-1 items-center">
<Link href={`/docs/designs/${design}/#notes`}>Designer Notes</Link>,
<Link href={`/docs/designs/${design}/#needs`}>What You Need</Link>,
<Link href={`/docs/designs/${design}/#fabric`}>Fabric Options</Link>,
@ -548,7 +548,7 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
<Link href={`/docs/designs/${design}/options/`}>Design Options</Link>,
<Link href={`/docs/designs/${design}/instructions/`}>Sewing Instructions</Link>
</div>
<div className="tw-my-4">{buttons}</div>
<div className="tw:my-4">{buttons}</div>
</div>
</div>
</>
@ -572,391 +572,3 @@ const SharingIsCaring = ({ design }) => (
<p>If you like, you can copy the hashtag below:</p>
</>
)
/*
// __SDEFILE__ - This file is a dependency for the stand-alone environment
// Dependencies
import {
nsMerge,
capitalize,
optionsMenuStructure,
optionType,
cloudflareImageUrl,
horFlexClasses,
} from 'shared/utils.mjs'
import { designs } from 'shared/config/designs.mjs'
import { examples } from 'site/components/design-examples.mjs'
// Hooks
import { useTranslation } from 'next-i18next'
import { useDesign } from 'site/hooks/use-design.mjs'
import { useContext, Fragment } from 'react'
// Context
import { ModalContext } from 'shared/context/modal-context.mjs'
// Components
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
import { lineDrawings } from 'shared/components/designs/linedrawings/index.mjs'
import { Difficulty } from 'shared/components/designs/difficulty.mjs'
import { PageLink, AnchorLink, Link } from 'shared/components/link.mjs'
import { DocsLink, DocsTitle } from 'shared/components/mdx/docs-helpers.mjs'
import { Popout } from 'shared/components/popout/index.mjs'
import { NewPatternIcon, DocsIcon } from 'shared/components/icons.mjs'
import { DynamicMdx } from 'shared/components/mdx/dynamic.mjs'
// Translation namespaces used on this page
export const ns = nsMerge(
'account',
'tags',
'techniques',
'measurements',
'workbench',
'designs',
'tags'
)
const Option = ({ id, option, design }) =>
optionType(option) === 'constant' ? null : (
<li key={option.name}>
<DocsLink site="org" slug={`docs/designs/${design}/options/${id.toLowerCase()}`} />
</li>
)
const OptionGroup = ({ id, group, t, design }) => (
<li key={id}>
<b>{t(`workbench:${id}`)}</b>
<ul className="list list-inside list-disc pl-2">
{Object.entries(group).map(([sid, entry]) =>
entry.isGroup ? (
<OptionGroup id={sid} key={sid} t={t} group={entry} design={design} />
) : (
<Option key={sid} id={sid} option={entry} design={design} />
)
)}
</ul>
</li>
)
export const SimpleOptionsList = ({ options, t, design }) => {
const structure = optionsMenuStructure(options, {}, true)
const output = []
for (const [key, entry] of Object.entries(structure)) {
const shared = { key, t, design, id: key }
if (entry.isGroup) output.push(<OptionGroup {...shared} group={entry} />)
else output.push(<Option {...shared} option={entry} />)
}
return <ul className="list list-inside pl-2 list-disc">{output}</ul>
}
export const DesignInfo = ({ design, docs = false, workbench = false, modal = false }) => {
const { setModal } = useContext(ModalContext)
const { t, i18n } = useTranslation([...ns, design])
const { language } = i18n
const Design = useDesign(design)
const config = Design.patternConfig
// Translate measurements
const measies = { required: {}, optional: {} }
if (config?.measurements) {
for (const m of config.measurements) measies.required[m] = t(`measurements:${m}`)
}
if (config?.optionalMeasurements) {
for (const m of config.optionalMeasurements) measies.optional[m] = t(`measurements:${m}`)
}
// Linedrawing
const LineDrawing = lineDrawings[design]
? lineDrawings[design]
: ({ className }) => <div className={className}></div>
// Docs content
const docsContent = (
<>
<h2 id="docs">{t('account:docs')}</h2>
<ul className="list list-disc list-inside pl-2">
<li>
<DocsLink site="org" slug={`docs/designs/${design}`} />
</li>
<li>
<DocsLink site="org" slug={`docs/designs/${design}/cutting`} />
</li>
<li>
<DocsLink site="org" slug={`docs/designs/${design}/instructions`} />
</li>
<li>
<DocsLink site="org" slug={`docs/designs/${design}/needs`} />
</li>
<li>
<DocsLink site="org" slug={`docs/designs/${design}/fabric`} />
</li>
{Object.keys(config.options).length > 0 ? (
<li>
<DocsLink site="org" slug={`docs/designs/${design}/options`} />
</li>
) : null}
<li>
<DocsLink site="org" slug={`docs/designs/${design}/notes`} />
</li>
</ul>
</>
)
return (
<>
<h5 className="-mt-6 text-accent font-medium">#FreeSewing{capitalize(design)}</h5>
<p className="text-xl">{t(`designs:${design}.d`)}</p>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-2">
{workbench ? null : (
<Link
className={`${horFlexClasses} btn btn-primary btn-lg flex md:hidden`}
href={`/new/${design}`}
>
<NewPatternIcon className="w-8 h-8" />
{t('tags:newThingPattern', { thing: capitalize(design) })}
</Link>
)}
{docs ? null : (
<Link
className={`${horFlexClasses} btn btn-secondary btn-lg flex md:hidden`}
href={`/docs/designs/${design}`}
>
<DocsIcon className="w-8 h-8" />
{t('account:docs')}
</Link>
)}
</div>
{docs || workbench || modal ? null : (
<div className="flex flex-row flex-wrap gap-2 md:gap-4 items-center p-4 border rounded-lg bg-secondary bg-opacity-5 max-w-4xl">
<b>Jump to:</b>
<AnchorLink id="notes">
<DocsTitle
slug={`docs/designs/${design}/notes`}
language={language}
format={(t) => t.split(':').pop().trim()}
/>
</AnchorLink>
{examples && <AnchorLink id="examples" txt={t('acount:examples')} />}
{['needs', 'fabric'].map((page) => (
<AnchorLink id={page} key={page}>
<DocsTitle
slug={`docs/designs/${design}/${page}`}
language={language}
format={(t) => t.split(':').pop().trim()}
/>
</AnchorLink>
))}
<AnchorLink id="docs" txt={t('account:docs')} />
<AnchorLink id="specs" txt={t('account:specifications')} />
</div>
)}
<div className={`mt-8 w-full ${docs ? '' : 'flex flex-row flex-wrap justify-between'}`}>
<div className={`w-full max-w-2xl ${docs ? '' : 'md:w-2/3 pr-0 md:pr-8'}`}>
<LineDrawing className="w-full text-base-content" />
{docs ? null : (
<>
<h2 id="notes">
<DocsTitle
slug={`docs/designs/${design}/notes`}
language={language}
format={(t) => t.split(':').pop().trim()}
/>
</h2>
<DynamicMdx
site="org"
slug={`docs/designs/${design}/notes`}
language={language}
title={false}
/>
</>
)}
{docs ? docsContent : null}
{examples ? (
<>
<h2 id="examples">{t('account:examples')}</h2>
{examples[design] ? (
<div className="grid grid-cols-2 gap-2 md:grid-cols-3 lg:grid-cols-3">
{examples[design].map((ex) => (
<button
key={ex}
onClick={() =>
setModal(
<ModalWrapper
flex="col"
justify="top lg:justify-center"
slideFrom="right"
>
<img
className="w-full shadow rounded-lg"
src={cloudflareImageUrl({ id: `showcase-${ex}`, variant: 'public' })}
/>
<p className="text-center">
<PageLink href={`/showcase/${ex}`} txt={t('account:visitShowcase')} />
</p>
</ModalWrapper>
)
}
>
<img
className="w-full shadow rounded-lg"
src={cloudflareImageUrl({ id: `showcase-${ex}`, variant: 'sq500' })}
/>
</button>
))}
</div>
) : (
<Popout note>
<h5>{t('account:noExamples')}</h5>
<p>{t('account:noExamplesMsg')}</p>
<p className="text-right">
<Link className="btn btn-primary" href="/new/showcase">
{t('account:showcaseNew')}
</Link>
</p>
</Popout>
)}
</>
) : null}
{docs
? null
: ['needs', 'fabric'].map((page) => (
<Fragment key={page}>
<h2 id={page}>
<DocsTitle
slug={`docs/designs/${design}/${page}`}
language={language}
format={(t) => t.split(':').pop().trim()}
/>
</h2>
<DynamicMdx
site="org"
slug={`docs/designs/${design}/${page}`}
language={language}
title={false}
/>
</Fragment>
))}
{docs ? null : docsContent}
</div>
<div className={`w-full ${docs ? '' : 'md:w-1/3'}`}>
{workbench ? null : (
<Link
className={`${horFlexClasses} btn btn-primary btn-lg hidden md:flex mb-2`}
href={`/new/${design}`}
>
<NewPatternIcon className="w-8 h-8" />
{t('tags:newThingPattern', { thing: capitalize(design) })}
</Link>
)}
{docs ? null : (
<Link
className={`${horFlexClasses} btn btn-secondary btn-lg hidden md:flex`}
href={`/docs/designs/${design}`}
>
<DocsIcon className="w-8 h-8" />
{t('account:docs')}
</Link>
)}
<h2 id="specs">{t('account:specifications')}</h2>
<h6 className="mt-4">{t('account:design')}</h6>
<ul>
{designs[design].design.map((person) => (
<li key={person}>{person}</li>
))}
</ul>
<h6 className="mt-4">{t('account:code')}</h6>
<ul>
{designs[design].code.map((person) => (
<li key={person}>{person}</li>
))}
</ul>
<h6 className="mt-4">{t('tags:difficulty')}</h6>
<Difficulty score={designs[design].difficulty} />
<h6 className="mt-4">{t('tags:tags')}</h6>
<div className="flex flex-row flex-wrap items-center gap-1">
{designs[design].tags.map((tag) => (
<span className="badge badge-primary font-medium" key={tag}>
{t(`tags:${tag}`)}
</span>
))}
</div>
<h6 className="mt-4">{t('techniques:techniques')}</h6>
<div className="flex flex-row flex-wrap items-center gap-1">
{designs[design].techniques.map((tech) => (
<span className="badge badge-accent font-medium" key={tech}>
{t(`techniques:${tech}`)}
</span>
))}
</div>
{Object.keys(measies.required).length > 0 ? (
<>
<h6 className="mt-4">{t('account:requiredMeasurements')}</h6>
<ul className="list list-disc list-inside pl-2">
{Object.keys(measies.required)
.sort()
.map((m) => (
<li key={m}>
<PageLink
href={`/docs/measurements/${m.toLowerCase()}`}
txt={measies.required[m]}
/>
</li>
))}
</ul>
</>
) : null}
{Object.keys(measies.optional).length > 0 ? (
<>
<h6 className="mt-4">{t('account:optionalMeasurements')}</h6>
<ul className="list list-disc list-inside pl-2">
{Object.keys(measies.optional)
.sort()
.map((m) => (
<li key={m}>
<PageLink
href={`/docs/measurements/${m.toLowerCase()}`}
txt={measies.optional[m]}
/>
</li>
))}
</ul>
</>
) : null}
{Object.keys(config.options).length > 0 ? (
<>
<h6 className="mt-4">{t('account:designOptions')}</h6>
<SimpleOptionsList options={config.options} t={t} design={design} />
</>
) : null}
<h6 className="mt-4">{t('account:parts')}</h6>
<ul className="list list-disc list-inside pl-2">
{config.draftOrder.map((part) => (
<li key={part}>{part}</li>
))}
</ul>
{Object.keys(config.plugins).length > 0 ? (
<>
<h6 className="mt-4">{t('account:plugins')}</h6>
<ul className="list list-disc list-inside pl-2">
{Object.keys(config.plugins).map((plugin) => (
<li key={plugin}>{plugin}</li>
))}
</ul>
</>
) : null}
</div>
</div>
</>
)
}
*/