diff --git a/config/exceptions.yaml b/config/exceptions.yaml index 3b47dc85614..3dfd00412bd 100644 --- a/config/exceptions.yaml +++ b/config/exceptions.yaml @@ -115,6 +115,8 @@ packageJson: "./hooks/useAccount": "./hooks/useAccount/index.mjs" "./hooks/useBackend": "./hooks/useBackend/index.mjs" "./hooks/useControl": "./hooks/useControl/index.mjs" + "./hooks/useDesign": "./hooks/useDesign/index.mjs" + "./hooks/useDesignTranslation": "./hooks/useDesignTranslation/index.mjs" "./hooks/useSelection": "./hooks/useSelection/index.mjs" # Lib "./lib/RestClient": "./lib/RestClient/index.mjs" diff --git a/packages/react/components/Editor/components/Accordion.mjs b/packages/react/components/Editor/components/Accordion.mjs index d31b1cc3243..ea9a068dd20 100644 --- a/packages/react/components/Editor/components/Accordion.mjs +++ b/packages/react/components/Editor/components/Accordion.mjs @@ -4,14 +4,14 @@ import React, { useState } from 'react' * DaisyUI's accordion seems rather unreliable. * So instead, we handle this in React state */ -const getProps = (isActive) => ({ +const getProps = (isActive = false) => ({ className: `tw-p-2 tw-px-4 tw-rounded-lg tw-bg-transparent tw-shadow hover:tw-cursor-pointer - tw-w-full tw-mt-2 tw-py-4 tw-h-auto tw-content-start tw-text-left tw-bg-opacity-20 + tw-w-full tw-h-auto tw-content-start tw-text-left tw-bg-opacity-20 ${isActive ? 'hover:tw-bg-transparent' : 'hover:tw-bg-secondary hover:tw-bg-opacity-10'}`, }) const getSubProps = (isActive) => ({ - className: ` tw-p-2 tw-px-4 tw-rounded tw-bg-transparent tw-w-full tw-mt-2 tw-py-4 tw-h-auto + className: `tw-p-2 tw-px-4 tw-rounded-none tw-bg-transparent tw-w-full tw-h-auto tw-content-start tw-bg-secondary tw-text-left tw-bg-opacity-20 ${ isActive diff --git a/packages/react/components/Editor/components/HeaderMenu.mjs b/packages/react/components/Editor/components/HeaderMenu.mjs index d1d7e0443ed..6fa5598c6ab 100644 --- a/packages/react/components/Editor/components/HeaderMenu.mjs +++ b/packages/react/components/Editor/components/HeaderMenu.mjs @@ -14,8 +14,10 @@ import { ExpandIcon, ExportIcon, FixmeIcon, + FlagIcon, KioskIcon, MenuIcon, + OptionsIcon, PaperlessIcon, ResetAllIcon, RightIcon, @@ -24,24 +26,28 @@ import { SaIcon, SaveAsIcon, SaveIcon, + SettingsIcon, TrashIcon, + UiIcon, UndoIcon, UnitsIcon, } from '@freesewing/react/components/Icon' +import { ButtonFrame } from '@freesewing/react/components/Input' import { DesignOptionsMenu } from './menus/DesignOptionsMenu.mjs' import { CoreSettingsMenu } from './menus/CoreSettingsMenu.mjs' import { UiPreferencesMenu } from './menus/UiPreferencesMenu.mjs' import { FlagsAccordionEntries } from './Flag.mjs' +import { UndoStep } from './views/UndosView.mjs' /* * Lookup object for header menu icons */ const headerMenuIcons = { - flag: RocketIcon, - options: RocketIcon, + flag: FlagIcon, + options: OptionsIcon, right: RightIcon, - settings: RocketIcon, - ui: RocketIcon, + settings: SettingsIcon, + ui: UiIcon, } export const HeaderMenuIcon = (props) => { @@ -101,7 +107,7 @@ export const HeaderMenuDropdown = (props) => {
setOpen(open === id ? false : id)} > {toggle} @@ -129,11 +135,11 @@ export const HeaderMenuDraftViewDesignOptions = (props) => { - fixme: pe:designOptions.t + Design Options } > @@ -146,12 +152,12 @@ export const HeaderMenuDraftViewCoreSettings = (props) => { return ( - fixme: pe:coreSettings.t + Core Settings } > @@ -164,12 +170,12 @@ export const HeaderMenuDraftViewUiPreferences = (props) => { return ( - fixme: pe:uiPreferences.t + UI Preferences } > @@ -184,7 +190,7 @@ export const HeaderMenuDraftViewFlags = (props) => { return ( @@ -389,7 +395,7 @@ export const HeaderMenuUndoIcons = (props) => {
- {viewLabels.undo.t} + {viewLabels.undos.t}
{undos.length}
@@ -479,18 +485,18 @@ export const HeaderMenuViewMenu = (props) => { className="tw-mb-1 tw-flex tw-flex-row tw-items-center tw-justify-between tw-w-full" > update.view(viewName)} > - {viewLabels[viewName]?.t || viewName} + + {viewLabels[viewName]?.t || viewName} + ) @@ -511,7 +517,7 @@ export const HeaderMenuViewMenu = (props) => { >
    {output}
diff --git a/packages/react/components/Editor/components/menus/Container.mjs b/packages/react/components/Editor/components/menus/Container.mjs index 13553f83745..74da6f0d16d 100644 --- a/packages/react/components/Editor/components/menus/Container.mjs +++ b/packages/react/components/Editor/components/menus/Container.mjs @@ -1,14 +1,16 @@ // Dependencies import { menuValueWasChanged } from '../../lib/index.mjs' +import { designOptionType } from '@freesewing/utils' // Hooks import React, { useState, useMemo } from 'react' // Components import { SubAccordion } from '../Accordion.mjs' -import { GroupIcon, OptionsIcon } from '@freesewing/react/components/Icon' +import { EditIcon, GroupIcon, OptionsIcon, ResetIcon } from '@freesewing/react/components/Icon' import { CoreSettingsMenu } from './CoreSettingsMenu.mjs' +import { FormControl } from '@freesewing/react/components/Input' /** @type {String} class to apply to buttons on open menu items */ -const iconButtonClass = 'btn btn-xs btn-ghost px-0 text-accent' +const iconButtonClass = 'tw-daisy-btn tw-daisy-btn-xs tw-daisy-btn-ghost tw-px-0 tw-text-accent' /** * A generic component for handling a menu item. @@ -37,6 +39,7 @@ export const MenuItem = ({ docs, config, Design, + i18n, }) => { // Local state - whether the override input should be shown const [override, setOverride] = useState(false) @@ -78,15 +81,15 @@ export const MenuItem = ({ }} > ) const ResetButton = ({ disabled = false }) => ( @@ -416,9 +425,11 @@ export const MenuOnlySettingInput = (props) => { config.sideBySide = true config.titleMethod = (entry, t) => { const chunks = entry.split('.') - return {t(`${chunks[0]}:${chunks[1]}`)} + return {t(`${chunks[0]}:${chunks[1]}`)} } - config.valueMethod = (entry) => {capitalize(entry.split('.')[0])} + config.valueMethod = (entry) => ( + {capitalize(entry.split('.')[0])} + ) config.dense = true // Sort alphabetically (translated) const order = [] diff --git a/packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs b/packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs index ea578d6396c..bd48d2025ae 100644 --- a/packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs +++ b/packages/react/components/Editor/components/menus/UiPreferencesMenu.mjs @@ -8,7 +8,6 @@ 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 } diff --git a/packages/react/components/Editor/components/menus/Value.mjs b/packages/react/components/Editor/components/menus/Value.mjs index fef47b76c7e..10c8f02b4b2 100644 --- a/packages/react/components/Editor/components/menus/Value.mjs +++ b/packages/react/components/Editor/components/menus/Value.mjs @@ -1,6 +1,6 @@ import React from 'react' import { mergeOptions } from '@freesewing/core' -import { formatMm } from '@freesewing/utils' +import { formatMm, formatPercentage } from '@freesewing/utils' import { BoolYesIcon, BoolNoIcon } from '@freesewing/react/components/Icon' /** diff --git a/packages/react/components/Editor/components/views/UndosView.mjs b/packages/react/components/Editor/components/views/UndosView.mjs new file mode 100644 index 00000000000..83acfe17e7d --- /dev/null +++ b/packages/react/components/Editor/components/views/UndosView.mjs @@ -0,0 +1,134 @@ +import React from 'react' +import { orderBy } from '@freesewing/utils' +import { ButtonFrame } from '@freesewing/react/components/Input' +import { UndoIcon } from '@freesewing/react/components/Icon' + +import { getUndoStepData } from '../../lib/index.mjs' + +/** + * The undos view shows the undo history, and allows restoring any undo state + * + * @param (object) props - All the props + * @param {object} designs - Object holding all designs + * @param {object} update - ViewWrapper state update object + */ +export const UndosView = ({ Design, update, state }) => { + const steps = orderBy(state._.undos, 'time', 'desc') + + return ( + <> + +
+

Undo History

+

Time-travel through your recent pattern changes

+ + Tip: Click on any change to undo all changes up to, and including, that change. + + {steps.length < 1 ? ( + +

Your undo history is currently empty

+

When you make changes to your pattern, they will show up here.

+

For example, you can click the button below to change the pattern rotation:

+ +

As soon as you do, the change will show up here, and you can undo it.

+
+ ) : ( +
+ {steps.map((step, index) => ( + + ))} +
+ )} +
+ + ) +} + +export const UndoStepTimeAgo = ({ step }) => { + if (!step.time) return null + const secondsAgo = Math.floor((Date.now() - step.time) / 100) / 10 + const minutesAgo = Math.floor(secondsAgo / 60) + const hoursAgo = Math.floor(minutesAgo / 60) + + return hoursAgo ? ( + {hoursAgo} hours ago + ) : minutesAgo ? ( + {minutesAgo} minutes ago + ) : ( + {secondsAgo} seconds ago + ) +} + +export const UndoStep = ({ update, state, step, Design, compact = false, index = 0 }) => { + /* + * Ensure path is always an array + */ + if (!Array.isArray(step.path)) step.path = step.path.split('.') + + /* + * Figure this out once + */ + const imperial = state.settings?.units === 'imperial' ? true : false + + /* + * Metadata can be ignored + */ + if (step.name === 'settings' && step.path[1] === 'metadata') return null + + /* + * Defer for anything else to this method + */ + const data = getUndoStepData({ step, state, Design, imperial }) + + if (data === false) return
{JSON.stringify(step, null, 2)}
//null + if (data === null) return

Unsupported

+ + if (compact) + return ( + update.restore(index, state._)}> +
+ + {data.msg ? data.msg : `pe:${data.optCode}`} +
+
+ ) + + return ( + <> +

+ +

+ update.restore(index, state._)}> +
+ + {data.fieldIcon || null} + {`pe:${data.optCode}`} + + + {data.icon || null} {`pe:${data.titleCode}`} + +
+
+ {data.msg ? ( + data.msg + ) : ( + <> + + {Array.isArray(data.newVal) ? data.newVal.join(', ') : data.newVal} + + + + {Array.isArray(data.oldVal) ? data.oldVal.join(', ') : data.oldVal} + + + )} +
+
+ + ) +} diff --git a/packages/react/components/Editor/lib/design-options.mjs b/packages/react/components/Editor/lib/design-options.mjs index 59c6eeec066..0726ffb0392 100644 --- a/packages/react/components/Editor/lib/design-options.mjs +++ b/packages/react/components/Editor/lib/design-options.mjs @@ -1,16 +1,5 @@ -export function designOptionType(option) { - if (typeof option?.pct !== 'undefined') return 'pct' - if (typeof option?.bool !== 'undefined') return 'bool' - if (typeof option?.count !== 'undefined') return 'count' - if (typeof option?.deg !== 'undefined') return 'deg' - if (typeof option?.list !== 'undefined') return 'list' - if (typeof option?.mm !== 'undefined') return 'mm' - - return 'constant' -} import { mergeOptions } from '@freesewing/core' -import set from 'lodash.set' -import orderBy from 'lodash.orderby' +import { designOptionType, set, orderBy } from '@freesewing/utils' export function menuDesignOptionsStructure(options, settings, asFullList = false) { if (!options) return options @@ -59,6 +48,7 @@ export function menuDesignOptionsStructure(options, settings, asFullList = false return menu } + /* * Helper method to grab an option from an Design options structure * @@ -66,7 +56,6 @@ export function menuDesignOptionsStructure(options, settings, asFullList = false */ export function getOptionStructure(option, Design, state) { const structure = menuDesignOptionsStructure(Design.patternConfig.options, state.settings) - console.log({ structure }) return findOption(structure, option) } diff --git a/packages/react/components/Editor/lib/editor.mjs b/packages/react/components/Editor/lib/editor.mjs index 611458fbf50..3ff8b15f217 100644 --- a/packages/react/components/Editor/lib/editor.mjs +++ b/packages/react/components/Editor/lib/editor.mjs @@ -1,5 +1,8 @@ // Dependencies +import React from 'react' import { defaultConfig } from '../config/index.mjs' +import { round } from '@freesewing/utils' +import { formatDesignOptionValue } from './index.mjs' // Components import { ErrorIcon, diff --git a/packages/react/components/Editor/lib/formatting.mjs b/packages/react/components/Editor/lib/formatting.mjs index d46446bad07..fa1d16ad98c 100644 --- a/packages/react/components/Editor/lib/formatting.mjs +++ b/packages/react/components/Editor/lib/formatting.mjs @@ -1,28 +1,22 @@ +import { designOptionType } from '@freesewing/utils' + /* * Method that capitalizes a string (make the first character uppercase) * - * @param {object} Swizzled - Swizzled code, not used here * @param {string} string - The input string to capitalize * @return {string} String - The capitalized input string */ -export function capitalize(Swizzled, string) { +export function capitalize(string) { return typeof string === 'string' ? string.charAt(0).toUpperCase() + string.slice(1) : '' } -export function formatDesignOptionValue(Swizzled, option, value, imperial) { - const oType = Swizzled.methods.designOptionType(option) - if (oType === 'pct') return Swizzled.methods.formatPercentage(value ? value : option.pct / 100) +export function formatDesignOptionValue(option, value, imperial) { + const oType = designOptionType(option) + if (oType === 'pct') return formatPercentage(value ? value : option.pct / 100) if (oType === 'deg') return `${value ? value : option.deg}°` if (oType === 'bool') - return typeof value === 'undefined' ? ( - option.bool - ) : value ? ( - - ) : ( - - ) - if (oType === 'mm') - return Swizzled.methods.formatMm(typeof value === 'undefined' ? option.mm : value, imperial) + return typeof value === 'undefined' ? option.bool : value ? : + if (oType === 'mm') return formatMm(typeof value === 'undefined' ? option.mm : value, imperial) if (oType === 'list') return typeof value === 'undefined' ? option.dflt : value return value @@ -36,7 +30,7 @@ export function formatDesignOptionValue(Swizzled, option, value, imperial) { * fraction: the value to process * format: the type of formatting to apply. html, notags, or anything else which will only return numbers */ -export function formatFraction128(Swizzled, fraction, format = 'html') { +export function formatFraction128(fraction, format = 'html') { let negative = '' let inches = '' let rest = '' @@ -50,19 +44,12 @@ export function formatFraction128(Swizzled, fraction, format = 'html') { rest = fraction - inches } let fraction128 = Math.round(rest * 128) - if (fraction128 == 0) - return Swizzled.methods.formatImperial(negative, inches || fraction128, false, false, format) + if (fraction128 == 0) return formatImperial(negative, inches || fraction128, false, false, format) for (let i = 1; i < 7; i++) { const numoFactor = Math.pow(2, 7 - i) if (fraction128 % numoFactor === 0) - return Swizzled.methods.formatImperial( - negative, - inches, - fraction128 / numoFactor, - Math.pow(2, i), - format - ) + return formatImperial(negative, inches, fraction128 / numoFactor, Math.pow(2, i), format) } return ( @@ -73,7 +60,7 @@ export function formatFraction128(Swizzled, fraction, format = 'html') { } // Formatting for imperial values -export function formatImperial(Swizzled, neg, inch, numo = false, deno = false, format = 'html') { +export function formatImperial(neg, inch, numo = false, deno = false, format = 'html') { if (format === 'html') { if (numo) return `${neg}${inch} ${numo}/${deno}"` else return `${neg}${inch}"` @@ -88,28 +75,27 @@ export function formatImperial(Swizzled, neg, inch, numo = false, deno = false, // Format a value in mm based on the user's units // Format can be html, notags, or anything else which will only return numbers -export function formatMm(Swizzled, val, units, format = 'html') { - val = Swizzled.methods.roundMm(val) +export function formatMm(val, units, format = 'html') { + val = roundMm(val) if (units === 'imperial' || units === true) { - if (val == 0) return Swizzled.methods.formatImperial('', 0, false, false, format) + if (val == 0) return formatImperial('', 0, false, false, format) let fraction = val / 25.4 - return Swizzled.methods.formatFraction128(fraction, format) + return formatFraction128(fraction, format) } else { - if (format === 'html' || format === 'notags') return Swizzled.methods.roundMm(val / 10) + 'cm' - else return Swizzled.methods.roundMm(val / 10) + if (format === 'html' || format === 'notags') return roundMm(val / 10) + 'cm' + else return roundMm(val / 10) } } // Format a percentage (as in, between 0 and 1) -export function formatPercentage(Swizzled, val) { +export function formatPercentage(val) { return Math.round(1000 * val) / 10 + '%' } /** * A generic rounding method * - * @param {object} Swizzled - Swizzled code, not used here * @param {number} val - The input number to round * @param {number} decimals - The number of decimal points to use when rounding * @return {number} result - The rounded number @@ -119,7 +105,7 @@ export function round(methods, val, decimals = 1) { } // Rounds a value in mm -export function roundMm(Swizzled, val, units) { +export function roundMm(val, units) { if (units === 'imperial') return Math.round(val * 1000000) / 1000000 else return Math.round(val * 10) / 10 } @@ -127,11 +113,10 @@ export function roundMm(Swizzled, val, units) { /** * Converts a value that contain a fraction to a decimal * - * @param {object} Swizzled - Swizzled code, not used here * @param {number} value - The input value * @return {number} result - The resulting decimal value */ -export function fractionToDecimal(Swizzled, value) { +export function fractionToDecimal(value) { // if it's just a number, return it if (!isNaN(value)) return value @@ -170,12 +155,11 @@ export function fractionToDecimal(Swizzled, value) { /** * Helper method to turn a measurement in millimeter regardless of units * - * @param {object} Swizzled - Swizzled code, including methods * @param {number} value - The input value * @param {string} units - One of 'metric' or 'imperial' * @return {number} result - Value in millimeter */ -export function measurementAsMm(Swizzled, value, units = 'metric') { +export function measurementAsMm(value, units = 'metric') { if (typeof value === 'number') return value * (units === 'imperial' ? 25.4 : 10) if (String(value).endsWith('.')) return false @@ -185,7 +169,7 @@ export function measurementAsMm(Swizzled, value, units = 'metric') { if (isNaN(value)) return false return value * 10 } else { - const decimal = Swizzled.methods.fractionToDecimal(value) + const decimal = fractionToDecimal(value) if (isNaN(decimal)) return false return decimal * 24.5 } @@ -194,13 +178,12 @@ export function measurementAsMm(Swizzled, value, units = 'metric') { /** * Converts a millimeter value to a Number value in the given units * - * @param {object} Swizzled - Swizzled code, not used here * @param {number} mmValue - The input value in millimeter * @param {string} units - One of 'metric' or 'imperial' * @result {number} result - The result in millimeter */ -export function measurementAsUnits(Swizzled, mmValue, units = 'metric') { - return Swizzled.methods.round(mmValue / (units === 'imperial' ? 25.4 : 10), 3) +export function measurementAsUnits(mmValue, units = 'metric') { + return round(mmValue / (units === 'imperial' ? 25.4 : 10), 3) } export function shortDate(locale = 'en', timestamp = false, withTime = true) { const options = { @@ -220,12 +203,11 @@ export function shortDate(locale = 'en', timestamp = false, withTime = true) { /* * Parses value that should be a distance (cm or inch) * - * @param {object} Swizzled - Swizzled code, including methods * @param {number} val - The input value * @param {bool} imperial - True if the units are imperial, false for metric * @return {number} result - The distance in the relevant units */ -export function parseDistanceInput(Swizzled, val = false, imperial = false) { +export function parseDistanceInput(val = false, imperial = false) { // No input is not valid if (!val) return false @@ -239,7 +221,7 @@ export function parseDistanceInput(Swizzled, val = false, imperial = false) { if (!val.match(regex)) return false // if fractions are allowed, parse for fractions, otherwise use the number as a value - if (imperial) val = Swizzled.methods.fractionToDecimal(val) + if (imperial) val = fractionToDecimal(val) return isNaN(val) ? false : Number(val) } diff --git a/packages/react/components/Editor/lib/index.mjs b/packages/react/components/Editor/lib/index.mjs index ec0c2d01688..8a7d53172c0 100644 --- a/packages/react/components/Editor/lib/index.mjs +++ b/packages/react/components/Editor/lib/index.mjs @@ -9,12 +9,7 @@ import { menuCoreSettingsSammHandler, menuCoreSettingsStructure, } from './core-settings.mjs' -import { - designOptionType, - findOption, - getOptionStructure, - menuDesignOptionsStructure, -} from './design-options.mjs' +import { findOption, getOptionStructure, menuDesignOptionsStructure } from './design-options.mjs' import { addUndoStep, cloneObject, @@ -70,7 +65,6 @@ export { menuCoreSettingsSammHandler, menuCoreSettingsStructure, // design-options.mjs - designOptionType, findOption, getOptionStructure, menuDesignOptionsStructure, diff --git a/packages/react/components/Editor/swizzle/components/undos-view.mjs b/packages/react/components/Editor/swizzle/components/undos-view.mjs deleted file mode 100644 index 2093d8aad07..00000000000 --- a/packages/react/components/Editor/swizzle/components/undos-view.mjs +++ /dev/null @@ -1,144 +0,0 @@ -import orderBy from 'lodash.orderby' - -/** - * The undos view shows the undo history, and allows restoring any undo state - * - * @param (object) props - All the props - * @param {object} props.swizzled - An object with swizzled components, hooks, methods, config, and defaults - * @param {object} designs - Object holding all designs - * @param {object} update - ViewWrapper state update object - */ -export const UndosView = ({ Design, Swizzled, update, state }) => { - const steps = orderBy(state._.undos, 'time', 'desc') - - return ( - <> - -
-

{Swizzled.methods.t('pe:view.undos.t')}

-

{Swizzled.methods.t('pe:view.undos.d')}

- - Tip: Click on any change to undo all changes up to, and including, that change. - - {steps.length < 1 ? ( - -

Your undo history is currently empty

-

When you make changes to your pattern, they will show up here.

-

For example, you can click the button below to change the pattern rotation:

- -

As soon as you do, the change will show up here, and you can undo it.

-
- ) : ( -
- {steps.map((step, index) => ( - - ))} -
- )} -
- - ) -} - -export const UndoStepTimeAgo = ({ Swizzled, step }) => { - if (!step.time) return null - const secondsAgo = Math.floor((Date.now() - step.time) / 100) / 10 - const minutesAgo = Math.floor(secondsAgo / 60) - const hoursAgo = Math.floor(minutesAgo / 60) - - return hoursAgo ? ( - - {hoursAgo} {Swizzled.methods.t('pe:hoursAgo')} - - ) : minutesAgo ? ( - - {minutesAgo} {Swizzled.methods.t('pe:minutesAgo')} - - ) : ( - - {secondsAgo} {Swizzled.methods.t('pe:secondsAgo')} - - ) -} - -export const UndoStep = ({ Swizzled, update, state, step, Design, compact = false, index = 0 }) => { - const { t } = Swizzled.methods - - /* - * Ensure path is always an array - */ - if (!Array.isArray(step.path)) step.path = step.path.split('.') - - /* - * Figure this out once - */ - const imperial = state.settings?.units === 'imperial' ? true : false - - /* - * Metadata can be ignored - */ - if (step.name === 'settings' && step.path[1] === 'metadata') return null - - /* - * Defer for anything else to this method - */ - const data = Swizzled.methods.getUndoStepData({ step, state, Design, imperial }) - - if (data === false) return
{JSON.stringify(step, null, 2)}
//null - if (data === null) return

Unsupported

- - if (compact) - return ( - update.restore(index, state._)}> -
- - {data.msg ? data.msg : t(`pe:${data.optCode}`)} -
-
- ) - - return ( - <> -

- -

- update.restore(index, state._)}> -
- - {data.fieldIcon || null} - {t(`pe:${data.optCode}`)} - - - {data.icon || null} {t(`pe:${data.titleCode}`)} - -
-
- {data.msg ? ( - data.msg - ) : ( - <> - - {Array.isArray(data.newVal) ? data.newVal.join(', ') : data.newVal} - - - - {Array.isArray(data.oldVal) ? data.oldVal.join(', ') : data.oldVal} - - - )} -
-
- - ) -} diff --git a/packages/react/components/Icon/index.mjs b/packages/react/components/Icon/index.mjs index cd0e3c14cc7..f9d4082ad42 100644 --- a/packages/react/components/Icon/index.mjs +++ b/packages/react/components/Icon/index.mjs @@ -493,10 +493,10 @@ export const ReloadIcon = (props) => ( ) -// Looks like a single rewind arrow +// Looks like a backspace key export const ResetIcon = (props) => ( - + ) diff --git a/packages/react/hooks/useDesignTranslation/index.mjs b/packages/react/hooks/useDesignTranslation/index.mjs new file mode 100644 index 00000000000..1dac8252af3 --- /dev/null +++ b/packages/react/hooks/useDesignTranslation/index.mjs @@ -0,0 +1,122 @@ +import { i18n as aaron } from '@freesewing/aaron' +import { i18n as albert } from '@freesewing/albert' +import { i18n as bee } from '@freesewing/bee' +import { i18n as bella } from '@freesewing/bella' +import { i18n as benjamin } from '@freesewing/benjamin' +import { i18n as bent } from '@freesewing/bent' +import { i18n as bibi } from '@freesewing/bibi' +import { i18n as bob } from '@freesewing/bob' +import { i18n as breanna } from '@freesewing/breanna' +import { i18n as brian } from '@freesewing/brian' +import { i18n as bruce } from '@freesewing/bruce' +import { i18n as carlita } from '@freesewing/carlita' +import { i18n as carlton } from '@freesewing/carlton' +import { i18n as cathrin } from '@freesewing/cathrin' +import { i18n as charlie } from '@freesewing/charlie' +import { i18n as cornelius } from '@freesewing/cornelius' +import { i18n as diana } from '@freesewing/diana' +import { i18n as florence } from '@freesewing/florence' +import { i18n as florent } from '@freesewing/florent' +import { i18n as gozer } from '@freesewing/gozer' +import { i18n as hi } from '@freesewing/hi' +import { i18n as holmes } from '@freesewing/holmes' +import { i18n as hortensia } from '@freesewing/hortensia' +import { i18n as huey } from '@freesewing/huey' +import { i18n as hugo } from '@freesewing/hugo' +import { i18n as jaeger } from '@freesewing/jaeger' +import { i18n as jane } from '@freesewing/jane' +import { i18n as lucy } from '@freesewing/lucy' +import { i18n as lumina } from '@freesewing/lumina' +import { i18n as lumira } from '@freesewing/lumira' +import { i18n as lunetius } from '@freesewing/lunetius' +import { i18n as noble } from '@freesewing/noble' +import { i18n as octoplushy } from '@freesewing/octoplushy' +import { i18n as onyx } from '@freesewing/onyx' +import { i18n as opal } from '@freesewing/opal' +import { i18n as otis } from '@freesewing/otis' +import { i18n as paco } from '@freesewing/paco' +import { i18n as penelope } from '@freesewing/penelope' +import { i18n as sandy } from '@freesewing/sandy' +import { i18n as shelly } from '@freesewing/shelly' +import { i18n as shin } from '@freesewing/shin' +import { i18n as simon } from '@freesewing/simon' +import { i18n as simone } from '@freesewing/simone' +import { i18n as skully } from '@freesewing/skully' +import { i18n as sven } from '@freesewing/sven' +import { i18n as tamiko } from '@freesewing/tamiko' +import { i18n as teagan } from '@freesewing/teagan' +import { i18n as tiberius } from '@freesewing/tiberius' +import { i18n as titan } from '@freesewing/titan' +import { i18n as trayvon } from '@freesewing/trayvon' +import { i18n as tristan } from '@freesewing/tristan' +import { i18n as uma } from '@freesewing/uma' +import { i18n as umbra } from '@freesewing/umbra' +import { i18n as wahid } from '@freesewing/wahid' +import { i18n as walburga } from '@freesewing/walburga' +import { i18n as waralee } from '@freesewing/waralee' +import { i18n as yuri } from '@freesewing/yuri' +import { i18n as lily } from '@freesewing/lily' + +export const designTranslations = { + aaron, + albert, + bee, + bella, + benjamin, + bent, + bibi, + bob, + breanna, + brian, + bruce, + carlita, + carlton, + cathrin, + charlie, + cornelius, + diana, + florence, + florent, + gozer, + hi, + holmes, + hortensia, + huey, + hugo, + jaeger, + jane, + lucy, + lumina, + lumira, + lunetius, + noble, + octoplushy, + onyx, + opal, + otis, + paco, + penelope, + sandy, + shelly, + shin, + simon, + simone, + skully, + sven, + tamiko, + teagan, + tiberius, + titan, + trayvon, + tristan, + uma, + umbra, + wahid, + walburga, + waralee, + yuri, + lily, +} + +export const useDesignTranslation = (design) => + designTranslations[design] ? designTranslations[design] : false diff --git a/packages/react/package.json b/packages/react/package.json index fe2bb32d462..fd443c9a7cd 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -66,6 +66,8 @@ "./hooks/useAccount": "./hooks/useAccount/index.mjs", "./hooks/useBackend": "./hooks/useBackend/index.mjs", "./hooks/useControl": "./hooks/useControl/index.mjs", + "./hooks/useDesign": "./hooks/useDesign/index.mjs", + "./hooks/useDesignTranslation": "./hooks/useDesignTranslation/index.mjs", "./hooks/useSelection": "./hooks/useSelection/index.mjs", "./lib/RestClient": "./lib/RestClient/index.mjs", "./lib/logoPath": "./components/Logo/path.mjs" diff --git a/packages/utils/src/index.mjs b/packages/utils/src/index.mjs index ae6b2da76d6..40110257c52 100644 --- a/packages/utils/src/index.mjs +++ b/packages/utils/src/index.mjs @@ -69,6 +69,23 @@ export function cloudflareImageUrl({ id = 'default-avatar', variant = 'public' } return `${cloudflareConfig.url}${id}/${variant}` } +/** + * Determines the design optino type based on the option's config + * + * @param {object} option - The option config + * @return {string} type - The option type + */ +export function designOptionType(option) { + if (typeof option?.pct !== 'undefined') return 'pct' + if (typeof option?.bool !== 'undefined') return 'bool' + if (typeof option?.count !== 'undefined') return 'count' + if (typeof option?.deg !== 'undefined') return 'deg' + if (typeof option?.list !== 'undefined') return 'list' + if (typeof option?.mm !== 'undefined') return 'mm' + + return 'constant' +} + /* * Parses value that should be a distance (cm or inch) into a value in mm * @@ -179,6 +196,16 @@ export function formatMm(val, units, format = 'html') { } } +/** + * Format a percentage (as in, between 0 and 1) + * + * @param {number} val - The value + * @return {string} pct - The value formatted as percentage + */ +export function formatPercentage(val) { + return Math.round(1000 * val) / 10 + '%' +} + /** convert a value that may contain a fraction to a decimal */ export function fractionToDecimal(value) { // if it's just a number, return it diff --git a/sites/org/tailwind.config.mjs b/sites/org/tailwind.config.mjs index 3c2e94798c0..3c5021d872e 100644 --- a/sites/org/tailwind.config.mjs +++ b/sites/org/tailwind.config.mjs @@ -13,7 +13,7 @@ export default { './tailwind-force.html', ], plugins: [daisyui], - corePlugins: { preflight: false }, + //corePlugins: { preflight: false }, darkMode: ['class', "[data-theme='dark']"], prefix: 'tw-', daisyui: {