1
0
Fork 0

wip: Work on menus

This commit is contained in:
joostdecock 2025-01-25 17:56:15 +01:00
parent 07ba6df1c4
commit b306d6374d
19 changed files with 249 additions and 210 deletions

3
.gitignore vendored
View file

@ -129,3 +129,6 @@ scripts/verdaccio.sh
# e2e test results # e2e test results
sites/*/playwright-report sites/*/playwright-report
# Local Netlify folder
.netlify

2
package-lock.json generated
View file

@ -62216,7 +62216,7 @@
"highlight.js": "^11.11.0", "highlight.js": "^11.11.0",
"html-react-parser": "^5.0.7", "html-react-parser": "^5.0.7",
"luxon": "^3.5.0", "luxon": "^3.5.0",
"nuqs": "^2.3.0", "nuqs": "^1.17.6",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
"tlds": "^1.255.0", "tlds": "^1.255.0",
"use-local-storage-state": "19.1.0", "use-local-storage-state": "19.1.0",

View file

@ -20,7 +20,7 @@ export const PatternLayout = (props) => {
<div className="tw-flex tw-flex-col tw-h-full"> <div className="tw-flex tw-flex-col tw-h-full">
<HeaderMenu state={props.state} {...{ update, Design, pattern, config }} /> <HeaderMenu state={props.state} {...{ update, Design, pattern, config }} />
<div className="tw-flex lg:tw-flex-row tw-grow lg:tw-max-h-[90vh] tw-max-h-[calc(100vh-3rem)] tw-h-full tw-py-4 lg:tw-mt-6"> <div className="tw-flex lg:tw-flex-row tw-grow lg:tw-max-h-[90vh] tw-max-h-[calc(100vh-3rem)] tw-h-full tw-py-4 lg:tw-mt-6">
<div className="lg:tw-w-2/3 tw-flex tw-flex-col tw-h-full tw-grow px-4"> <div className="lg:tw-w-2/3 tw-flex tw-flex-col tw-h-full tw-grow tw-p-2 tw-shadow tw-mx-2">
{props.output} {props.output}
</div> </div>
{menu ? ( {menu ? (

View file

@ -5,7 +5,13 @@ import { designOptionType } from '@freesewing/utils'
import React, { useState, useMemo } from 'react' import React, { useState, useMemo } from 'react'
// Components // Components
import { SubAccordion } from '../Accordion.mjs' import { SubAccordion } from '../Accordion.mjs'
import { EditIcon, GroupIcon, OptionsIcon, ResetIcon } from '@freesewing/react/components/Icon' import {
EditIcon,
GroupIcon,
OptionsIcon,
ResetIcon,
TipIcon,
} from '@freesewing/react/components/Icon'
import { CoreSettingsMenu } from './CoreSettingsMenu.mjs' import { CoreSettingsMenu } from './CoreSettingsMenu.mjs'
import { FormControl } from '@freesewing/react/components/Input' import { FormControl } from '@freesewing/react/components/Input'
@ -103,12 +109,14 @@ export const MenuItem = ({
buttons.push(<ResetButton open disabled={!changed} key="clear" />) buttons.push(<ResetButton open disabled={!changed} key="clear" />)
return ( return (
<>
<FormControl <FormControl
label={ FIXME_REMOVED_label={
<span className="tw-text-base tw-font-normal"> <span className="tw-text-base tw-font-normal">
{config.choiceTitles ? config.choiceTitles[current] : i18n.en.o[name].d} {config.choiceTitles ? config.choiceTitles[current] : i18n.en.o[name].d}
</span> </span>
} }
label={false}
id={config.name} id={config.name}
labelBR={<div className="tw-flex tw-flex-row tw-items-center tw-gap-2">{buttons}</div>} labelBR={<div className="tw-flex tw-flex-row tw-items-center tw-gap-2">{buttons}</div>}
labelBL={ labelBL={
@ -121,6 +129,17 @@ export const MenuItem = ({
> >
<Input {...drillProps} /> <Input {...drillProps} />
</FormControl> </FormControl>
{config.about ? (
<div className="tw-flex tw-flex-row tw-border tw-border-success tw-rounded">
<div className="tw-bg-success tw-text-success-content tw-p-1 tw-rounded-l tw-flex tw-flex-row tw-items-center">
<TipIcon className="tw-w-6 tw-h-6 tw-text-success-content" />
</div>
<div className="tw-p-1 tw-text-sm tw-font-medium tw-bg-success/10 tw-grow tw-rounded-r">
{config.about}
</div>
</div>
) : null}
</>
) )
} }
@ -206,7 +225,9 @@ export const MenuItemGroup = ({
<div className="tw-flex tw-flex-row tw-items-center tw-justify-between tw-w-full" key="a"> <div className="tw-flex tw-flex-row tw-items-center tw-justify-between tw-w-full" key="a">
<div className="tw-flex tw-flex-row tw-items-center tw-gap-4 tw-w-full"> <div className="tw-flex tw-flex-row tw-items-center tw-gap-4 tw-w-full">
<ItemIcon /> <ItemIcon />
<span className="tw-font-medium tw-capitalize">{getItemLabel(i18n, itemName)}</span> <span className="tw-font-medium tw-capitalize">
{item.title ? item.title : getItemLabel(i18n, itemName)}
</span>
</div> </div>
<div className="tw-font-bold"> <div className="tw-font-bold">
<Value <Value

View file

@ -58,16 +58,17 @@ const getTitleAndDesc = (config = {}, i18n = {}, isDesignOption = false) => {
} }
} }
console.log(config)
let titleKey = config.choiceTitles let titleKey = config.choiceTitles
? config.choiceTitles[entry] ? 'fixme' //config.choiceTitles[entry]
: isDesignOption : isDesignOption
? i18n.en.o[name] ? i18n.en.o[name]
: `${name}.o.${entry}` : `${name}.o.${entry}`
if (!config.choiceTitles && i18n && i18n.en.o[`${name}.${entry}`]) if (!config.choiceTitles && i18n && i18n.en.o[`${name}.${entry}`])
titleKey = i18n.en.o[`${name}.${entry}`] titleKey = i18n.en.o[`${name}.${entry}`]
console.log({ titleKey, titles: config.choiceTitles, isDesignOption }) console.log({ titleKey, titles: config.choiceTitles, isDesignOption })
const title = config.titleMethod const title = config.title
? config.title
: config.titleMethod
? config.titleMethod(entry) ? config.titleMethod(entry)
: typeof titleKey === 'string' : typeof titleKey === 'string'
? i18n.en.o[titleKey]?.t ? i18n.en.o[titleKey]?.t
@ -115,8 +116,9 @@ export const MenuListInput = ({
}) })
return config.list.map((entry) => { return config.list.map((entry) => {
const { title, desc } = getTitleAndDesc(config, i18n, isDesignOption) const { title = false, about = false } = config //getTitleAndDesc(config, i18n, isDesignOption)
const sideBySide = config.sideBySide || desc.length + title.length < 42 if (!title || !about) console.log('No title or about in', { name, config, design })
const sideBySide = config.sideBySide || about.length + title.length < 42
return ( return (
<ButtonFrame <ButtonFrame
@ -136,8 +138,10 @@ export const MenuListInput = ({
sideBySide ? 'tw-flex-row tw-justify-between tw-gap-2' : 'tw-flex-col' sideBySide ? 'tw-flex-row tw-justify-between tw-gap-2' : 'tw-flex-col'
}`} }`}
> >
<div className="tw-font-bold tw-text-lg tw-shrink-0">{title}</div> <div className="tw-font-semibold tw-shrink-0">{config.choiceTitles[entry]}</div>
{compact ? null : <div className="tw-text-base tw-font-normal">{desc}</div>} {compact || !config.choiceDescriptions ? null : (
<div className="tw-text-base tw-font-normal">{config.choiceDescriptions[entry]}</div>
)}
</div> </div>
</ButtonFrame> </ButtonFrame>
) )
@ -199,74 +203,6 @@ export const MenuMmInput = (props) => {
) )
} }
/**
* A number input that accepts comma or period decimal separators.
* Because our use case is almost never going to include thousands, we're using a very simple way of accepting commas:
* The validator checks for the presence of a single comma or period followed by numbers
* The parser replaces a single comma with a period
*
* optionally accepts fractions
* @param {Number} options.val the value of the input
* @param {Function} options.onUpdate a function to handle when the value is updated to a valid value
* @param {Boolean} options.fractions should the input allow fractional input
*/
//export const MenuNumberInput = ({
// value,
// onUpdate,
// onMount,
// className,
// fractions = true,
// min = -Infinity,
// max = Infinity,
// swizzled,
//}) => {
// const valid = useRef(validateVal(value, fractions, min, max))
//
// const handleChange = useCallback(
// (newVal) => {
// // only actually update if the value is valid
// if (typeof onUpdate === 'function') {
// onUpdate(valid.current, newVal)
// }
// },
// [onUpdate, valid]
// )
//
// // onChange
// const onChange = useCallback(
// (evt) => {
// const newVal = evt.target.value
// // set validity so it will display
// valid.current = validateVal(newVal, fractions, min, max)
//
// // handle the change
// handleChange(newVal)
// },
// [fractions, min, max, valid]
// )
//
// const val = typeof value === 'undefined' ? config.dflt : value
//
// useEffect(() => {
// if (typeof onMount === 'function') {
// onMount(valid.current)
// }
// }, [onMount, valid])
//
// return (
// <input
// type="text"
// inputMode="number"
// className={`input input-secondary ${className || 'input-sm grow text-base-content'}
// ${valid.current === false && 'input-error'}
// ${valid.current && 'input-success'}
// `}
// value={val}
// onChange={onChange}
// />
// )
//}
/** A {@see SliderInput} to handle percentage values */ /** A {@see SliderInput} to handle percentage values */
export const MenuPctInput = ({ current, changed, updateHandler, config, ...rest }) => { export const MenuPctInput = ({ current, changed, updateHandler, config, ...rest }) => {
const factor = 100 const factor = 100

View file

@ -13,15 +13,15 @@ export const UiPreferencesMenu = ({ update, state, Design }) => {
const drillProps = { Design, state, update } const drillProps = { Design, state, update }
const inputs = { const inputs = {
ux: (props) => <MenuUxSettingInput {...drillProps} {...props} />, ux: (props) => <MenuUxSettingInput {...drillProps} {...props} />,
aside: (props) => <MenuListInput {...drillProps} {...props} />, //aside: (props) => <MenuListInput {...drillProps} {...props} />,
kiosk: (props) => <MenuListInput {...drillProps} {...props} />, //kiosk: (props) => <MenuListInput {...drillProps} {...props} />,
rotate: (props) => <MenuListInput {...drillProps} {...props} />, rotate: (props) => <MenuListInput {...drillProps} {...props} />,
renderer: (props) => <MenuListInput {...drillProps} {...props} />, renderer: (props) => <MenuListInput {...drillProps} {...props} />,
} }
const values = { const values = {
ux: (props) => <Ux ux={state.ui.ux} {...props} />, ux: (props) => <span>{state.ui.ux}/5</span>,
aside: MenuListValue, //aside: MenuListValue,
kiosk: MenuListValue, //kiosk: MenuListValue,
rotate: MenuListValue, rotate: MenuListValue,
renderer: MenuListValue, renderer: MenuListValue,
} }
@ -36,7 +36,7 @@ export const UiPreferencesMenu = ({ update, state, Design }) => {
<UiPreference updateHandler={update} {...{ inputs, values, Design }} {...props} /> <UiPreference updateHandler={update} {...{ inputs, values, Design }} {...props} />
), ),
isFirst: true, isFirst: true,
name: 'pe:uiPreferences', name: 'UI Preferences',
language: state.locale, language: state.locale,
passProps: { passProps: {
ux: state.ui?.ux, ux: state.ui?.ux,

View file

@ -64,9 +64,9 @@ export const DraftView = ({ Design, state, update, config }) => {
} }
return ( return (
<PatternLayout <>
{...{ update, Design, output, state, pattern, config }} <PatternLayout {...{ update, Design, output, state, pattern, config }} />
menu={state.ui?.aside ? <DraftMenu {...{ Design, pattern, update, state }} /> : null} <p className="tw-clear-both">where does this go?</p>
/> </>
) )
} }

View file

@ -1,6 +1,8 @@
import React from 'react'
// Dependencies // Dependencies
import { defaultConfig as config } from '../config/index.mjs' import { defaultConfig as config } from '../config/index.mjs'
import { measurementAsMm } from '@freesewing/utils' import { measurementAsMm } from '@freesewing/utils'
import { linkClasses } from '@freesewing/utils'
/* /*
* Components * Components
* Note that these are only used as returns values * Note that these are only used as returns values
@ -58,19 +60,30 @@ export function menuCoreSettingsSammHandler({ updateHandler, config }) {
export function menuCoreSettingsSaboolHandler({ toggleSa }) { export function menuCoreSettingsSaboolHandler({ toggleSa }) {
return toggleSa return toggleSa
} }
const CoreDocsLink = ({ item }) => (
<a href={`/docs/about/site/draft/#${item.toLowerCase()}`} className={`${linkClasses} tw-px-2`}>
Learn more
</a>
)
export function menuCoreSettingsStructure({ units = 'metric', sabool = false, parts = [] }) { export function menuCoreSettingsStructure({ units = 'metric', sabool = false, parts = [] }) {
return { return {
sabool: { sabool: {
dense: true,
title: 'Include seam allowance',
about: (
<>
Controls whether or not you want to include seam allowance on your pattern.
<CoreDocsLink item="sabool" />
</>
),
ux: config.uxLevels.core.sa, ux: config.uxLevels.core.sa,
list: [0, 1], list: [0, 1],
choiceTitles: { choiceTitles: {
0: 'Do not include seam allowance', 0: 'Do not include seam allowance',
1: 'Include seam allowance', 1: 'Include seam allowance',
}, },
choiceDescriptions: {
0: 'This generates a pattern which does not include any seam allowance. The size of the seam allowance does not matter as no seam allowance will be included',
1: 'This generates a pattern that will include seam allowance. The size of the seam allowance is set individually',
},
valueTitles: { valueTitles: {
0: 'No', 0: 'No',
1: 'Yes', 1: 'Yes',
@ -80,6 +93,13 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
}, },
samm: sabool samm: sabool
? { ? {
title: 'Seam Allowance Size',
about: (
<>
Controls the size of the pattern's seam allowance.
<CoreDocsLink item="sa" />
</>
),
ux: config.uxLevels.core.sa, ux: config.uxLevels.core.sa,
min: 0, min: 0,
max: units === 'imperial' ? 2 : 2.5, max: units === 'imperial' ? 2 : 2.5,
@ -87,17 +107,44 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
icon: SaIcon, icon: SaIcon,
} }
: false, : false,
units: {
dense: true,
title: 'Pattern units',
about: (
<>
Allows you to switch between metric and imperial units on the pattern.
<CoreDocsLink item="units" />
</>
),
ux: config.uxLevels.core.units,
list: ['metric', 'imperial'],
dflt: 'metric',
choiceTitles: {
metric: 'Metric',
imperial: 'Imperial',
},
valueTitles: {
metric: 'Metric',
imperial: 'Imperial',
},
icon: UnitsIcon,
},
paperless: { paperless: {
dense: true,
title: 'Paperless pattern',
about: (
<>
Trees are awesome, and taping together sewing patterns is not much fun. Try our paperless
mode to avoid the need to print out your pattern altogether.
<CoreDocsLink item="paperless" />
</>
),
ux: config.uxLevels.core.paperless, ux: config.uxLevels.core.paperless,
list: [0, 1], list: [0, 1],
choiceTitles: { choiceTitles: {
0: 'Generate a regular pattern', 0: 'Generate a regular pattern',
1: 'Generate a paperless pattern', 1: 'Generate a paperless pattern',
}, },
choiceDescriptions: {
0: 'This will generate a regular pattern, which you can them print out.',
1: 'This will generate a pattern with dimensions and a grid, which allows you to transfer it on fabric or another medium without the need to print the pattern.',
},
valueTitles: { valueTitles: {
0: 'No', 0: 'No',
1: 'Yes', 1: 'Yes',
@ -105,35 +152,22 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
dflt: 0, dflt: 0,
icon: PaperlessIcon, icon: PaperlessIcon,
}, },
units: {
ux: config.uxLevels.core.units,
list: ['metric', 'imperial'],
dflt: 'metric',
choiceTitles: {
metric: 'Metric',
imperial: 'Imperial',
},
choiceDescriptions: {
0: 'Use this if you use the metric system, and centimeters are something you are familiar with. This is the best choice for most people around the world.',
1: 'Use this if inches are more familiar to you. This is often the preferred choice for people based in the UK & US.',
},
valueTitles: {
metric: 'Metric',
imperial: 'Imperial',
},
icon: UnitsIcon,
},
complete: { complete: {
dense: true,
title: 'Generate a detailed pattern',
about: (
<>
Controls how detailed the pattern is; Either a complete pattern with all details, or a
basic outline of the pattern parts.
<CoreDocsLink item="complete" />
</>
),
ux: config.uxLevels.core.complete, ux: config.uxLevels.core.complete,
list: [1, 0], list: [1, 0],
dflt: 1, dflt: 1,
choiceTitles: { choiceTitles: {
0: 'Generate a pattern outline', 0: 'Generate a pattern outline',
1: 'Generate a complete pattern', 1: 'Generate a detailed pattern',
},
choiceDescriptions: {
0: 'Only generate the outline of the pattern parts. Use this if you are looking to use a laser cutter or have other specific needs.',
1: 'This will generate a complete pattern with all annotations, markings and lines. Use this if you are not certain what to choose.',
}, },
valueTitles: { valueTitles: {
0: 'No', 0: 'No',
@ -142,6 +176,15 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
icon: DetailIcon, icon: DetailIcon,
}, },
expand: { expand: {
dense: true,
title: 'Expand pattern parts',
about: (
<>
Controls efforts to save paper. Disable this to expand all pattern parts at the cost of
using more space & paper.
<CoreDocsLink item="expand" />
</>
),
ux: config.uxLevels.core.expand, ux: config.uxLevels.core.expand,
list: [1, 0], list: [1, 0],
dflt: 1, dflt: 1,
@ -149,10 +192,6 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
0: 'Keep pattern parts compact where possible', 0: 'Keep pattern parts compact where possible',
1: 'Expand all pattern parts', 1: 'Expand all pattern parts',
}, },
choiceDescriptions: {
0: 'This will generate a more dense representation of the pattern which includes all info without using up too much space.',
1: 'This will generate a pattern where all parts are drown to their full size, even if they are simple rectangles.',
},
valueTitles: { valueTitles: {
0: 'No', 0: 'No',
1: 'Yes', 1: 'Yes',
@ -160,6 +199,7 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
icon: ExpandIcon, icon: ExpandIcon,
}, },
only: { only: {
title: 'Only included selected pattern parts',
ux: config.uxLevels.core.only, ux: config.uxLevels.core.only,
dflt: false, dflt: false,
list: parts, list: parts,
@ -167,6 +207,7 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
icon: IncludeIcon, icon: IncludeIcon,
}, },
scale: { scale: {
title: 'Pattern annotations scale',
ux: config.uxLevels.core.scale, ux: config.uxLevels.core.scale,
min: 0.1, min: 0.1,
max: 5, max: 5,
@ -175,6 +216,7 @@ export function menuCoreSettingsStructure({ units = 'metric', sabool = false, pa
icon: ScaleIcon, icon: ScaleIcon,
}, },
margin: { margin: {
title: 'Pattern parts margin',
ux: config.uxLevels.core.margin, ux: config.uxLevels.core.margin,
min: 0, min: 0,
max: 2.5, max: 2.5,

View file

@ -3,6 +3,7 @@ import React from 'react'
import { defaultConfig } from '../config/index.mjs' import { defaultConfig } from '../config/index.mjs'
import { round } from '@freesewing/utils' import { round } from '@freesewing/utils'
import { formatDesignOptionValue, menuCoreSettingsStructure } from './index.mjs' import { formatDesignOptionValue, menuCoreSettingsStructure } from './index.mjs'
import { menuUiPreferencesStructure } from './ui-preferences.mjs'
// Components // Components
import { import {
ErrorIcon, ErrorIcon,
@ -113,7 +114,6 @@ export function getCoreSettingUndoStepData({ step, state, Design }) {
* Save us some typing * Save us some typing
*/ */
const cord = settingsValueCustomOrDefault const cord = settingsValueCustomOrDefault
const formatMm = formatMm
const Html = HtmlSpan const Html = HtmlSpan
/* /*

View file

@ -1,3 +1,4 @@
import React from 'react'
import { defaultConfig } from '../config/index.mjs' import { defaultConfig } from '../config/index.mjs'
import { import {
MenuIcon, MenuIcon,
@ -11,28 +12,49 @@ export function menuUiPreferencesStructure() {
const uiUx = defaultConfig.uxLevels.ui const uiUx = defaultConfig.uxLevels.ui
const uiPreferences = { const uiPreferences = {
ux: { ux: {
dense: true,
title: 'User Experience',
about: 'Controls the user experience, from keep it simple, to give me all the powers',
ux: uiUx.ux, ux: uiUx.ux,
emoji: '🖥️', emoji: '🖥️',
list: [1, 2, 3, 4, 5], list: [1, 2, 3, 4, 5],
choiceTitles: {}, choiceTitles: {
1: 'Keep it as simple as possible',
2: 'Keep it simple, but not too simple',
3: 'Balance simplicity with power',
4: 'Give me all powers, but keep me safe',
5: 'Get out of my way',
},
_choiceDescriptions: {
1: 'Hides all but the most crucial features.',
2: 'Hides most of the advanced features.',
3: 'Reveals the majority of advanced features, but not all of them.',
4: 'Reveals all advanced features, keeps handrails and safety checks.',
5: 'Reveals all advanced features, removes handrails and safety checks.',
},
icon: UxIcon, icon: UxIcon,
dflt: defaultConfig.defaultUx, dflt: defaultConfig.defaultUx,
}, },
/*
aside: { aside: {
title: 'Aside Menu',
about: 'Whether or not to display the aside menu',
ux: uiUx.aside, ux: uiUx.aside,
list: [0, 1], list: [0, 1],
choiceTitles: { choiceTitles: {
0: 'pe:noAside', 0: 'Display the aside menu',
1: 'pe:withAside', 1: 'Hide the aside menu',
}, },
choiceDescriptions: { choiceDescriptions: {
0: 'pe:noAside', 0: 'Displays the Design Options, Core Settings, and UI Preferences menu on the side of the screen (not on mobile).',
1: 'pe:withAside', 1: 'Uses the entire screen size for your pattern, providing access to the Design Options, Core Settings, and UI Preferences through the header navigation only.',
}, },
dflt: 1, dflt: 1,
icon: MenuIcon, icon: MenuIcon,
}, },
kiosk: { kiosk: {
title: 'Kiosk View',
about: 'Whether or not to hide the header and footer',
ux: uiUx.kiosk, ux: uiUx.kiosk,
list: [0, 1], list: [0, 1],
choiceTitles: { choiceTitles: {
@ -46,26 +68,37 @@ export function menuUiPreferencesStructure() {
dflt: 0, dflt: 0,
icon: KioskIcon, icon: KioskIcon,
}, },
*/
rotate: { rotate: {
dense: true,
title: 'Rotate Pattern',
about: 'Allows you to rotate your pattern 90 degrees, handy for tall patterns',
ux: uiUx.rotate, ux: uiUx.rotate,
list: [0, 1], list: [0, 1],
choiceTitles: { choiceTitles: {
0: 'pe:rotateNo', 0: 'Do not rotate the pattern',
1: 'pe:rotateYes', 1: 'Rotate the pattern 90 degrees',
},
choiceDescriptions: {
0: 'pe:noAside',
1: 'pe:withAside',
}, },
dflt: 0, dflt: 0,
icon: RotateIcon, icon: RotateIcon,
}, },
renderer: { renderer: {
dense: true,
title: 'Pattern render engine',
about: 'Change the way the pattern is rendered on screen',
ux: uiUx.renderer, ux: uiUx.renderer,
list: ['react', 'svg'], list: ['react', 'svg'],
choiceTitles: { choiceTitles: {
react: 'pe:renderWithReact', react: (
svg: 'pe:renderWithCore', <span>
Render using <em>@freesewing/react</em>
</span>
),
svg: (
<span>
Render using <em>@freesewing/core</em>
</span>
),
}, },
choiceDescriptions: { choiceDescriptions: {
0: 'pe:noAside', 0: 'pe:noAside',
@ -73,13 +106,12 @@ export function menuUiPreferencesStructure() {
}, },
valueTitles: { valueTitles: {
react: 'React', react: 'React',
svg: 'SVG', svg: 'Core',
}, },
dflt: 'react', dflt: 'react',
icon: RocketIcon, icon: RocketIcon,
}, },
} }
uiPreferences.ux.list.forEach((i) => (uiPreferences.ux.choiceTitles[i] = 'pe:ux' + i))
return uiPreferences return uiPreferences
} }

View file

@ -89,6 +89,11 @@ export const ChatIcon = (props) => (
export const CircleIcon = (props) => ( export const CircleIcon = (props) => (
<IconWrapper {...props}> <IconWrapper {...props}>
<circle cx="12" cy="12" r="10" /> <circle cx="12" cy="12" r="10" />
{props.label ? (
<text x="12" y="17.5" stroke="none" fill="currentColor" textAnchor="middle">
{props.label}
</text>
) : null}
</IconWrapper> </IconWrapper>
) )

View file

@ -69,9 +69,9 @@ export const FormControl = ({
<label className="tw-daisy-label tw-pb-0" htmlFor={forId}> <label className="tw-daisy-label tw-pb-0" htmlFor={forId}>
{topLabelChildren} {topLabelChildren}
</label> </label>
) : ( ) : label ? (
<div className="tw-daisy-label tw-pb-0">{topLabelChildren}</div> <div className="tw-daisy-label tw-pb-0">{topLabelChildren}</div>
)} ) : null}
{children} {children}
{labelBL || labelBR ? ( {labelBL || labelBR ? (
forId ? ( forId ? (
@ -99,7 +99,7 @@ export const ButtonFrame = ({
<button <button
className={` className={`
tw-daisy-btn tw-daisy-btn-ghost tw-daisy-btn-secondary tw-daisy-btn tw-daisy-btn-ghost tw-daisy-btn-secondary
tw-w-full ${dense ? 'tw-mt-1 tw-py-0 tw-daisy-btn-sm' : 'tw-mt-2 tw-py-4 tw-h-auto tw-content-start'} tw-w-full ${dense ? 'tw-mt-1 tw-daisy-btn-sm tw-font-light' : 'tw-mt-2 tw-py-4 tw-h-auto tw-content-start'}
tw-border-2 tw-border-secondary tw-text-left tw-bg-opacity-20 tw-border-2 tw-border-secondary tw-text-left tw-bg-opacity-20
${accordion ? 'hover:tw-bg-transparent' : 'hover:tw-bg-secondary hover:tw-bg-opacity-10'} ${accordion ? 'hover:tw-bg-transparent' : 'hover:tw-bg-secondary hover:tw-bg-opacity-10'}
hover:tw-border-secondary hover:tw-border-solid hover:tw-border-2 hover:tw-border-secondary hover:tw-border-solid hover:tw-border-2

View file

@ -13,3 +13,9 @@ export const Ux = ({ ux = 0 }) => (
))} ))}
</div> </div>
) )
export const UxMini = ({ ux = 0 }) => (
<div className="flex flex-row">
<CircleIcon className={`tw-w-6 tw-h-6 tw-stroke-secondary tw-fill-secondary/20`} label={ux} />
</div>
)

View file

@ -83,4 +83,4 @@ submit an issue.
We'd love to hear your feedback. We're **@freesewing_org** on We'd love to hear your feedback. We're **@freesewing_org** on
[Twitter](https://twitter.com/freesewing_org) and [Twitter](https://twitter.com/freesewing_org) and
[Instagram](https://instagram.com/freesewing_org), use the **#freesewing** [Instagram](https://instagram.com/freesewing_org), use the **#freesewing**
hashtag, or [check our share page](/share). hashtag, or [check our share page](#fixme).

View file

@ -153,4 +153,4 @@ And if you happen to like what we do here, perhaps now is a good time to tell
your friends about FreeSewing. After all, they can now check it out without your friends about FreeSewing. After all, they can now check it out without
needing to sign up. needing to sign up.
PS: We have [a handy share page](/share/) you can use for this. PS: We have [a handy share page](#fixme) you can use for this.

View file

@ -17,9 +17,15 @@
--neutral-50: #fafafa; --neutral-50: #fafafa;
--neutral-100: #f5f5f5; --neutral-100: #f5f5f5;
--neutral-200: #e5e5e5; --neutral-200: #e5e5e5;
--neutral-400: #a3a3a3;
--neutral-500: #737373; --neutral-500: #737373;
--neutral-600: #525252; --neutral-600: #525252;
--neutral-900: #0a0a0a; --neutral-900: #0a0a0a;
--amber-600: #d97706;
--red-500: #ef4444;
--blue-500: #3b82f6;
--pink-500: #ec4899;
--violet-500: #8b5cf6;
/* Color Palette */ /* Color Palette */
--ifm-color-primary: var(--sky-500); --ifm-color-primary: var(--sky-500);
@ -77,47 +83,17 @@
--ifm-footer-link-color: var(--sky-600); --ifm-footer-link-color: var(--sky-600);
/* /*
* These are variables to style FreeSewing SVG output (drafts, examples, and so on) * FreeSewing pattern vars
*/ */
--pattern-bg: #fafafa; '--pattern-bg':var(--neutral-50);
--pattern-fabric: #404040; '--pattern-fabric':var(--neutral-900);
--pattern-lining: #10b981; '--pattern-lining':var(--emerald-500);
--pattern-interfacing: #a3a3a3; '--pattern-interfacing':var(--neutral-400);
--pattern-canvas: #d97706; ,'--pattern-canvas': var(--amber-600);
--pattern-various: #ef4444; '--pattern-various':var(--red-500);
--pattern-mark: #3b82f6; '--pattern-mark':var(--blue-500);
--pattern-contrast: #ec4899; '--pattern-contrast':var(--pink-500);
--pattern-note: #8b5cf6; '--pattern-note':var(--violet-500);
--pattern-color-0: #ef4444;
--pattern-color-1: #22c55e;
--pattern-color-2: #3b82f6;
--pattern-color-3: #eab308;
--pattern-color-4: #ec4899;
--pattern-color-5: #8b5cf6;
--pattern-color-6: #14b8a6;
--pattern-color-7: #f59e0b;
--pattern-color-8: #d946ef;
--pattern-color-9: #06b6d4;
--pattern-text-xs: 0.2rem;
--pattern-text-sm: 0.3rem;
--pattern-text: 0.4rem;
--pattern-text-lg: 0.6rem;
--pattern-text-xl: 0.8rem;
--pattern-text-2xl: 1.5rem;
--pattern-text-3xl: 2rem;
--pattern-text-4xl: 3rem;
--pattern-scale: 1;
--pattern-stroke-xs: 0.2px;
--pattern-stroke-sm: 0.4px;
--pattern-stroke: 0.7px;
--pattern-stroke-lg: 1.3px;
--pattern-stroke-xl: 2px;
--pattern-stroke-2xl: 4px;
--pattern-stroke-3xl: 6px;
--pattern-stroke-4xl: 8px;
--pattern-stroke-5xl: 12px;
--pattern-stroke-6xl: 16px;
--pattern-stroke-7xl: 20px;
} }
/* For readability concerns, you should choose a lighter palette in dark mode. */ /* For readability concerns, you should choose a lighter palette in dark mode. */
@ -148,6 +124,19 @@
/* Footer colors */ /* Footer colors */
--ifm-footer-background-color: var(--neutral-900); --ifm-footer-background-color: var(--neutral-900);
--ifm-footer-link-color: var(--sky-300); --ifm-footer-link-color: var(--sky-300);
/*
* FreeSewing pattern vars
*/
'--pattern-bg':var(--neutral-900);
'--pattern-fabric':var(--neutral-5);
'--pattern-lining':var(--emerald-500);
'--pattern-interfacing':var(--neutral-400);
,'--pattern-canvas': var(--amber-600);
'--pattern-various':var(--red-500);
'--pattern-mark':var(--blue-500);
'--pattern-contrast':var(--pink-500);
'--pattern-note':var(--violet-500);
} }
/* /*
@ -155,6 +144,11 @@
*/ */
@import './code.css'; @import './code.css';
/*
* Add styling for FreeSewing patterns
*/
@import './patterns.css';
div.footer__copyright { div.footer__copyright {
font-size: 0.8rem; font-size: 0.8rem;
} }

File diff suppressed because one or more lines are too long

View file

@ -70,7 +70,7 @@ export const theme = {
'--focus-ring-offset': '2px', '--focus-ring-offset': '2px',
'--pattern-bg': colors.neutral['900'], '--pattern-bg': colors.neutral['900'],
'--pattern-fabric': colors.neutral['300'], '--pattern-fabric': colors.neutral['50'],
'--pattern-lining': colors.emerald['300'], '--pattern-lining': colors.emerald['300'],
'--pattern-interfacing': colors.neutral['500'], '--pattern-interfacing': colors.neutral['500'],
'--pattern-canvas': colors.amber['300'], '--pattern-canvas': colors.amber['300'],

View file

@ -170,7 +170,7 @@ export const theme = {
// Pattern background color // Pattern background color
'--pattern-bg': colors.neutral['50'], '--pattern-bg': colors.neutral['50'],
// Color for the main fabric // Color for the main fabric
'--pattern-fabric': colors.neutral['700'], '--pattern-fabric': colors.neutral['900'],
// Color for lining fabric // Color for lining fabric
'--pattern-lining': colors.emerald['500'], '--pattern-lining': colors.emerald['500'],
// Color for interfacing // Color for interfacing