From c8b989afcbe62a59d31040dde39d382e2415a27e Mon Sep 17 00:00:00 2001 From: joostdecock Date: Sun, 19 Jan 2025 11:01:34 +0100 Subject: [PATCH] wip: work on editor --- config/exceptions.yaml | 1 + .../components/Editor/components/Flag.mjs | 131 ++++++++++++++++++ .../Editor/components/HeaderMenu.mjs | 24 +++- .../Editor/components/ZoomablePattern.mjs | 6 +- .../components/menus/CoreSettingsMenu.mjs | 16 +++ .../components/menus/UiPreferencesMenu.mjs | 60 ++++++++ .../Editor/components/menus/Value.mjs | 78 +++++++++-- .../components/menus/ui-preferences-menu.mjs | 60 -------- .../Editor/components/views/index.mjs | 2 +- .../react/components/Editor/lib/editor.mjs | 7 +- .../components/Editor/lib/ui-preferences.mjs | 25 ++-- .../Editor/swizzle/components/flags.mjs | 103 -------------- .../Editor/swizzle/components/ux.mjs | 12 -- packages/react/components/Icon/index.mjs | 16 +-- packages/react/components/Ux/index.mjs | 15 ++ packages/react/package.json | 4 +- 16 files changed, 345 insertions(+), 215 deletions(-) create mode 100644 packages/react/components/Editor/components/Flag.mjs create mode 100644 packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs delete mode 100644 packages/react/components/Editor/components/menus/ui-preferences-menu.mjs delete mode 100644 packages/react/components/Editor/swizzle/components/flags.mjs delete mode 100644 packages/react/components/Editor/swizzle/components/ux.mjs create mode 100644 packages/react/components/Ux/index.mjs diff --git a/config/exceptions.yaml b/config/exceptions.yaml index 124f370d643..3b47dc85614 100644 --- a/config/exceptions.yaml +++ b/config/exceptions.yaml @@ -105,6 +105,7 @@ packageJson: "./components/Table": "./components/Table/index.mjs" "./components/Time": "./components/Time/index.mjs" "./components/Uuid": "./components/Uuid/index.mjs" + "./components/Ux": "./components/Ux/index.mjs" "./components/Yaml": "./components/Yaml/index.mjs" "./components/Xray": "./components/Xray/index.mjs" # Context diff --git a/packages/react/components/Editor/components/Flag.mjs b/packages/react/components/Editor/components/Flag.mjs new file mode 100644 index 00000000000..e93b4bf5ec5 --- /dev/null +++ b/packages/react/components/Editor/components/Flag.mjs @@ -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 +} + +export const Flag = ({ data, handleUpdate }) => { + const btnIcon = data.suggest?.icon ? ( + + ) : null + + const button = + data.suggest?.text && data.suggest?.update ? ( + + ) : 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 ( +
+
+
{desc}
+
{notes}
+
+ {button ? ( +
{button}
+ ) : null} +
+ ) +} + +export const FlagsAccordionTitle = ({ flags }) => { + const flagList = flattenFlags(flags) + + if (Object.keys(flagList).length < 1) return null + + return ( + <> +
+ Flags ({Object.keys(flagList).length}) + +
+

+ {Object.keys(flagList).length > 1 + ? 'Some issues about your current pattern need your attention.' + : 'A specific issue about your current pattern needs your attention.'} +

+ + ) +} + +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 ( + { + const title = flag.replace ? mustache.render(flag.title, flag.replace) : flag.title + + return [ +
+
+
+ +
+ {title} +
+ {flag.type} +
, + , + key, + ] + })} + /> + ) +} diff --git a/packages/react/components/Editor/components/HeaderMenu.mjs b/packages/react/components/Editor/components/HeaderMenu.mjs index b3cbe371a90..6416a2b5852 100644 --- a/packages/react/components/Editor/components/HeaderMenu.mjs +++ b/packages/react/components/Editor/components/HeaderMenu.mjs @@ -1,5 +1,5 @@ // Dependencies -import { missingMeasurements } from '../lib/index.mjs' +import { missingMeasurements, flattenFlags } from '../lib/index.mjs' // Hooks import React, { useState } from 'react' // Components @@ -7,9 +7,29 @@ import { Null } from './Null.mjs' import { AsideViewMenuSpacer } from './AsideViewMenu.mjs' import { ViewIcon, viewLabels } from './views/index.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 { CoreSettingsMenu } from './menus/CoreSettingsMenu.mjs' +import { UiPreferencesMenu } from './menus/UiPreferencesMenu.mjs' +import { FlagsAccordionEntries } from './Flag.mjs' export const HeaderMenuAllViews = ({ config, state, update, open, setOpen }) => ( diff --git a/packages/react/components/Editor/components/ZoomablePattern.mjs b/packages/react/components/Editor/components/ZoomablePattern.mjs index 8d6566c0d16..7e71cda3317 100644 --- a/packages/react/components/Editor/components/ZoomablePattern.mjs +++ b/packages/react/components/Editor/components/ZoomablePattern.mjs @@ -1,11 +1,12 @@ import React, { useState, useMemo, useCallback, forwardRef, useContext } from 'react' import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch' +import { Pattern } from '@freesewing/react/components/Pattern' /* * A pattern you can pan and zoom */ export const ZoomablePattern = forwardRef(function ZoomablePatternRef(props, ref) { - const { renderProps, Swizzled, rotate } = props + const { renderProps, rotate } = props const { onTransformed, setZoomFunctions } = useContext(ZoomContext) return ( @@ -24,9 +25,8 @@ export const ZoomablePattern = forwardRef(function ZoomablePatternRef(props, ref id="pan-zoom-pattern" > {props.children || ( - diff --git a/packages/react/components/Editor/components/menus/CoreSettingsMenu.mjs b/packages/react/components/Editor/components/menus/CoreSettingsMenu.mjs index 890e96e8fba..3c604677322 100644 --- a/packages/react/components/Editor/components/menus/CoreSettingsMenu.mjs +++ b/packages/react/components/Editor/components/menus/CoreSettingsMenu.mjs @@ -5,6 +5,22 @@ import { menuCoreSettingsSammHandler, menuCoreSettingsStructure, } 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 diff --git a/packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs b/packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs new file mode 100644 index 00000000000..ea578d6396c --- /dev/null +++ b/packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs @@ -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) => , + aside: (props) => , + kiosk: (props) => , + rotate: (props) => , + renderer: (props) => , + } + const values = { + ux: (props) => , + aside: MenuListValue, + kiosk: MenuListValue, + rotate: MenuListValue, + renderer: MenuListValue, + } + + return ( + ( + + ), + 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 }) => ( + 3} ux={ux} /> +) diff --git a/packages/react/components/Editor/components/menus/Value.mjs b/packages/react/components/Editor/components/menus/Value.mjs index f72c49f32e1..fef47b76c7e 100644 --- a/packages/react/components/Editor/components/menus/Value.mjs +++ b/packages/react/components/Editor/components/menus/Value.mjs @@ -1,34 +1,62 @@ import React from 'react' 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 = () => ( FIXME: No ConstantOptionvalue implemented ) -/** 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 }) => ( ) -/** 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 }) => ( {changed ? current : config.deg}° ) /** * 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 */ export const MenuHighlightValue = ({ changed, children }) => ( {children} ) -/** Displays a list option value */ -export const MenuListOptionValue = (props) => ( - 'fixme handle option translation'} /> -) +/** + * Displays a list option value + */ +export const MenuListOptionValue = (props) => /** * Displays the correct, translated value for a list @@ -51,20 +79,26 @@ export const MenuListValue = ({ current, config, changed }) => { else if (val) key = else key = - const translated = config.doNotTranslate || typeof key !== 'string' ? key : t(key) + const translated = config.doNotTranslate || key return {translated} } -/** Displays the corrent, translated value for a boolean */ +/** + * Displays the corrent, translated value for a boolean + */ export const MenuBoolValue = MenuListOptionValue -/** Displays the MmOptions are not supported */ +/** + * Displays the MmOptions are not supported + */ export const MenuMmOptionValue = () => ( FIXME: No Mm Options are not supported ) -/** 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 }) => ( ( ) -/** 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 * @@ -83,7 +118,7 @@ export const MenuMmValue = ({ current, config, units, changed }) => ( * ----------------------0----------------------------------------------- * * msg PencilIcon ResetIcon * ************************************************************************** - * */ + */ export const MenuPctOptionValue = ({ config, current, settings, changed, patternConfig }) => { 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 + * * @param {Number|String|Boolean} options.current - The current value, if it has been changed * @param {Number|String|Boolean} options.dflt - The default value * @param {Boolean} options.changed - Has the value been changed? @@ -109,10 +145,24 @@ export const MenuShowValue = ({ current, dflt, changed }) => { return {changed ? current : dflt} } +/** + * 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 }) => ( ) +/** + * 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 }) => ( { - const structure = Swizzled.methods.menuUiPreferencesStructure() - - const drillProps = { Design, state, update } - const inputs = { - ux: (props) => , - aside: (props) => , - kiosk: (props) => , - rotate: (props) => , - renderer: (props) => , - } - const values = { - ux: (props) => , - aside: Swizzled.components.MenuListValue, - kiosk: Swizzled.components.MenuListValue, - rotate: Swizzled.components.MenuListValue, - renderer: Swizzled.components.MenuListValue, - } - - return ( - ( - - ), - 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 }) => ( - 3} - ux={ux} - /> -) diff --git a/packages/react/components/Editor/components/views/index.mjs b/packages/react/components/Editor/components/views/index.mjs index b8970b76fc4..6a488346b93 100644 --- a/packages/react/components/Editor/components/views/index.mjs +++ b/packages/react/components/Editor/components/views/index.mjs @@ -100,7 +100,7 @@ export const viewLabels = { t: 'Choose a different view', d: 'fixme', }, - undos: { + undo: { t: 'Undo History', d: 'Time-travel through your recent pattern changes', }, diff --git a/packages/react/components/Editor/lib/editor.mjs b/packages/react/components/Editor/lib/editor.mjs index 0e0b1566ec1..611458fbf50 100644 --- a/packages/react/components/Editor/lib/editor.mjs +++ b/packages/react/components/Editor/lib/editor.mjs @@ -1,3 +1,5 @@ +// Dependencies +import { defaultConfig } from '../config/index.mjs' // Components import { ErrorIcon, @@ -35,9 +37,10 @@ export function draft(Design, settings) { return data } -export function flattenFlags(flags, config) { + +export function flattenFlags(flags) { const all = {} - for (const type of config.flagTypes) { + for (const type of defaultConfig.flagTypes) { let i = 0 if (flags[type]) { for (const flag of Object.values(flags[type])) { diff --git a/packages/react/components/Editor/lib/ui-preferences.mjs b/packages/react/components/Editor/lib/ui-preferences.mjs index 39c61b157fd..119984d7f6e 100644 --- a/packages/react/components/Editor/lib/ui-preferences.mjs +++ b/packages/react/components/Editor/lib/ui-preferences.mjs @@ -1,13 +1,22 @@ -export function menuUiPreferencesStructure(Swizzled) { - const uiUx = Swizzled.config.uxLevels.ui +import { defaultConfig } from '../config/index.mjs' +import { + MenuIcon, + KioskIcon, + RotateIcon, + RocketIcon, + UxIcon, +} from '@freesewing/react/components/Icon' + +export function menuUiPreferencesStructure() { + const uiUx = defaultConfig.uxLevels.ui const uiPreferences = { ux: { ux: uiUx.ux, emoji: '🖥️', list: [1, 2, 3, 4, 5], choiceTitles: {}, - icon: Swizzled.components.UxIcon, - dflt: Swizzled.config.defaultUx, + icon: UxIcon, + dflt: defaultConfig.defaultUx, }, aside: { ux: uiUx.aside, @@ -17,7 +26,7 @@ export function menuUiPreferencesStructure(Swizzled) { 1: 'pe:withAside', }, dflt: 1, - icon: Swizzled.components.MenuIcon, + icon: MenuIcon, }, kiosk: { ux: uiUx.kiosk, @@ -27,7 +36,7 @@ export function menuUiPreferencesStructure(Swizzled) { 1: 'pe:kioskMode', }, dflt: 0, - icon: Swizzled.components.KioskIcon, + icon: KioskIcon, }, rotate: { ux: uiUx.rotate, @@ -37,7 +46,7 @@ export function menuUiPreferencesStructure(Swizzled) { 1: 'pe:rotateYes', }, dflt: 0, - icon: Swizzled.components.RotateIcon, + icon: RotateIcon, }, renderer: { ux: uiUx.renderer, @@ -51,7 +60,7 @@ export function menuUiPreferencesStructure(Swizzled) { svg: 'SVG', }, dflt: 'react', - icon: Swizzled.components.RocketIcon, + icon: RocketIcon, }, } diff --git a/packages/react/components/Editor/swizzle/components/flags.mjs b/packages/react/components/Editor/swizzle/components/flags.mjs deleted file mode 100644 index c549905f1fd..00000000000 --- a/packages/react/components/Editor/swizzle/components/flags.mjs +++ /dev/null @@ -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 ? : null -} - -export const Flag = ({ Swizzled, data, handleUpdate }) => { - const btnIcon = data.suggest?.icon ? ( - - ) : null - const { t } = Swizzled.methods - - const button = - data.suggest?.text && data.suggest?.update ? ( - - ) : 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 ( -
-
-
{desc}
-
{notes}
-
- {button ?
{button}
: null} -
- ) -} -// - -export const FlagsAccordionTitle = ({ flags, Swizzled }) => { - const { t } = Swizzled.methods - const flagList = Swizzled.methods.flattenFlags(flags) - - if (Object.keys(flagList).length < 1) return null - - return ( - <> -
- - {t('pe:flagMenu.t')} ({Object.keys(flagList).length}) - - -
-

- {Object.keys(flagList).length > 1 ? t('pe:flagMenuMany.d') : t('pe:flagMenuOne.d')} -

- - ) -} - -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 ( - { - const title = flag.replace ? mustache.render(t(flag.title), flag.replace) : t(flag.title) - - return [ -
-
-
- -
- {title} -
- {flag.type} -
, - , - key, - ] - })} - /> - ) -} diff --git a/packages/react/components/Editor/swizzle/components/ux.mjs b/packages/react/components/Editor/swizzle/components/ux.mjs deleted file mode 100644 index a48f72a2781..00000000000 --- a/packages/react/components/Editor/swizzle/components/ux.mjs +++ /dev/null @@ -1,12 +0,0 @@ -export const Ux = ({ Swizzled, ux = 0 }) => ( -
- {[0, 1, 2, 3, 4].map((i) => ( - - ))} -
-) diff --git a/packages/react/components/Icon/index.mjs b/packages/react/components/Icon/index.mjs index cfac80d20d9..487b52cf2c7 100644 --- a/packages/react/components/Icon/index.mjs +++ b/packages/react/components/Icon/index.mjs @@ -242,6 +242,13 @@ export const FingerprintIcon = (props) => ( ) +// Looks lik an exclamation point inside a circle +export const FixmeIcon = (props) => ( + + + +) + // Looks lik a flag export const FlagIcon = (props) => ( @@ -753,12 +760,3 @@ export const ViewDocsIcon = DocsIcon export const ViewDesignsIcon = DesignIcon export const ViewViewPickerIcon = UiIcon 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 diff --git a/packages/react/components/Ux/index.mjs b/packages/react/components/Ux/index.mjs new file mode 100644 index 00000000000..c01e1ffa99e --- /dev/null +++ b/packages/react/components/Ux/index.mjs @@ -0,0 +1,15 @@ +import React from 'react' +import { CircleIcon } from '@freesewing/react/components/Icon' + +export const Ux = ({ ux = 0 }) => ( +
+ {[0, 1, 2, 3, 4].map((i) => ( + + ))} +
+) diff --git a/packages/react/package.json b/packages/react/package.json index 74dc6d1af64..fe2bb32d462 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -58,6 +58,7 @@ "./components/Table": "./components/Table/index.mjs", "./components/Time": "./components/Time/index.mjs", "./components/Uuid": "./components/Uuid/index.mjs", + "./components/Ux": "./components/Ux/index.mjs", "./components/Yaml": "./components/Yaml/index.mjs", "./components/Xray": "./components/Xray/index.mjs", "./context/LoadingStatus": "./context/LoadingStatus/index.mjs", @@ -81,12 +82,13 @@ "highlight.js": "^11.11.0", "html-react-parser": "^5.0.7", "luxon": "^3.5.0", - "nuqs": "^2.3.0", + "nuqs": "^1.17.6", "react-markdown": "^9.0.1", "tlds": "^1.255.0", "use-local-storage-state": "19.1.0", "use-session-storage-state": "^19.0.0" }, + "devDependencies": {}, "files": [ "components/**", "hooks/**",