1
0
Fork 0

wip: work on editor

This commit is contained in:
joostdecock 2025-01-19 11:01:34 +01:00
parent 30ae7b09da
commit c8b989afcb
16 changed files with 345 additions and 215 deletions

View file

@ -105,6 +105,7 @@ packageJson:
"./components/Table": "./components/Table/index.mjs" "./components/Table": "./components/Table/index.mjs"
"./components/Time": "./components/Time/index.mjs" "./components/Time": "./components/Time/index.mjs"
"./components/Uuid": "./components/Uuid/index.mjs" "./components/Uuid": "./components/Uuid/index.mjs"
"./components/Ux": "./components/Ux/index.mjs"
"./components/Yaml": "./components/Yaml/index.mjs" "./components/Yaml": "./components/Yaml/index.mjs"
"./components/Xray": "./components/Xray/index.mjs" "./components/Xray": "./components/Xray/index.mjs"
# Context # Context

View file

@ -0,0 +1,131 @@
import React from 'react'
import mustache from 'mustache'
import { defaultConfig } from '../config/index.mjs'
import { flattenFlags } from '../lib/index.mjs'
import {
ChatIcon,
ErrorIcon,
ExpandIcon,
DocsIcon,
FixmeIcon,
FlagIcon,
OptionsIcon,
TipIcon,
WarningIcon,
WrenchIcon,
} from '@freesewing/react/components/Icon'
import { SubAccordion } from './Accordion.mjs'
/*
* Helper object to look up flag icons
*/
const flagIcons = {
error: ErrorIcon,
expand: ExpandIcon,
fixme: WrenchIcon,
info: DocsIcon,
note: ChatIcon,
otions: OptionsIcon,
tip: TipIcon,
warning: WarningIcon,
}
export const FlagTypeIcon = ({ type, className = 'tw-w-6 tw-h-6' }) => {
const Icon = flagIcons[type] || FixmeIcon
return <Icon className={className} />
}
export const Flag = ({ data, handleUpdate }) => {
const btnIcon = data.suggest?.icon ? (
<FlagTypeIcon type={data.suggest.icon} className="tw-w-5 tw-h-6 sm:tw-w-6 tw-h-6" />
) : null
const button =
data.suggest?.text && data.suggest?.update ? (
<button
className={`tw-btn tw-btn-secondary tw-btn-outline tw-flex tw-flex-row tw-items-center ${
btnIcon ? 'tw-gap-6' : ''
}`}
onClick={() => handleUpdate(data.suggest.update)}
>
{btnIcon}
{data.suggest.text}
</button>
) : null
const desc = data.replace ? mustache.render(data.desc, data.replace) : data.desc
const notes = data.notes
? Array.isArray(data.notes)
? '\n\n' +
data.notes
.map((note) => (data.replace ? mustache.render(note, data.replace) : note))
.join('\n\n')
: '\n\n' + (data.replace ? mustache.render(data.notes, data.replace) : data.notes)
: null
return (
<div className="tw-flex tw-flex-col tw-gap-2 tw-items-start">
<div className="first:tw-mt-0 tw-grow md flag">
<pre>{desc}</pre>
<pre>{notes}</pre>
</div>
{button ? (
<div className="tw-mt-2 tw-w-full tw-flex tw-flex-row tw-justify-end">{button}</div>
) : null}
</div>
)
}
export const FlagsAccordionTitle = ({ flags }) => {
const flagList = flattenFlags(flags)
if (Object.keys(flagList).length < 1) return null
return (
<>
<h5 className="tw-flex tw-flex-row tw-gap-2 tw-items-center tw-justify-between tw-w-full">
<span className="tw-text-left">Flags ({Object.keys(flagList).length})</span>
<FlagTypeIcon className="tw-w-8 tw-h-8" />
</h5>
<p className="tw-text-left">
{Object.keys(flagList).length > 1
? 'Some issues about your current pattern need your attention.'
: 'A specific issue about your current pattern needs your attention.'}
</p>
</>
)
}
export const FlagsAccordionEntries = ({ flags, update }) => {
const flagList = flattenFlags(flags)
if (Object.keys(flagList).length < 1) return null
const handleUpdate = (config) => {
if (config.settings) update.settings(...config.settings)
if (config.ui) update.ui(...config.settings)
}
return (
<SubAccordion
items={Object.entries(flagList).map(([key, flag], i) => {
const title = flag.replace ? mustache.render(flag.title, flag.replace) : flag.title
return [
<div className="tw-w-full tw-flex tw-flex-row tw-gap2 tw-justify-between" key={i}>
<div className="tw-flex tw-flex-row tw-items-center tw-gap-2">
<div className="tw-no-shrink">
<FlagIcon type={flag.type} />
</div>
<span className="tw-font-medium tw-text-left">{title}</span>
</div>
<span className="tw-uppercase tw-font-bold">{flag.type}</span>
</div>,
<Flag key={key} data={flag} handleUpdate={handleUpdate} />,
key,
]
})}
/>
)
}

View file

@ -1,5 +1,5 @@
// Dependencies // Dependencies
import { missingMeasurements } from '../lib/index.mjs' import { missingMeasurements, flattenFlags } from '../lib/index.mjs'
// Hooks // Hooks
import React, { useState } from 'react' import React, { useState } from 'react'
// Components // Components
@ -7,9 +7,29 @@ import { Null } from './Null.mjs'
import { AsideViewMenuSpacer } from './AsideViewMenu.mjs' import { AsideViewMenuSpacer } from './AsideViewMenu.mjs'
import { ViewIcon, viewLabels } from './views/index.mjs' import { ViewIcon, viewLabels } from './views/index.mjs'
import { Tooltip } from './Tooltip.mjs' import { Tooltip } from './Tooltip.mjs'
import { ErrorIcon } from '@freesewing/react/components/Icon' import {
CircleIcon,
DetailIcon,
ErrorIcon,
ExpandIcon,
ExportIcon,
KioskIcon,
MenuIcon,
PaperlessIcon,
ResetAllIcon,
RocketIcon,
RotateIcon,
SaIcon,
SaveAsIcon,
SaveIcon,
TrashIcon,
UndoIcon,
UnitsIcon,
} from '@freesewing/react/components/Icon'
import { DesignOptionsMenu } from './menus/DesignOptionsMenu.mjs' import { DesignOptionsMenu } from './menus/DesignOptionsMenu.mjs'
import { CoreSettingsMenu } from './menus/CoreSettingsMenu.mjs' import { CoreSettingsMenu } from './menus/CoreSettingsMenu.mjs'
import { UiPreferencesMenu } from './menus/UiPreferencesMenu.mjs'
import { FlagsAccordionEntries } from './Flag.mjs'
export const HeaderMenuAllViews = ({ config, state, update, open, setOpen }) => ( export const HeaderMenuAllViews = ({ config, state, update, open, setOpen }) => (
<HeaderMenuViewMenu {...{ config, state, update, open, setOpen }} /> <HeaderMenuViewMenu {...{ config, state, update, open, setOpen }} />

View file

@ -1,11 +1,12 @@
import React, { useState, useMemo, useCallback, forwardRef, useContext } from 'react' import React, { useState, useMemo, useCallback, forwardRef, useContext } from 'react'
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch' import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch'
import { Pattern } from '@freesewing/react/components/Pattern'
/* /*
* A pattern you can pan and zoom * A pattern you can pan and zoom
*/ */
export const ZoomablePattern = forwardRef(function ZoomablePatternRef(props, ref) { export const ZoomablePattern = forwardRef(function ZoomablePatternRef(props, ref) {
const { renderProps, Swizzled, rotate } = props const { renderProps, rotate } = props
const { onTransformed, setZoomFunctions } = useContext(ZoomContext) const { onTransformed, setZoomFunctions } = useContext(ZoomContext)
return ( return (
@ -24,9 +25,8 @@ export const ZoomablePattern = forwardRef(function ZoomablePatternRef(props, ref
id="pan-zoom-pattern" id="pan-zoom-pattern"
> >
{props.children || ( {props.children || (
<Swizzled.components.Pattern <Pattern
{...{ renderProps }} {...{ renderProps }}
t={Swizzled.methods.t}
ref={ref} ref={ref}
className={`freesewing pattern w-full ${rotate ? '-rotate-90' : ''}`} className={`freesewing pattern w-full ${rotate ? '-rotate-90' : ''}`}
/> />

View file

@ -5,6 +5,22 @@ import {
menuCoreSettingsSammHandler, menuCoreSettingsSammHandler,
menuCoreSettingsStructure, menuCoreSettingsStructure,
} from '../../lib/index.mjs' } from '../../lib/index.mjs'
import {
MenuBoolInput,
MenuListInput,
MenuMmInput,
MenuOnlySettingInput,
MenuSliderInput,
} from './Input.mjs'
import {
//MenuBoolValue,
MenuListValue,
MenuMmValue,
MenuOnlySettingValue,
MenuScaleSettingValue,
} from './Value.mjs'
import { MenuItemGroup } from './Container.mjs'
import { SettingsIcon } from '@freesewing/react/components/Icon'
/** /**
* The core settings menu * The core settings menu

View file

@ -0,0 +1,60 @@
// Depdendencies
import React from 'react'
import { menuUiPreferencesStructure } from '../../lib/index.mjs'
// Components
import { MenuUxSettingInput, MenuListInput } from './Input.mjs'
import { MenuListValue } from './Value.mjs'
import { MenuItemGroup } from './Container.mjs'
import { Ux } from '@freesewing/react/components/Ux'
export const UiPreferencesMenu = ({ update, state, Design }) => {
console.log(state)
const structure = menuUiPreferencesStructure()
const drillProps = { Design, state, update }
const inputs = {
ux: (props) => <MenuUxSettingInput {...drillProps} {...props} />,
aside: (props) => <MenuListInput {...drillProps} {...props} />,
kiosk: (props) => <MenuListInput {...drillProps} {...props} />,
rotate: (props) => <MenuListInput {...drillProps} {...props} />,
renderer: (props) => <MenuListInput {...drillProps} {...props} />,
}
const values = {
ux: (props) => <Ux ux={state.ui.ux} {...props} />,
aside: MenuListValue,
kiosk: MenuListValue,
rotate: MenuListValue,
renderer: MenuListValue,
}
return (
<MenuItemGroup
{...{
structure,
ux: state.ui?.ux,
currentValues: state.ui || {},
Item: (props) => (
<UiPreference updateHandler={update} {...{ inputs, values, Design }} {...props} />
),
isFirst: true,
name: 'pe:uiPreferences',
language: state.locale,
passProps: {
ux: state.ui?.ux,
settings: state.settings,
patternConfig: Design.patternConfig,
},
updateHandler: update.ui,
isDesignOptionsGroup: false,
state,
Design,
inputs,
values,
}}
/>
)
}
export const UiPreference = ({ name, ux, ...rest }) => (
<MenuItem {...rest} name={name} allowToggle={!['ux', 'view'].includes(name) && ux > 3} ux={ux} />
)

View file

@ -1,34 +1,62 @@
import React from 'react' import React from 'react'
import { mergeOptions } from '@freesewing/core' import { mergeOptions } from '@freesewing/core'
import { formatMm } from '@freesewing/utils'
import { BoolYesIcon, BoolNoIcon } from '@freesewing/react/components/Icon'
/** Displays that constant values are not implemented in the front end */ /**
* this method is here to capture deprecated use of the translation method
*
* @param {string} key - The translation key
* @retunr {string} key - Returns the key as-is
*/
const t = (key) => {
console.log('FIXME: t method called in react/components/Editor/components/menus/Value.mjs')
return key
}
/**
* Displays that constant values are not implemented in the front end
*/
export const MenuConstantOptionValue = () => ( export const MenuConstantOptionValue = () => (
<span className="text-error">FIXME: No ConstantOptionvalue implemented</span> <span className="text-error">FIXME: No ConstantOptionvalue implemented</span>
) )
/** Displays a count value*/ /**
* Displays a count value
*
* @param {object} config - The option config
* @param {number} current - The current (count) value
* @param {bool} changed - Whether or not the value is non-default
*/
export const MenuCountOptionValue = ({ config, current, changed }) => ( export const MenuCountOptionValue = ({ config, current, changed }) => (
<MenuShowValue {...{ current, changed, dflt: config.count }} /> <MenuShowValue {...{ current, changed, dflt: config.count }} />
) )
/** Displays a degree value */ /**
* Displays a degree value
*
* @param {object} config - The option config
* @param {number} current - The current (count) value
* @param {bool} changed - Whether or not the value is non-default
*/
export const MenuDegOptionValue = ({ config, current, changed }) => ( export const MenuDegOptionValue = ({ config, current, changed }) => (
<MenuHighlightValue changed={changed}> {changed ? current : config.deg}&deg;</MenuHighlightValue> <MenuHighlightValue changed={changed}> {changed ? current : config.deg}&deg;</MenuHighlightValue>
) )
/** /**
* A component to highlight a changed value * A component to highlight a changed value
* @param {Boolean} changed - Whether the value is changed or not *
* @param {Boolean} changed - Whether the value is non-default
* @param {Function} children - The React children * @param {Function} children - The React children
*/ */
export const MenuHighlightValue = ({ changed, children }) => ( export const MenuHighlightValue = ({ changed, children }) => (
<span className={changed ? 'text-accent' : ''}> {children} </span> <span className={changed ? 'text-accent' : ''}> {children} </span>
) )
/** Displays a list option value */ /**
export const MenuListOptionValue = (props) => ( * Displays a list option value
<MenuListValue {...props} t={(input) => 'fixme handle option translation'} /> */
) export const MenuListOptionValue = (props) => <MenuListValue {...props} t={t} />
/** /**
* Displays the correct, translated value for a list * Displays the correct, translated value for a list
@ -51,20 +79,26 @@ export const MenuListValue = ({ current, config, changed }) => {
else if (val) key = <BoolYesIcon /> else if (val) key = <BoolYesIcon />
else key = <BoolNoIcon /> else key = <BoolNoIcon />
const translated = config.doNotTranslate || typeof key !== 'string' ? key : t(key) const translated = config.doNotTranslate || key
return <MenuHighlightValue changed={changed}>{translated}</MenuHighlightValue> return <MenuHighlightValue changed={changed}>{translated}</MenuHighlightValue>
} }
/** Displays the corrent, translated value for a boolean */ /**
* Displays the corrent, translated value for a boolean
*/
export const MenuBoolValue = MenuListOptionValue export const MenuBoolValue = MenuListOptionValue
/** Displays the MmOptions are not supported */ /**
* Displays the MmOptions are not supported
*/
export const MenuMmOptionValue = () => ( export const MenuMmOptionValue = () => (
<span className="text-error">FIXME: No Mm Options are not supported</span> <span className="text-error">FIXME: No Mm Options are not supported</span>
) )
/** Displays a formated mm value based on the current units */ /**
* Displays a formated mm value based on the current units
*/
export const MenuMmValue = ({ current, config, units, changed }) => ( export const MenuMmValue = ({ current, config, units, changed }) => (
<MenuHighlightValue changed={changed}> <MenuHighlightValue changed={changed}>
<span <span
@ -75,7 +109,8 @@ export const MenuMmValue = ({ current, config, units, changed }) => (
</MenuHighlightValue> </MenuHighlightValue>
) )
/** Displays the current percentage value, and the absolute value if configured /**
* Displays the current percentage value, and the absolute value if configured
* *
************************************************************************** **************************************************************************
* SliderIcon Title THIS * * SliderIcon Title THIS *
@ -83,7 +118,7 @@ export const MenuMmValue = ({ current, config, units, changed }) => (
* ----------------------0----------------------------------------------- * * ----------------------0----------------------------------------------- *
* msg PencilIcon ResetIcon * * msg PencilIcon ResetIcon *
************************************************************************** **************************************************************************
* */ */
export const MenuPctOptionValue = ({ config, current, settings, changed, patternConfig }) => { export const MenuPctOptionValue = ({ config, current, settings, changed, patternConfig }) => {
const val = changed ? current : config.pct / 100 const val = changed ? current : config.pct / 100
@ -101,6 +136,7 @@ export const MenuPctOptionValue = ({ config, current, settings, changed, pattern
/** /**
* A component to display a value, highligting it if it changed * A component to display a value, highligting it if it changed
*
* @param {Number|String|Boolean} options.current - The current value, if it has been changed * @param {Number|String|Boolean} options.current - The current value, if it has been changed
* @param {Number|String|Boolean} options.dflt - The default value * @param {Number|String|Boolean} options.dflt - The default value
* @param {Boolean} options.changed - Has the value been changed? * @param {Boolean} options.changed - Has the value been changed?
@ -109,10 +145,24 @@ export const MenuShowValue = ({ current, dflt, changed }) => {
return <MenuHighlightValue changed={changed}> {changed ? current : dflt} </MenuHighlightValue> return <MenuHighlightValue changed={changed}> {changed ? current : dflt} </MenuHighlightValue>
} }
/**
* Displays the value for core's scale setting
*
* @param {object} config - The option config
* @param {number} current - The current (count) value
* @param {bool} changed - Whether or not the value is non-default
*/
export const MenuScaleSettingValue = ({ current, config, changed }) => ( export const MenuScaleSettingValue = ({ current, config, changed }) => (
<MenuHighlightValue current={current} dflt={config.dflt} changed={changed} /> <MenuHighlightValue current={current} dflt={config.dflt} changed={changed} />
) )
/**
* Displays the value for core's only setting
*
* @param {object} config - The option config
* @param {number} current - The current (count) value
* @param {bool} changed - Whether or not the value is non-default
*/
export const MenuOnlySettingValue = ({ current, config }) => ( export const MenuOnlySettingValue = ({ current, config }) => (
<MenuHighlightValue <MenuHighlightValue
current={current?.length} current={current?.length}

View file

@ -1,60 +0,0 @@
export const UiPreferencesMenu = ({ Swizzled, update, state, Design }) => {
const structure = Swizzled.methods.menuUiPreferencesStructure()
const drillProps = { Design, state, update }
const inputs = {
ux: (props) => <Swizzled.components.MenuUxSettingInput {...drillProps} {...props} />,
aside: (props) => <Swizzled.components.MenuListInput {...drillProps} {...props} />,
kiosk: (props) => <Swizzled.components.MenuListInput {...drillProps} {...props} />,
rotate: (props) => <Swizzled.components.MenuListInput {...drillProps} {...props} />,
renderer: (props) => <Swizzled.components.MenuListInput {...drillProps} {...props} />,
}
const values = {
ux: (props) => <Swizzled.components.Ux ux={state.ui.ux} {...props} />,
aside: Swizzled.components.MenuListValue,
kiosk: Swizzled.components.MenuListValue,
rotate: Swizzled.components.MenuListValue,
renderer: Swizzled.components.MenuListValue,
}
return (
<Swizzled.components.MenuItemGroup
{...{
structure,
ux: state.ui?.ux,
currentValues: state.ui || {},
Item: (props) => (
<Swizzled.components.UiPreference
updateHandler={update}
{...{ inputs, values, Swizzled, Design }}
{...props}
/>
),
isFirst: true,
name: 'pe:uiPreferences',
language: state.locale,
passProps: {
ux: state.ui?.ux,
settings: state.settings,
patternConfig: Design.patternConfig,
},
updateHandler: update.ui,
isDesignOptionsGroup: false,
Swizzled,
state,
Design,
inputs,
values,
}}
/>
)
}
export const UiPreference = ({ Swizzled, name, ux, ...rest }) => (
<Swizzled.components.MenuItem
{...rest}
name={name}
allowToggle={!['ux', 'view'].includes(name) && ux > 3}
ux={ux}
/>
)

View file

@ -100,7 +100,7 @@ export const viewLabels = {
t: 'Choose a different view', t: 'Choose a different view',
d: 'fixme', d: 'fixme',
}, },
undos: { undo: {
t: 'Undo History', t: 'Undo History',
d: 'Time-travel through your recent pattern changes', d: 'Time-travel through your recent pattern changes',
}, },

View file

@ -1,3 +1,5 @@
// Dependencies
import { defaultConfig } from '../config/index.mjs'
// Components // Components
import { import {
ErrorIcon, ErrorIcon,
@ -35,9 +37,10 @@ export function draft(Design, settings) {
return data return data
} }
export function flattenFlags(flags, config) {
export function flattenFlags(flags) {
const all = {} const all = {}
for (const type of config.flagTypes) { for (const type of defaultConfig.flagTypes) {
let i = 0 let i = 0
if (flags[type]) { if (flags[type]) {
for (const flag of Object.values(flags[type])) { for (const flag of Object.values(flags[type])) {

View file

@ -1,13 +1,22 @@
export function menuUiPreferencesStructure(Swizzled) { import { defaultConfig } from '../config/index.mjs'
const uiUx = Swizzled.config.uxLevels.ui import {
MenuIcon,
KioskIcon,
RotateIcon,
RocketIcon,
UxIcon,
} from '@freesewing/react/components/Icon'
export function menuUiPreferencesStructure() {
const uiUx = defaultConfig.uxLevels.ui
const uiPreferences = { const uiPreferences = {
ux: { ux: {
ux: uiUx.ux, ux: uiUx.ux,
emoji: '🖥️', emoji: '🖥️',
list: [1, 2, 3, 4, 5], list: [1, 2, 3, 4, 5],
choiceTitles: {}, choiceTitles: {},
icon: Swizzled.components.UxIcon, icon: UxIcon,
dflt: Swizzled.config.defaultUx, dflt: defaultConfig.defaultUx,
}, },
aside: { aside: {
ux: uiUx.aside, ux: uiUx.aside,
@ -17,7 +26,7 @@ export function menuUiPreferencesStructure(Swizzled) {
1: 'pe:withAside', 1: 'pe:withAside',
}, },
dflt: 1, dflt: 1,
icon: Swizzled.components.MenuIcon, icon: MenuIcon,
}, },
kiosk: { kiosk: {
ux: uiUx.kiosk, ux: uiUx.kiosk,
@ -27,7 +36,7 @@ export function menuUiPreferencesStructure(Swizzled) {
1: 'pe:kioskMode', 1: 'pe:kioskMode',
}, },
dflt: 0, dflt: 0,
icon: Swizzled.components.KioskIcon, icon: KioskIcon,
}, },
rotate: { rotate: {
ux: uiUx.rotate, ux: uiUx.rotate,
@ -37,7 +46,7 @@ export function menuUiPreferencesStructure(Swizzled) {
1: 'pe:rotateYes', 1: 'pe:rotateYes',
}, },
dflt: 0, dflt: 0,
icon: Swizzled.components.RotateIcon, icon: RotateIcon,
}, },
renderer: { renderer: {
ux: uiUx.renderer, ux: uiUx.renderer,
@ -51,7 +60,7 @@ export function menuUiPreferencesStructure(Swizzled) {
svg: 'SVG', svg: 'SVG',
}, },
dflt: 'react', dflt: 'react',
icon: Swizzled.components.RocketIcon, icon: RocketIcon,
}, },
} }

View file

@ -1,103 +0,0 @@
import mustache from 'mustache'
export const FlagTypeIcon = ({ Swizzled, type, className = 'w-6 h-6' }) => {
const Icon = Swizzled.components[`Flag${Swizzled.methods.capitalize(type)}Icon`]
return Icon ? <Icon className={className} /> : null
}
export const Flag = ({ Swizzled, data, handleUpdate }) => {
const btnIcon = data.suggest?.icon ? (
<Swizzled.components.FlagTypeIcon type={data.suggest.icon} className="w-5 h-6 sm:w-6 h-6" />
) : null
const { t } = Swizzled.methods
const button =
data.suggest?.text && data.suggest?.update ? (
<button
className={`btn btn-secondary btn-outline flex flex-row items-center ${
btnIcon ? 'gap-6' : ''
}`}
onClick={() => handleUpdate(data.suggest.update)}
>
{btnIcon}
{t(data.suggest.text)}
</button>
) : null
const desc = data.replace ? mustache.render(t(data.desc), data.replace) : t(data.desc)
const notes = data.notes
? Array.isArray(data.notes)
? '\n\n' +
data.notes
.map((note) => (data.replace ? mustache.render(t(note), data.replace) : t(note)))
.join('\n\n')
: '\n\n' + (data.replace ? mustache.render(t(data.notes), data.replace) : t(data.notes))
: null
return (
<div className="flex flex-col gap-2 items-start">
<div className="first:mt-0 grow md flag">
<pre>{desc}</pre>
<pre>{notes}</pre>
</div>
{button ? <div className="mt-2 w-full flex flex-row justify-end">{button}</div> : null}
</div>
)
}
//<Mdx md={notes ? desc + notes : desc} />
export const FlagsAccordionTitle = ({ flags, Swizzled }) => {
const { t } = Swizzled.methods
const flagList = Swizzled.methods.flattenFlags(flags)
if (Object.keys(flagList).length < 1) return null
return (
<>
<h5 className="flex flex-row gap-2 items-center justify-between w-full">
<span className="text-left">
{t('pe:flagMenu.t')} ({Object.keys(flagList).length})
</span>
<Swizzled.components.FlagTypeIcon className="w-8 h-8" />
</h5>
<p className="text-left">
{Object.keys(flagList).length > 1 ? t('pe:flagMenuMany.d') : t('pe:flagMenuOne.d')}
</p>
</>
)
}
export const FlagsAccordionEntries = ({ flags, update, Swizzled }) => {
const flagList = Swizzled.methods.flattenFlags(flags)
const { t } = Swizzled.methods
if (Object.keys(flagList).length < 1) return null
const handleUpdate = (config) => {
if (config.settings) update.settings(...config.settings)
if (config.ui) update.ui(...config.settings)
}
return (
<Swizzled.components.SubAccordion
items={Object.entries(flagList).map(([key, flag], i) => {
const title = flag.replace ? mustache.render(t(flag.title), flag.replace) : t(flag.title)
return [
<div className="w-full flex flex-row gap2 justify-between" key={i}>
<div className="flex flex-row items-center gap-2">
<div className="no-shrink">
<Swizzled.components.FlagIcon type={flag.type} />
</div>
<span className="font-medium text-left">{title}</span>
</div>
<span className="uppercase font-bold">{flag.type}</span>
</div>,
<Swizzled.components.Flag key={key} t={t} data={flag} handleUpdate={handleUpdate} />,
key,
]
})}
/>
)
}

View file

@ -1,12 +0,0 @@
export const Ux = ({ Swizzled, ux = 0 }) => (
<div className="flex flex-row">
{[0, 1, 2, 3, 4].map((i) => (
<Swizzled.components.CircleIcon
key={i}
fill={i < ux ? true : false}
className={`w-6 h-6 ${i < ux ? 'stroke-secondary fill-secondary' : 'stroke-current'}`}
fillOpacity={0.3}
/>
))}
</div>
)

View file

@ -242,6 +242,13 @@ export const FingerprintIcon = (props) => (
</IconWrapper> </IconWrapper>
) )
// Looks lik an exclamation point inside a circle
export const FixmeIcon = (props) => (
<IconWrapper {...props}>
<path d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
</IconWrapper>
)
// Looks lik a flag // Looks lik a flag
export const FlagIcon = (props) => ( export const FlagIcon = (props) => (
<IconWrapper {...props}> <IconWrapper {...props}>
@ -753,12 +760,3 @@ export const ViewDocsIcon = DocsIcon
export const ViewDesignsIcon = DesignIcon export const ViewDesignsIcon = DesignIcon
export const ViewViewPickerIcon = UiIcon export const ViewViewPickerIcon = UiIcon
export const ViewUndosIcon = BackIcon export const ViewUndosIcon = BackIcon
// Flag icons
export const FlagNoteIcon = ChatIcon
export const FlagInfoIcon = DocsIcon
export const FlagTipIcon = TipIcon
export const FlagWarningIcon = WarningIcon
export const FlagErrorIcon = ErrorIcon
export const FlagFixmeIcon = WrenchIcon
export const FlagExpandIcon = ExpandIcon
export const FlagOtionsIcon = OptionsIcon

View file

@ -0,0 +1,15 @@
import React from 'react'
import { CircleIcon } from '@freesewing/react/components/Icon'
export const Ux = ({ ux = 0 }) => (
<div className="flex flex-row">
{[0, 1, 2, 3, 4].map((i) => (
<CircleIcon
key={i}
fill={i < ux ? true : false}
className={`tw-w-6 tw-h-6 ${i < ux ? 'tw-stroke-secondary tw-fill-secondary' : 'tw-stroke-current'}`}
fillOpacity={0.3}
/>
))}
</div>
)

View file

@ -58,6 +58,7 @@
"./components/Table": "./components/Table/index.mjs", "./components/Table": "./components/Table/index.mjs",
"./components/Time": "./components/Time/index.mjs", "./components/Time": "./components/Time/index.mjs",
"./components/Uuid": "./components/Uuid/index.mjs", "./components/Uuid": "./components/Uuid/index.mjs",
"./components/Ux": "./components/Ux/index.mjs",
"./components/Yaml": "./components/Yaml/index.mjs", "./components/Yaml": "./components/Yaml/index.mjs",
"./components/Xray": "./components/Xray/index.mjs", "./components/Xray": "./components/Xray/index.mjs",
"./context/LoadingStatus": "./context/LoadingStatus/index.mjs", "./context/LoadingStatus": "./context/LoadingStatus/index.mjs",
@ -81,12 +82,13 @@
"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",
"use-session-storage-state": "^19.0.0" "use-session-storage-state": "^19.0.0"
}, },
"devDependencies": {},
"files": [ "files": [
"components/**", "components/**",
"hooks/**", "hooks/**",