diff --git a/config/dependencies.yaml b/config/dependencies.yaml index 34ad7af1760..b90fa4ff143 100644 --- a/config/dependencies.yaml +++ b/config/dependencies.yaml @@ -126,9 +126,14 @@ plugintest: '@freesewing/plugin-theme': *freesewing react-components: _: + axios: *axios html-react-parser: "^5.0.7" + nuqs: "^1.17.6" + react-markdown: "^9.0.1" + use-local-storage-state: "19.1.0" + use-session-storage-state: "^19.0.0" peer: - react: '>=14' + react: "^18.2.0" rehype-jargon: _: 'unist-util-visit': &unist-util-visit '5.0.0' diff --git a/markdown/org/showcase/first-attempt-sewing-undies/en.md b/markdown/org/showcase/first-attempt-sewing-undies/en.md index 4a0a793e799..5249fd7fec4 100644 --- a/markdown/org/showcase/first-attempt-sewing-undies/en.md +++ b/markdown/org/showcase/first-attempt-sewing-undies/en.md @@ -7,5 +7,5 @@ author: auxsuivants designs: ["umbra"] --- -I've been wanting to attempt a pair of these forever, but i've had so many other projects going. But I finally got to them tonight and they're definitely comfy and cute as all hell. Gonna be making a lot more of these <3 +I've been wanting to attempt a pair of these forever, but i've had so many other projects going. But I finally got to them tonight and they're definitely comfy and cute as all hell. Gonna be making a lot more of these <3 diff --git a/package.json b/package.json index febddb6bb75..bc53ae97453 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "cleanall": "lerna run clean", "test": "lerna run test", "e2e": "lerna run e2e", - "prettier": "npx prettier --write 'config/*' 'config/**/*' 'packages/**/src/*.mjs' 'packages/i18n/src/locales/**/*.*' 'packages/**/tests/*.mjs'", + "prettier": "npx prettier --write 'config/*' 'config/**/*' 'packages/**/src/*.mjs' 'packages/i18n/src/locales/**/*.*' 'packages/**/tests/*.mjs' 'packages/react-components/src/**/*.mjs'", "reconfigure": "all-contributors generate && node --experimental-json-modules --no-warnings scripts/reconfigure.mjs", "sitebuildconfigure": "SITEBUILD=1 node --experimental-json-modules --no-warnings scripts/reconfigure.mjs", "prerelease": "lerna version --no-git-tag-version --no-push && yarn reconfigure && yarn buildall", diff --git a/packages/react-components/package.json b/packages/react-components/package.json index c30277751e8..ad193fe633b 100644 --- a/packages/react-components/package.json +++ b/packages/react-components/package.json @@ -23,7 +23,12 @@ ".": { "internal": "./src/index.mjs", "default": "./dist/index.mjs" - } + }, + "./pattern": "./src/pattern/index.mjs", + "./xray": "./src/pattern-xray/index.mjs", + "./editor": "./src/editor/index.mjs", + "./icons": "./src/editor/swizzle/components/icons.mjs" + }, "scripts": { "build": "node build.mjs", @@ -40,10 +45,15 @@ "wbuild:all": "yarn wbuild" }, "peerDependencies": { - "react": ">=14" + "react": "^18.2.0" }, "dependencies": { - "html-react-parser": "^5.0.7" + "axios": "1.6.8", + "html-react-parser": "^5.0.7", + "nuqs": "^1.17.6", + "react-markdown": "^9.0.1", + "use-local-storage-state": "19.1.0", + "use-session-storage-state": "^19.0.0" }, "devDependencies": {}, "files": [ diff --git a/packages/react-components/src/editor/components/view-wrapper.mjs b/packages/react-components/src/editor/components/view-wrapper.mjs new file mode 100644 index 00000000000..ff6f1949d06 --- /dev/null +++ b/packages/react-components/src/editor/components/view-wrapper.mjs @@ -0,0 +1,137 @@ +// Hooks +import { useState } from 'react' + +/** + * The editor view wrapper component + * + * Figures out what view to load initially, + * and handles state for the pattern, inclding the view + * + * @param (object) props - All the props + * @param {object} props.designs - An object holding all designs + * @param {object} props.preload - Object holding state to preload + * @param {object} props.locale - The locale (language) code + * @param {object} props.design - A design name to force the editor to use this design + * @param {object} props.Swizzled - An object holding swizzled code + */ +export const ViewWrapper = ({ designs = {}, preload = {}, design = false, Swizzled }) => { + /* + * Ephemeral state will not be stored in the state backend + * It is used for things like loading state and so on + */ + const [ephemeralState, setEphemeralState] = useState({}) + // Editor state + const allState = Swizzled.hooks.useEditorState( + Swizzled.methods.initialEditorState(preload), + setEphemeralState + ) + const state = allState[0] + const update = allState[2] + + // Don't bother before state is initialized + if (!state) return + + // Figure out what view to load + const [View, extraProps] = viewfinder({ design, designs, preload, state, Swizzled }) + + /* + * Pass this down to allow disabling features that require measurements + */ + const { missingMeasurements = [] } = extraProps + + /* + * Almost all editor state has a default settings, and when that is selected + * we just unset that value in the state. This way, state holds only what is + * customized, and it makes it a lot easier to see how a pattern was edited. + * The big exception is the 'ui.ux' setting. If it is unset, a bunch of + * components will not function properly. We could guard against this by passing + * the default to all of these components, but instead, we just check that state + * is undefined, and if so pass down the default ux value here. + * This way, should more of these exceptions get added over time, we can use + * the same centralized solution. + */ + const passDownState = + state.ui.ux === undefined + ? { + ...state, + ui: { ...state.ui, ux: Swizzled.config.defaultUx }, + _: { ...ephemeralState, missingMeasurements }, + } + : { ...state, _: { ...ephemeralState, missingMeasurements } } + + return ( +
+ {Swizzled.config.withAside ? ( + + ) : null} +
+ + +
+
+ ) +} + +/** + * Helper method to figure out what view to load + * based on the props passed in, and destructure + * the props we need for it. + * + * @param (object) props - All the props + * @param {object} props.design - The (name of the) current design + * @param {object} props.designs - An object holding all designs + * @param (object) props.state - React state passed down from the wrapper view + */ +const viewfinder = ({ design, designs, state, Swizzled }) => { + /* + * Grab Design from props or state and make them extra props + */ + if (!design && state?.design) design = state.design + const Design = designs[design] || false + const extraProps = { design, Design } + + /* + * If no design is set, return the designs view + */ + if (!designs[design]) return [getViewComponent('designs', Swizzled), extraProps] + + /* + * If we have a design, do we have the measurements? + */ + const [measurementsOk, missingMeasurements] = Swizzled.methods.hasRequiredMeasurements( + designs[design], + state.settings?.measurements + ) + if (missingMeasurements) extraProps.missingMeasurements = missingMeasurements + + /* + * Allow all views that do not require measurements before + * we force the user to the measurements view + */ + if (state.view && Swizzled.config.measurementsFreeViews.includes(state.view)) { + const view = getViewComponent(state.view, Swizzled) + if (view) return [view, extraProps] + } + + if (!measurementsOk) return [getViewComponent('measurements', Swizzled), extraProps] + + /* + * If a view is set, return that + */ + const view = getViewComponent(state.view, Swizzled) + if (view) return [view, extraProps] + + /* + * If no obvious view was found, return the view picker + */ + return [getViewComponent('picker', Swizzled), extraProps] +} + +const getViewComponent = (view = false, Swizzled) => + view ? Swizzled.components[Swizzled.config.viewComponents[view]] : false diff --git a/packages/react-components/src/editor/en.yaml b/packages/react-components/src/editor/en.yaml new file mode 100644 index 00000000000..fb9d3eb5e8e --- /dev/null +++ b/packages/react-components/src/editor/en.yaml @@ -0,0 +1,290 @@ +# Popout component +comment: Comment +note: Note +tip: Tip +warning: Warning +fixme: FIXME +link: Link +related: Related + +# Designs view +chooseADesign: Choose a Design +# Mesurements view +measurements: Measurements +measurementsAreOk: We have all required measurements to draft this pattern. +editMeasurements: Edit Measurements +editMeasurementsDesc: You can manually set or override measurements below. +requiredMeasurements: Required Measurements +optionalMeasurements: Optional Measurements +missingMeasurements: Missing Measurements +missingMeasurementsInfo: To generate this pattern, we need the following additional measurements +missingMeasurementsNotify: To generate this pattern, we need some additional measurements + +# Measurements sets +noOwnSets: You do not have any of your own measurements sets (yet) +pleaseMtm: Because our patterns are bespoke, we strongly suggest you take accurate measurements. +noOwnSetsMsg: You can store your measurements as a measurements set, after which you can generate as many patterns as you want for them. +chooseFromOwnSets: Choose one of your own measurements sets +chooseFromOwnSetsDesc: Pick any of your own measurements sets that have all required measurements to generate this pattern. +newSet: Create a new measurements set +someSetsLacking: Some of your measurements sets lack the measurements required to generate this pattern +chooseFromBookmarkedSets: Choose one of the measurements sets you've bookmarked +chooseFromBookmarkedSetsDesc: If you've bookmarked any measurements sets, you can select from those too. +chooseFromCuratedSets: Choose one of FreeSewing's curated measurements sets +chooseFromCuratedSetsDesc: If you're just looking to try out our platform, you can select from our list of curated measurements sets. + +# View wrapper +requiredPropsMissing.t: Required props are missing +requiredPropsMissing.d: The FreeSewing pattern editor needs to be initialized properly. It currently lacks some props to be able to bootstrap. + +# View picker +chooseAnActivity: Choose an activity +chooseAnotherActivity: Choose a different activity +view.draft.t: Draft Pattern +view.draft.d: Choose this if you are not certain what to pick +view.measurements.t: Pattern Measurements +view.measurements.d: Update or load measurements to generate a pattern for +view.test.t: Test Design +view.test.d: See how different options or changes in measurements influence the design +view.timing.t: Time Design +view.timing.d: Shows detailed timing of the pattern being drafted, allowing you to find bottlenecks in performance +view.printLayout.t: Print Layout +view.printLayout.d: Organize your pattern parts to minimize paper use +view.save.t: Save pattern as... +view.save.d: Save the changes to this pattern in your account, or save it as a new pattern +view.export.t: Export Pattern +view.export.d: Export this pattern into a variety of formats +view.editSettings.t: Edit settings by hand +view.editSettings.d: Throw caution to the wind, and hand-edit the pattern's settings +view.logs.t: Pattern Logs +view.logs.d: Show the logs generated by the pattern, useful to troubleshoot problems +view.inspect.t: Pattern inspector +view.inspect.d: Load the pattern in the inspector, giving you in-depth info about a pattern's components +view.docs.t: Documentation +view.docs.d: More information and links to documentation +view.designs.t: Choose a different Design +view.designs.d: "Current design: {- design }" +view.picker.t: Choose a different view +view.undos.t: Undo History +view.undos.d: Time-travel through your recent pattern changes +showAdvancedOptions: Show advanced options +hideAdvancedOptions: Hide advanced options +views.t: Views +views.d: Choose between the main views of the pattern editor +measurementsFreeViewsOnly.t: We are only showing activities that do not require measurements +measurementsFreeViewsOnly.d: Once we have all measurements required to generate a pattern, you will have more choices here. + + +# menus +youAreUsingTheDefaultValue: You are using the default value +youAreUsingACustomValue: You are using a custom value +designOptions.t: Design Options +designOptions.d: These options are specific to this design. You can use them to customize your pattern in a variety of ways. +fit.t: Fit +style.t: Style +advanced.t: Advanced +coreSettings.t: Core Settings +coreSettings.d: These settings are not specific to the design, but instead allow you to customize various parameters of the FreeSewing core library, which generates the design for you. +paperless.t: Paperless +paperless.d: 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. +samm.t: Seam Allowance Size +samm.d: Controls the amount of seam allowance used in your pattern +sabool.t: Include Seam Allowance +sabool.d: Controls whether or not to include seam allowance in your pattern +complete.t: Details +complete.d: Controls how detailed the pattern is; Either a complete pattern with all details, or a basic outline of the pattern parts +expand.t: Expand +expand.d: Controls efforts to save paper. Disable this to expand all pattern parts at the cost of using more space. +only.t: Included Parts +only.d: Use this to control exactly which pattern parts will be included in your pattern +units.t: Units +units.d: This setting determines how unit are displayed on your pattern +margin.t: Margin +margin.d: Controls the margin around pattern parts +scale.t: Scale +scale.d: Controls the overall line width, font size, and other elements that do not scale with the pattern's measurements +yes: Yes +no: No +completeYes.t: Generate a complete pattern +completeYes.d: This will generate a complete pattern with all notations, lines, markings. Use this if you are not certain what to choose. +completeNo.t: Generate a pattern outline +completeNo.d: Only generate the outline of the pattern parts. Use this if you are looking to use a laser cutter or have other specific needs. +expandYes.t: Expand all pattern parts +expandYes.d: This will generate a pattern where all pattern parts are drawn to their full size, even if they are simple rectangles. +expandNo.t: Keep patterns parts compact where possible +expandNo.d: This will draw a more dense representation of the pattern which includes all info without using up too much space & paper. +saNo.t: Do not include seam allowance +saNo.d: 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. +saYes.t: Include seam allowance +saYes.d: This generates a pattern that will include seam allowance. The size of the seam allowance is set individually. +paperlessNo.t: Generate a regular pattern +paperlessNo.d: This will generate a regular pattern, which you can then print out. +paperlessYes.t: Generate a paperless pattern +paperlessYes.d: This generates a pattern with dimensions and a grid, which allows you to transfer it on fabric or another medium without the need to print out the pattern. +metric: Metric +uiPreferences.t: UI Preferences +uiPreferences.d: These preferences control the UI (User Interface) of the pattern editor +renderer.t: Render Engine +renderer.d: Controls how the pattern is rendered (drawn) on the screen +renderWithReact.t: Render with FreeSewing's React components +renderWithReact.d: Render as SVG through our React components. Allows interactivity and is optimized for screen. Use this if you are not sure what to pick. +renderWithCore.t: Render with FreeSewing's Core library +renderWithCore.d: Render directly to SVG from Core. Allows no interactivity and is optimized for print. Use this if you want to know what it will look like when exported. +kiosk.t: Kiosk Mode +kiosk.d: Controls how the pattern editor is embedded in the web page. +aside.t: Aside Menu +aside.d: Controls whether or not to show menus on the side of larger screens +withAside.t: Also show menus on the side +withAside.d: Shows menus both on the side of the screen, as well as the drop-downs in the header (this only applies to larger screens) +noAside.t: Only show menus in the header +noAside.d: Only shows the drop-down variant of the menus, making more room for your pattern +rotate.t: Rotate pattern +rotate.d: Allows you to rotate your pattern 90 degrees to better fit your screen +rotateNo.t: Do not rotate pattern +rotateNo.d: Show the pattern as it is +rotateYes.t: Rotate pattern 90 degrees +rotateYes.d: Rotate the pattern 90 degrees counter clockwise +websiteMode.t: Use inline mode +websiteMode.d: Embeds the pattern editor in the natural flow of the web page. +kioskMode.t: Use kiosk mode +kioskMode.d: Breaks out the pattern editor to fill the entire page. +ux.t: User Experience +ux.d: Which user experience do you prefer? From keep it simple, to give me all the powers. +inspect.t: Inspect +inspect.d: Enabling this will allow you to drill down into the pattern, and pull up information about its various parts, paths, and points. +inspectNo.t: Disable the inspector +inspectNo.d: This is the default, the pattern inspector is disabled and the pattern is displayed as usual. +inspectYes.t: Enable the inspector +inspectYes.d: With the pattern inspector enabled and the React rendering engine selected, we will add interactivity to the pattern to allow you to inspect the various elements that make up the pattern. +draft: Draft +test: Test +print: Print layout +cut: Cut Layout +save: Save +export: Export +edit: Edit +draft.t: Draft your pattern +draft.d: Launches FreeSewing flagship pattern editor, where you can tweak your pattern to your heart's desire +test.t: Test your pattern +test.d: See how your pattern adapts to changes in options, or measurements +print.t: Print Layout +print.d: Allows you to arrange your pattern pieces so you can printing your pattern on as little pages as possible +cut.t: Cutting layout +cut.d: Allows you to arrange your pattern pieces so you can determine exactly how much fabric you need to make it. +save.t: Save your pattern +save.d: Save the current pattern to your FreeSewing account +export.t: Export your pattern +export.d: Allows you to export this pattern to a variety of formats +logs.t: Pattern logs +enterCustomValue: Enter a custom value + +# ux +ux1.t: Keep it as simple as possible +ux1.d: Hides all but the most essential features. +ux2.t: Keep it simple, but not too simple +ux2.d: Hides the majority of features. +ux3.t: Balance simplicity with power +ux3.d: Reveals the majority of features, but not all. +ux4.t: Give me all powers, but keep me safe +ux4.d: Reveals all features, keeps handrails and safety checks. +ux5.t: Get out of my way +ux5.d: Reveals all features, removes all handrails and safety checks. + +# Tooltips +tt.changeEditorView: Change to a different view +tt.toggleSa: Turns Seam Allowance on or off (see Core Settings) +tt.togglePaperless: Turns Paperless on or off (see Core Settings) +tt.toggleComplete: Turns Details on or off (see Core Settings) +tt.toggleExpand: Turns Expand on or off (see Core Settings) +tt.toggleUnits: Switches Units between metric and imperial (see Core Settings) +tt.changeUx: Changes your UX setting (see UI Preferences) +tt.toggleAside: Turn the Aside Menu on or off (see UI Preferences) +tt.toggleKiosk: Turns Kiosk Mode on or off (see UI Preferences) +tt.toggleRotate: Turns Rotate Pattern on or off (see UI Preferences) +tt.toggleRenderer: Switches the Render Engine between React and SVG (see UI Preferences) +tt.exportPattern: Export pattern +tt.savePattern: Save pattern +tt.savePatternAs: Save pattern as... +tt.undo: Undo most recent change +tt.undoAll: Undo all changes since the last save point +tt.resetDesign: Reset all settings, but keep the design and measurements +tt.resetAll: Reset the editor completely + +# flags +apply: Apply +decrease: Decrease +disable: Disable +dismiss: Dismiss +expandIsOff.t: This design saves space (and trees) because expand is disabled +expandIsOff.d: "Because the **expand** core setting is currently disabled, some parts are not fully drawn or not shown at all. Typically, these are simple rectangles that only take up space, or things that can be cut on the fold. \n\nTo expand all pattern parts to their full size, enable the expand setting." +expandIsOn.t: This design can save space (and trees) +expandIsOn.d: "Because the **expand** core setting is currently enabled, all parts are fully drawn. You can display this design in a more compact way by disabling the **expand** setting. \n\nDoing so will mean that some parts are not fully drawn or not shown at all. Typically, these are simple rectangles that only take up space, or things that can be cut on the fold." +enable: Enable +flags: Flags +flagMenu.t: Flags +flagMenuOne.d: A specific issue about your current pattern needs your attention. +flagMenuMany.d: Some issues about your current pattern need your attention. +hide: Hide +increase: Increase +show: Show +saIncluded: (This includes seam allowance) +saExcluded: (This does not include seam allowance) +saUnused: (This part does not require any seam allowance) +partHiddenByExpand: This part is not shown because the **expand** core setting is currently disabled. Enable it to show this pattern part. + +# Auth +authRequired: Authentication required +membersOnly: This functionality requires a FreeSewing account. +signUp: Sign Up +signIn: Sign In +statusUnknown: Account status warning +statusUnknownMsg: Your account status prohibits us from processing your data. Please contact support. +consentLacking: Consent lacking +consentLackingMsg: We do not have your consent for processing your data. Without consent, we have no legal basis to process your data. +accountProhibited: Your account has been disabled +accountProhibitedMsg: Your account has been administratively disabled. +accountDisabled: Account disabled +accountDisabledMsg: You cannot re-enable a disabled account. You need to contact support to address this. +accountInactive: Your account is inactive +accountInactiveMsg: You must activate your account via the signup link we sent you. +signupAgain: If you cannot find the link, you can receive a new one by signing up again. +cannotUse: A disabled account cannot be used. +contactSupport: Contact support +reviewConsent: Review your consent +roleLacking: You lack the required role to access this content +roleLackingMsg: This content requires the { requiredRole } role. Your role is { role } which does not grant you access to this content. + +# save pattern +bookmarkPattern: Bookmark pattern +savePattern: Save pattern +saveAsNewPattern: Save as a New Pattern +savePatternAs: Save pattern as... +savePatternAsHellip: Save pattern as... +patternBookmarkCreated: Pattern bookmark created +see: See +addNotes: Add notes +addSettingsToNotes: Add settings to notes +exporting: Exporting +exportAsData: Export as data +exportForEditing: Export for editing +exportForPrinting: Export for printing +exportPattern-txt: Export a PDF suitable for your printer, or download this pattern in a variety of formats +exportPattern: Export pattern +settings: Settings +patternTitle: Pattern Title +patternNotes: Pattern Notes +toAccessPatternsGoTo: To access your patterns, go to +genericLoadingMessage: Hang tight, we're working on it... +patternSavedAs: Pattern saved as +cancel: Cancel + +# undo history +secondsAgo: seconds ago +minutesAgo: minutes ago +hoursAgo: hours ago +undos.unknown.t: Unknown Change +defaultRestored: Cleared (default restored) +includeAllParts: Include all parts +allFirstLetter: A +undo: Undo +xMeasurementsChanged: "{ count } Measurements changed" diff --git a/packages/react-components/src/editor/index.mjs b/packages/react-components/src/editor/index.mjs new file mode 100644 index 00000000000..13f00b88dd6 --- /dev/null +++ b/packages/react-components/src/editor/index.mjs @@ -0,0 +1,89 @@ +import { useState, useEffect } from 'react' +import { swizzleConfig } from './swizzle/config.mjs' +import { swizzleComponents } from './swizzle/components/index.mjs' +import { swizzleHooks } from './swizzle/hooks/index.mjs' +import { swizzleMethods } from './swizzle/methods/index.mjs' +import { ViewWrapper } from './components/view-wrapper.mjs' +// This is an exception as we need to show something before Swizzled components are ready +import { TemporaryLoader as UnswizzledTemporaryLoader } from './swizzle/components/loaders.mjs' + +/* + * Namespaces used by the pattern editor + */ +export const ns = ['pe', 'measurements'] + +/** + * PatternEditor is the high-level FreeSewing component + * that provides the entire pattern editing environment + * + * @param {object} props.design = The name of the design we are editing + * @param {object} props.designs = An object holding the designs code + * @param {object} props.components = An object holding components to swizzle + * @param {object} props.hooks = An object holding hooks to swizzle + * @param {object} props.methods = An object holding methods to swizzle + * @param {object} props.config = An object holding the editor config to swizzle + * @param {object} props.locale = The locale (language) code + * @param {object} props.preload = Any state to preload + * + */ +export const PatternEditor = (props) => { + const [swizzled, setSwizzled] = useState(false) + + useEffect(() => { + if (!swizzled) { + const merged = { + config: swizzleConfig(props.config), + } + merged.methods = swizzleMethods(props.methods, merged) + merged.components = swizzleComponents(props.components, merged) + merged.hooks = swizzleHooks(props.hooks, merged) + setSwizzled(merged) + } + }, [swizzled, props.components, props.config, props.hooks, props.methods]) + + if (!swizzled?.hooks) return + /* + * First of all, make sure we have all the required props + */ + const lackingProps = lackingPropsCheck(props) + if (lackingProps !== false) return + + /* + * Extract props we care about + */ + const { designs = {}, locale = 'en', preload } = props + + /* + * Now return the view wrapper and pass it the relevant props and the swizzled props + */ + return +} + +/** + * Helper function to verify that all props that are required to + * run the editor are present. + * + * Note that these errors are not translation, because they are + * not intended for end-users, but rather for developers. + * + * @param {object} props - The props passed to the PatternEditor component + * @return {bool} result - Either true or false depending on required props being present + */ +const lackingPropsCheck = (props) => { + if (typeof props.designs !== 'object') + return "Please pass a 'designs' prop with the designs supported by this editor" + if (Object.keys(props.designs).length < 1) return "The 'designs' prop does not hold any designs" + + return false +} + +/** + * A component to inform the user that the editor cannot be started + * because there are missing required props + */ +const LackingPropsError = ({ error }) => ( +
+

Unable to initialize pattern editor

+

{error}

+
+) diff --git a/packages/react-components/src/editor/nl.yaml b/packages/react-components/src/editor/nl.yaml new file mode 100644 index 00000000000..58d34e436a4 --- /dev/null +++ b/packages/react-components/src/editor/nl.yaml @@ -0,0 +1,23 @@ +# Popout component +comment: Opmerking +note: Notitie +tip: Tip +warning: Waarschuwing +fixme: FIXME +link: Link +related: Gerelateerd + +# Mesurements view +measurements: Maten +measurementsAreOk: We hebben alle benodigde maten om dit patroon te tekenen. +editMeasurements: Maten Aanpassen +editMeasurementsDesc: Hier kan je manueel de maten aanpassen. +requiredMeasurements: Vereiste Maten +optionalMeasurements: Optionele Maten + +# Designs view +pickADesign: Kies een ontwerp + +# View wrapper +requiredPropsMissing.t: Vereiste props ontbreken +requiredPropsMissing.d: De FreeSewing patroon editor moet correct geinitialiseerd worden. Momenteel ontbreken een aantal props die noodzakelijk zijn om de editor te starten. diff --git a/packages/react-components/src/editor/props.md b/packages/react-components/src/editor/props.md new file mode 100644 index 00000000000..7933b55d590 --- /dev/null +++ b/packages/react-components/src/editor/props.md @@ -0,0 +1,26 @@ +# List of props that can be passed to the pattern editor + +| Prop | Default | Description | +| ---- | ------- | ----------- | +| `design` | `undefined` | Name of the current design (key in the `objects` prop).
Note that this will set the initial state, but it can be changed by the user. | +| `designs` | `{}` |Object holding all designs that are available. | +| `locale` | `en` | Language code | +| `imperial`| `false` | Whether to use imperial units as the default, or not | +| `components` | `{}` | Object holding swizzled components | +| `hooks` | `{}` | Object holding swizzled hooks | +| `methods` | `{}` | Object holding swizzled methods | + + +## Defaults object + +```mjs +{ + locale: 'en', + imperial: 'false', + ui: { + renderer: 'react', + kiosk: false, + } +} +``` + diff --git a/packages/react-components/src/editor/swizzle/components/accordion.mjs b/packages/react-components/src/editor/swizzle/components/accordion.mjs new file mode 100644 index 00000000000..d2d561f0cbe --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/accordion.mjs @@ -0,0 +1,64 @@ +import { useState } from 'react' + +/* + * DaisyUI's accordion seems rather unreliable. + * So instead, we handle this in React state + */ +const getProps = (isActive) => ({ + className: `p-2 px-4 rounded-lg bg-transparent shadow + w-full mt-2 py-4 h-auto content-start text-left bg-opacity-20 + ${isActive ? 'hover:bg-transparent' : 'hover:bg-secondary hover:bg-opacity-10'}`, +}) + +const getSubProps = (isActive) => ({ + className: ` p-2 px-4 rounded bg-transparent w-full mt-2 py-4 h-auto + content-start bg-secondary text-left bg-opacity-20 + ${ + isActive + ? 'bg-opacity-100 hover:bg-transparent shadow' + : 'hover:bg-opacity-10 hover:bg-secondary ' + }`, +}) + +const components = { + button: (props) => , + div: (props) =>
{props.children}
, +} + +export const BaseAccordion = ({ + items, // Items in the accordion + act, // Allows one to preset the active (opened) entry + propsGetter = getProps, // Method to get the relevant props + component = 'button', +}) => { + const [active, setActive] = useState(act) + const Component = components[component] + + return ( + + ) +} + +export const SubAccordion = (props) => +export const Accordion = (props) => diff --git a/packages/react-components/src/editor/swizzle/components/aside-view-menu.mjs b/packages/react-components/src/editor/swizzle/components/aside-view-menu.mjs new file mode 100644 index 00000000000..13bca1d376a --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/aside-view-menu.mjs @@ -0,0 +1,122 @@ +import { useState } from 'react' + +export const AsideViewMenuButton = ({ + href, + label, + children, + onClick = false, + active = false, + extraClasses = 'lg:hover:bg-secondary lg:hover:text-secondary-content', + Swizzled, +}) => { + const className = `w-full flex flex-row items-center px-4 py-2 ${extraClasses} ${ + active + ? 'font-bold lg:font-normal bg-secondary bg-opacity-10 lg:bg-secondary lg:text-secondary-content lg:bg-opacity-50' + : 'lg:bg-neutral lg:text-neutral-content' + }` + const span = {label} + + return onClick ? ( + + ) : ( + + {span} + {children} + + ) +} + +export const ViewTypeIcon = ({ Swizzled, view, className = 'h-6 w-6 grow-0' }) => { + const Icon = Swizzled.components[`View${Swizzled.methods.capitalize(view)}Icon`] + if (!Icon) return + + return +} + +export const AsideViewMenuSpacer = () => ( +
+) + +export const AsideViewMenuIcons = ({ state, update, setDense, dense, Swizzled }) => { + const { t } = Swizzled.methods + const iconSize = 'h-6 w-6 grow-0' + + const output = [ + setDense(!dense)} + label={ + dense ? ( + '' + ) : ( + + Editor Views + + ) + } + extraClasses="hidden lg:flex text-accent bg-neutral hover:bg-accent hover:text-neutral-content" + > + {dense ? ( + + ) : ( + + )} + , + ] + + let i = 1 + for (const view of [ + 'spacer', + ...Swizzled.config.mainViews, + 'spacer', + ...Swizzled.config.extraViews, + 'spacerOver3', + ...Swizzled.config.devViews, + 'spacer', + 'picker', + ]) { + if (view === 'spacer') output.push() + else if (view === 'spacerOver3') + output.push(state.ui.ux > 3 ? : null) + else if (state.ui.ux >= Swizzled.config.uxLevels.views[view]) + output.push( + update.view(view)} + label={t(`pe:view.${view}.t`)} + active={state.view === view} + > + + + ) + i++ + } + + return output +} + +export const AsideViewMenu = ({ Swizzled, update, state }) => { + const [dense, setDense] = useState(true) + return ( +
+ +
+ ) +} diff --git a/packages/react-components/src/editor/swizzle/components/auth-wrapper.mjs b/packages/react-components/src/editor/swizzle/components/auth-wrapper.mjs new file mode 100644 index 00000000000..26ff0ad6be6 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/auth-wrapper.mjs @@ -0,0 +1,210 @@ +import { useState, useEffect } from 'react' + +export const AuthMessageWrapper = ({ children }) => ( +
{children}
+) + +export const ContactSupport = ({ t, Swizzled }) => ( +
+ + {t('contactSupport')} + +
+) + +export const AuthRequired = ({ t, banner, Swizzled }) => ( + + {banner} +

{Swizzled.methods.t('pe:authRequired')}

+

{t('pe:membersOnly')}

+
+ + + {t('pe:signUp')} + + + + {t('signIn')} + +
+
+) + +export const AccountInactive = ({ t, banner, Swizzled }) => ( + + {banner} +

{t('accountInactive')}

+

{t('accountInactiveMsg')}

+

{t('signupAgain')}

+
+ + {t('signUp')} + +
+
+) + +export const AccountDisabled = ({ t, banner, Swizzled }) => ( + + {banner} +

{t('accountDisabled')}

+

{t('accountDisabledMsg')}

+ +
+) + +export const AccountProhibited = ({ t, banner, Swizzled }) => ( + + {banner} +

{t('accountProhibited')}

+

{t('accountProhibitedMsg')}

+ +
+) + +export const AccountStatusUnknown = ({ t, banner, Swizzled }) => ( + + {banner} +

{t('statusUnknown')}

+

{t('statusUnknownMsg')}

+ +
+) + +export const RoleLacking = ({ t, requiredRole, role, banner, Swizzled }) => ( + + {banner} +

{t('roleLacking')}

+

+ + +) + +export const ConsentLacking = ({ banner, Swizzled }) => { + //const { setAccount, setToken, setSeenUser } = Swizzled.hooks.useAccount() + //const backend = Swizzled.hooks.useBackend() + + //const updateConsent = async ({ consent1, consent2 }) => { + // let consent = 0 + // if (consent1) consent = 1 + // if (consent1 && consent2) consent = 2 + // if (consent > 0) { + // const result = await backend.updateConsent(consent) + // if (result.success) { + // setToken(result.data.token) + // setAccount(result.data.account) + // setSeenUser(result.data.account.username) + // refresh() + // } else { + // console.log('something went wrong', result) + // refresh() + // } + // } + //} + + return ( + +

+ {banner} +

FIXME: Handle content form

+ {/**/} +
+
+ ) +} + +export const AuthWrapper = ({ children, requiredRole = 'user', Swizzled }) => { + const { t } = Swizzled.methods + const { useAccount, useBackend } = Swizzled.hooks + const { account, setAccount, token, admin, stopImpersonating, signOut } = useAccount() + const backend = useBackend() + + const [ready, setReady] = useState(false) + const [impersonating, setImpersonating] = useState(false) + const [error, setError] = useState(false) + const [refreshCount, setRefreshCount] = useState(0) + + /* + * Avoid hydration errors + */ + useEffect(() => { + const verifyAdmin = async () => { + const result = await backend.adminPing(admin.token) + if (result.success && result.data.account.role === 'admin') { + setImpersonating({ + admin: result.data.account.username, + user: account.username, + }) + } + setReady(true) + } + const verifyUser = async () => { + const result = await backend.ping() + if (result.success) { + // Refresh account in local storage + setAccount({ + ...account, + ...result.data.account, + }) + } else { + if (result.data?.error?.error) setError(result.data.error.error) + else signOut() + } + setReady(true) + } + if (admin && admin.token) verifyAdmin() + if (token) verifyUser() + else setReady(true) + }, [admin, token, refreshCount]) + + const refresh = () => { + setRefreshCount(refreshCount + 1) + setError(false) + } + + if (!ready) return + + const banner = impersonating ? ( +
+ + Hi {impersonating.admin}, you are currently impersonating {impersonating.user} + + +
+ ) : null + + const childProps = { t, banner, Swizzled } + + if (!token || !account.username) return + if (error) { + if (error === 'accountInactive') return + if (error === 'accountDisabled') return + if (error === 'accountBlocked') return + if (error === 'consentLacking') + return + return + } + + if ( + !Swizzled.config.roles.levels[account.role] || + Swizzled.config.roles.levels[account.role] < Swizzled.config.roles.levels[requiredRole] + ) { + return ( + + ) + } + + return children +} diff --git a/packages/react-components/src/editor/swizzle/components/curated-sets.mjs b/packages/react-components/src/editor/swizzle/components/curated-sets.mjs new file mode 100644 index 00000000000..bb25160bf82 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/curated-sets.mjs @@ -0,0 +1,37 @@ +export const CuratedMeasurementsSetLineup = ({ sets = [], locale, clickHandler, Swizzled }) => ( +
1 ? 'justify-start px-8' : 'justify-center' + } overflow-x-scroll`} + style={{ + backgroundImage: `url(/img/lineup-backdrop.svg)`, + width: 'auto', + backgroundSize: 'auto 100%', + backgroundRepeat: 'repeat-x', + }} + > + {sets.map((set) => { + const props = { + className: 'aspect-[1/3] w-auto h-96', + style: { + backgroundImage: `url(${Swizzled.methods.cloudImageUrl({ + id: `cset-${set.id}`, + type: 'lineup', + })})`, + width: 'auto', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + }, + onClick: () => clickHandler(set), + } + + return ( +
+ + {set[`name${Swizzled.methods.capitalize(locale)}`]} +
+ ) + })} +
+) diff --git a/packages/react-components/src/editor/swizzle/components/designs-view.mjs b/packages/react-components/src/editor/swizzle/components/designs-view.mjs new file mode 100644 index 00000000000..f6ed0a3a326 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/designs-view.mjs @@ -0,0 +1,28 @@ +/** + * The design view is loaded if and only if not design is passed to the editor + * + * @param {Object} props - All the props + * @param {Object} designs - Object holding all designs + * @param {object} props.Swizzled - An object holding swizzled code + * @param {Object} update - ViewWrapper state update object + */ +export const DesignsView = ({ designs = {}, Swizzled, update }) => ( +
+

{Swizzled.methods.t('pe:chooseADesign')}

+
    + {Object.keys(designs).map((name) => ( +
  • + +
  • + ))} +
+
+) diff --git a/packages/react-components/src/editor/swizzle/components/draft-view.mjs b/packages/react-components/src/editor/swizzle/components/draft-view.mjs new file mode 100644 index 00000000000..39975a0255e --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/draft-view.mjs @@ -0,0 +1,67 @@ +/** + * The draft view allows users to tweak their pattern + * + * @param (object) props - All the props + * @param {function} props.Design - The design constructor + * @param {array} props.missingMeasurements - List of missing measurements for the current design + * @param {object} props.state - The ViewWrapper state object + * @param {object} props.state.settings - The current settings + * @param {object} props.update - Helper object for updating the ViewWrapper state + * @param {object} props.Swizzled - An object holding swizzled code + * @return {function} DraftView - React component + */ +export const DraftView = ({ Design, state, update, Swizzled }) => { + /* + * Don't trust that we have all measurements + * + * We do not need to change the view here. That is done in the central + * ViewWrapper componenet. However, checking the measurements against + * the design takes a brief moment, so this component will typically + * render before that happens, and if measurments are missing it will + * throw and error. + * + * So when measurements are missing, we just return here and the view + * will switch on the next render loop. + */ + if (Swizzled.methods.missingMeasurements(state)) return null + + /* + * First, attempt to draft + */ + const { pattern } = Swizzled.methods.draft(Design, state.settings) + + let output = null + let renderProps = false + if (state.ui?.renderer === 'svg') { + try { + const __html = pattern.render() + output = ( + +
+ + ) + } catch (err) { + console.log(err) + } + } else { + renderProps = pattern.getRenderProps() + output = ( + + ) + } + + return ( + + ) : null + } + /> + ) +} diff --git a/packages/react-components/src/editor/swizzle/components/dynamic-mdx.mjs b/packages/react-components/src/editor/swizzle/components/dynamic-mdx.mjs new file mode 100644 index 00000000000..27319260e6e --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/dynamic-mdx.mjs @@ -0,0 +1,12 @@ +/* + * Loading MDX dynamically is non-trivial an depends on the + * environment in which the component is loaded. + * By default, we use this component, which disabled loading + * inline docs. If you want this to work, you need to pass in + * your own DynamicMdx component into the editor: + * + * + */ +export const DynamicMdx = ({ Swizzled }) => ( + Not implemented +) diff --git a/packages/react-components/src/editor/swizzle/components/error-view.mjs b/packages/react-components/src/editor/swizzle/components/error-view.mjs new file mode 100644 index 00000000000..d2997094f14 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/error-view.mjs @@ -0,0 +1,23 @@ +/** + * The error view is loaded if and only an error occurs that we can't handle + * + * @param {object} props - The component's props + * @param {object} props.Swizzled - Swizzled code + * @param {function} props.Design - The design constructor + * @param {object} props.state - The ViewWrapper state object + * @param {object} props.state.settings - The current settings + * @param {object} props.update - Helper object for updating the ViewWrapper state + * @param {array} props.missingMeasurements - List of missing measurements for the current design + * @param {object} props.components - The possibly swizzled components + * @param {object} props.methods - The possibly swizzled methods + * @param {function} props.methods.t - The translation method + * @param {object} props.config - The possibly swizzled pattern editor configuration + * @return {function} MeasurementsView - React component + */ +export const ErrorView = ({ Swizzled, state }) => ( +
+

{Swizzled.methods.t('oops')}

+

FIXME: Something went wrong

+
{JSON.stringify(state, null, 2)}
+
+) diff --git a/packages/react-components/src/editor/swizzle/components/flags.mjs b/packages/react-components/src/editor/swizzle/components/flags.mjs new file mode 100644 index 00000000000..c549905f1fd --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/flags.mjs @@ -0,0 +1,103 @@ +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/src/editor/swizzle/components/header-menu.mjs b/packages/react-components/src/editor/swizzle/components/header-menu.mjs new file mode 100644 index 00000000000..e88d0812c77 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/header-menu.mjs @@ -0,0 +1,525 @@ +import { useState } from 'react' + +export const HeaderMenu = ({ state, Swizzled, update, Design, pattern }) => { + const [open, setOpen] = useState() + + /* + * Guard views that require measurements agains missing measurements + * and make sure there's a view-specific header menu + */ + const ViewMenu = + !Swizzled.methods.missingMeasurements(state) && + Swizzled.components[`HeaderMenu${Swizzled.config.viewComponents[state.view]}`] + ? Swizzled.components[`HeaderMenu${Swizzled.config.viewComponents[state.view]}`] + : Swizzled.components.Null + + return ( +
+
+ + +
+
+ ) +} + +export const HeaderMenuAllViews = ({ state, Swizzled, update, open, setOpen }) => ( + +) + +export const HeaderMenuDraftView = (props) => { + const { Swizzled } = props + const flags = props.pattern?.setStores?.[0]?.plugins?.['plugin-annotations']?.flags + + return ( + <> +
+ + + + {flags ? : null} +
+ + + + + ) +} + +export const HeaderMenuDropdown = (props) => { + const { Swizzled, tooltip, toggle, open, setOpen, id } = props + /* + * We need to use both !fixed and md:!absolute here to override DaisyUI's + * classes on dropdown-content to force the dropdown to use the available + * screen space on mobile, rather than be positioned under its toggle button + */ + + return props.disabled ? ( + + + + ) : ( + +
+
setOpen(open === id ? false : id)} + > + {toggle} +
+
+ {props.children} +
+ {open === id && ( +
setOpen(false)} + >
+ )} +
+
+ ) +} + +export const HeaderMenuDraftViewDesignOptions = (props) => { + const { Swizzled } = props + + return ( + + + {Swizzled.methods.t('pe:designOptions.t')} + + } + > + + + ) +} + +export const HeaderMenuDraftViewCoreSettings = (props) => { + const { Swizzled } = props + + return ( + + + {Swizzled.methods.t('pe:coreSettings.t')} + + } + > + + + ) +} + +export const HeaderMenuDraftViewUiPreferences = (props) => { + const { Swizzled } = props + + return ( + + + {Swizzled.methods.t('pe:uiPreferences.t')} + + } + > + + + ) +} + +export const HeaderMenuDraftViewFlags = (props) => { + const { Swizzled } = props + const count = Object.keys(Swizzled.methods.flattenFlags(props.flags)).length + + return ( + + + + {Swizzled.methods.t('pe:flags')} + ({count}) + + + } + > + + + ) +} + +export const HeaderMenuDraftViewIcons = (props) => { + const { Swizzled, update } = props + const Button = Swizzled.components.HeaderMenuButton + const size = 'w-5 h-5' + const muted = 'text-current opacity-50' + const ux = props.state.ui.ux + const levels = { + ...props.Swizzled.config.uxLevels.core, + ...props.Swizzled.config.uxLevels.ui, + } + + return ( +
+ {ux >= levels.sa ? ( + + ) : null} + {ux >= levels.paperless ? ( + + ) : null} + {ux >= levels.complete ? ( + + ) : null} + {ux >= levels.expand ? ( + + ) : null} + {ux >= levels.units ? ( + + ) : null} + + {ux >= levels.ux ? ( +
+ + {[0, 1, 2, 3, 4].map((i) => ( + + ))} + +
+ ) : null} + {ux >= levels.aside ? ( + + ) : null} + {ux >= levels.kiosk ? ( + + ) : null} + {ux >= levels.rotate ? ( + + ) : null} + {ux >= levels.renderer ? ( + + ) : null} +
+ ) +} + +export const HeaderMenuUndoIcons = (props) => { + const { Swizzled, update, state, Design } = props + const Button = Swizzled.components.HeaderMenuButton + const size = 'w-5 h-5' + const undos = props.state._?.undos && props.state._.undos.length > 0 ? props.state._.undos : false + + return ( +
+ + + + + {Swizzled.methods.t('pe:undo')} + + } + > + {undos ? ( +
    + {undos.slice(0, 9).map((step, index) => ( +
  • + +
  • + ))} +
  • + { + return null /*update.state(index, state._) */ + }} + > +
    +
    + + {Swizzled.methods.t(`pe:view.undos.t`)}... +
    + {undos.length} +
    +
    +
  • +
+ ) : null} +
+ + +
+ ) +} + +export const HeaderMenuSaveIcons = (props) => { + const { Swizzled, update } = props + const Button = Swizzled.components.HeaderMenuButton + const size = 'w-5 h-5' + const saveable = props.state._?.undos && props.state._.undos.length > 0 + + return ( +
+ + + +
+ ) +} + +export const HeaderMenuIcon = (props) => { + const { Swizzled, name, extraClasses = '' } = props + const Icon = + Swizzled.components[`${Swizzled.methods.capitalize(name)}Icon`] || Swizzled.components.Noop + return +} +export const HeaderMenuIconSpacer = () => | + +export const HeaderMenuButton = ({ + Swizzled, + updateHandler, + children, + tooltip, + disabled = false, +}) => ( + + + +) + +export const HeaderMenuViewMenu = (props) => { + const { Swizzled, update, state } = props + const output = [] + let i = 1 + for (const viewName of [ + 'spacer', + ...Swizzled.config.mainViews, + 'spacer', + ...Swizzled.config.extraViews, + 'spacerOver3', + ...Swizzled.config.devViews, + 'spacer', + 'picker', + ]) { + if (viewName === 'spacer') output.push() + else if (viewName === 'spacerOver3') + output.push(state.ui.ux > 3 ? : null) + else if ( + state.ui.ux >= Swizzled.config.uxLevels.views[viewName] && + (Swizzled.config.measurementsFreeViews.includes(viewName) || + state._.missingMeasurements.length < 1) + ) + output.push( +
  • + update.view(viewName)} + > + + {Swizzled.methods.t(`pe:view.${viewName}.t`)} + +
  • + ) + i++ + } + + return ( + + + {Swizzled.methods.t('pe:views.t')} + + } + > +
      + {output} +
    +
    + ) +} diff --git a/packages/react-components/src/editor/swizzle/components/html-span.mjs b/packages/react-components/src/editor/swizzle/components/html-span.mjs new file mode 100644 index 00000000000..3d98e8acc26 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/html-span.mjs @@ -0,0 +1 @@ +export const HtmlSpan = ({ html }) => diff --git a/packages/react-components/src/editor/swizzle/components/icons.mjs b/packages/react-components/src/editor/swizzle/components/icons.mjs new file mode 100644 index 00000000000..fc2952d6858 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/icons.mjs @@ -0,0 +1,1134 @@ +/* + * Used inside the pattern editor + */ +export const logoPath = + 'm18.56 0c-0.4945 0.3515-0.469 0.3065-0.8685 0.437-0.916 0.2995-1.7945 0.135-2.837 0.048-0.3135-0.035-0.6245-0.0555-0.928-0.0575-1.5325-0.0105-2.834 0.439-3.0805 1.694-0.4545 0.2755-0.8725 0.609-1.2865 0.943-0.884 0.6975-1.5495 1.55-2.0035 2.5775-0.62 1.5175-0.06 3.146 0.2175 4.684 0.066 0.36 0.135 0.703 0.172 0.8355 0.0525 0.1865 0.145 0.3645 0.2455 0.5245 0.0235 4e-3 0.1475-0.187 0.177-0.2715 0.043-0.123 0.0385-0.3205-0.0085-0.4905-0.104-0.3825-0.203-0.693-0.2115-0.849-0.015-0.293 0.042-0.5635 0.149-0.6975 0.038-0.0475 0.125 0.1975 0.1025 0.2885-0.0265 0.1095-0.0465 0.297-0.038 0.3835 0.0235 0.293 0.0665 0.6065 0.12 0.8805 0.0685 0.3535 0.098 0.5805 0.0855 0.6685-9e-3 0.064-0.039 0.1285-0.154 0.3265-0.1 0.1735-0.152 0.314-0.16 0.438-0.0085 0.121 0.028 0.4235 0.062 0.4975 0.0495 0.1155 0.1985 0.237 0.3285 0.267 0.1245 0.0475 0.187 0.146 0.251 0.2565 0.1555 0.2965 0.2755 0.6575 0.3945 1.2015 0.058 0.2605 0.1065 0.493 0.122 0.615-0.96 1e-3 -2.1895 0.0015-3.3095 0.0015-0.377 6e-3 -1.058-0.171-1.6825-0.212-0.0905-0.977-0.5195-2.112-1.2535-2.178-0.501-0.0455-0.9165 0.145-1.268 0.9365l0.01 0.0425c0.2075-0.1735 0.4265-0.704 1.2155-0.6675 0.606 0.0275 0.808 1.1745 0.868 1.8645-0.369 0.027-0.683 0.1405-0.847 0.424h-0.0035c0 5e-4 0 0.0015 0.0015 0.0025-0.0015 1e-3 -0.0015 2e-3 -0.0015 3e-3h0.0035c0.169 0.2905 0.4945 0.403 0.877 0.4255 0.2555 7.225 7.047 8.157 8.903 8.157 6.924 0 9.348-4.705 9.7125-6.5685 0.1705 0.794-0.3665 1.8055-0.495 2.552 1.4605-1.6885 1.1965-3.312 0.9295-4.945 0.222 0.264 0.5225 0.4275 0.93 0.337-0.2905-0.194-0.6845-0.058-0.9205-0.8765-0.103-0.3535-0.192-0.6185-0.2805-0.841-0.191-0.7165-0.462-1.401-0.795-2.068-0.281-0.7235-0.0955-1.1925-0.1235-1.8135 0.5055 1.667 0.8215 2.1105 1.4115 2.285-1.484-1.788-0.976-4.5565-1.8145-7.0275 0.3795 0.223 0.8125 0.29 1.2735 0.0175-0.446-0.127-0.891 0.2085-1.531-0.732-0.5405-1.0515-1.3235-1.828-2.2735-2.513-0.509-0.332-1.074-0.555-1.642-0.762 0.5785-0.145 1.2245-0.66 1.2545-1.0445zm-0.9705 5.5535c0.762 0.278 1.602 1.334 1.5925 2.37v0.058c-0.0205 1.407-0.66 2.1635-0.633 3.1005 0.0345 1.1035 0.5095 1.4885 0.604 1.6725-0.162-0.6805-0.257-1.5365-0.043-2.2145 0.275-0.872 0.5525-1.594 0.5325-2.277-0.01-0.16-0.078-0.7585-0.1235-1.0235 0.817 1.179-0.177 2.8935 0.109 4.0155 0.497 1.9545 2.7245 2.2015 2.0615 6.1395-0.5835 3.462-4.5815 6.0895-8.6795 6.0895-3.038 0-8.3025-1.6815-8.5625-7.646 0.6175-0.044 1.2815-0.216 1.654-0.21 1.126 0 2.412 5e-4 3.381 1e-3 0.182 0.821 0.3185 1.019 1.009 1.566 0.768 0.604 0.947 0.6775 2.083 0.6885 1.1365 0.0115 1.4735-0.232 2.576-1.275 0.238-0.279 0.341-0.6445 0.4565-0.988 1.134-0.0105 1.961-0.0305 2.7745-0.0685 0.8285-0.0375 0.9455 0 2.2805-0.1375-1.335-0.1375-1.452-0.1-2.2805-0.138-0.792-0.036-1.594-0.0565-2.6785-0.0665 0.091-0.4085 0.221-0.8075 0.3595-1.202 0.0855-0.2325 0.186-0.459 0.289-0.6845l0.1125-0.035c0.217-0.077 0.409-0.242 0.4855-0.465 0.0985-0.2955 0.0285-0.6275-0.162-0.869-0.0655-0.0905-0.147-0.206-0.1805-0.257-0.1005-0.159-0.103-0.2475-0.018-0.8385 0.0715-0.495 0.0795-0.754 0.03-1.005-0.01-0.1435-0.011-0.4385-0.0155-0.518 0.038 0.021 0.1205 0.209 0.204 0.4635 0.083 0.2555 0.0965 0.3085 0.1155 0.526 0.021 0.247-0.0715 0.43-0.1475 0.7985-0.038 0.19-0.0715 0.3665-0.0715 0.3905 0 0.0255 0.018 0.0795 0.037 0.1215 0.0445 0.094 0.128 0.226 0.1435 0.226 0.2725-0.3005 0.4325-0.6715 0.575-1.048 0.15-0.426 0.194-0.878 0.299-1.3165 0.085-0.382 0.183-0.7645 0.2135-1.1565 0.0615-0.765 0.0255-1.305-0.1435-2.102-0.0405-0.18-0.1575-0.5235-0.239-0.6855zm-2.68 3.7685c0.2925-0.0035 0.582 0.032 0.8575 0.1115 0.3745 0.1435 0.427 0.478 0.496 0.8535 0.0385 0.24 0.037 0.4125-0.0065 0.6945-0.0305 0.409-0.193 0.7255-0.548 0.948-0.5355 0.099-1.108 0.1945-1.562-0.16-0.381-0.525-0.6105-1.1885-0.523-1.8355 0.0555-0.2655 0.179-0.4035 0.433-0.486 0.2735-0.0785 0.563-0.1215 0.853-0.126zm-4.4415 0.0475c0.2735-0.0025 0.55 0.0265 0.702 0.1235 0.6525 0.4415 0.443 1.16 0.185 1.7905-0.3755 0.8255-1.1875 0.795-1.9745 0.7885-0.4355-0.1275-0.4755-0.4845-0.5385-0.866-0.054-0.3685-0.169-0.7635-0.073-1.134 0.2465-0.596 1.1475-0.6645 1.699-0.7025zm9.9515 0.103c0.0035 1.5865 0.2745 2.366 0.8185 3.4895-0.3205-0.6115-0.7785-0.9595-0.949-1.6905-0.326-1.4115 0.0255-1.3325 0.1305-1.799zm-7.9065 1.149c0.086 0.087 0.1275 0.207 0.202 0.3025 0.0575-0.0985 0.1165-0.1965 0.1905-0.284 0.0385 1e-3 0.0855 0.077 0.128 0.213 0.182 0.503 0.2175 1.0565 0.4535 1.54 0.2205 0.35-0.0805 0.554-0.411 0.57-0.241-5e-4 -0.343-0.165-0.4845-0.328-0.0365 0.1065-0.106 0.175-0.189 0.247-0.211 0.177-0.6245 0.1115-0.6885-0.1675 0.085-0.533 0.3565-1.0225 0.5345-1.5335 0.0885-0.1865 0.0895-0.3295 0.2645-0.5595zm-3.096 2.6925c0.1065 0 0.399 0.1985 0.4585 0.3105 0.041 0.0745 0.1345 0.3645 0.141 0.435 0.0105 0.084-0.015 0.283-0.041 0.337-0.019 0.0385-0.0335 0.044-0.0555 0.019-0.0185-0.021-0.2635-0.491-0.42-0.802-0.123-0.249-0.136-0.2995-0.083-0.2995zm6.111 0.1555c4e-3 5e-4 0.01 2e-3 0.0155 0.0035 0.033 0.0135 0.01 0.1305-0.114 0.5555-0.0235 0.128-0.0805 0.229-0.164 0.313-0.0275 0-0.04-0.032-0.083-0.2095-0.0365-0.1515-0.0405-0.2865-0.015-0.4075 0.044-0.1515 0.222-0.198 0.3605-0.255zm-0.7415 0.9265c0.0105-2e-3 0.0205 0.0035 0.0335 0.014 0.045 0.0315 0.0515 0.1145 0.0215 0.277-0.0365 0.209-0.0445 0.232-0.0985 0.2535-0.0235 0.0105-0.0655 0.018-0.0935 0.018-0.0505-9e-3 -0.0635-0.05-0.0515-0.112 0-0.13 0.038-0.243 0.124-0.3765 0.0325-0.05 0.049-0.0715 0.0645-0.074zm-4.3165 0.0095c0.0345 0 0.1385 0.075 0.177 0.127 0.043 0.055 0.092 0.3825 0.0645 0.439-0.0315 0.071-0.1855 0.0355-0.228-0.053-0.026-0.053-0.0875-0.339-0.0875-0.407 0-0.063 0.0305-0.106 0.074-0.106zm3.9455 0.0865c0.042 0.06 0.053 0.137 0.044 0.306l-0.0085 0.154-0.044 0.044c-0.0265 0.0245-0.0715 0.0545-0.0985 0.067-0.0595 0.028-0.105 0.0305-0.1135 8e-3 -0.01-0.03 7e-3 -0.221 0.0255-0.2855 0.0215-0.0665 0.118-0.265 0.15-0.307 0.0145-0.0385 0.0315 0.0095 0.045 0.0135zm-2.5105-9e-3c0.0905 0.023 0.1305 0.1045 0.18 0.1785l0.0335 0.066-0.047 0.1635c-0.025 0.09-0.0515 0.171-0.0595 0.18-9e-3 0.01-0.0425 0.015-0.092 0.0145-0.132-0.0035-0.147-9e-3 -0.1825-0.063l-0.033-0.049 0.028-0.1375c0.0405-0.198 0.06-0.2575 0.105-0.3085 0.0235-0.0275 0.047-0.0425 0.0675-0.0445zm-0.8355 0.1415 0.0745 0.0745 0.0125 0.1685c0.0065 0.092 0.0095 0.1775 0.0045 0.188-0.0045 0.0145-0.0315 0.0185-0.115 0.0185h-0.1085c-0.058-0.0635-0.076-0.141-0.1005-0.221-0.057-0.2405-0.057-0.35 2e-3 -0.3645 0.0965 6e-3 0.16 0.076 0.2305 0.136zm2.9-0.1155c0.118 0.0315 0.0945 0.219 0.094 0.353-9e-3 0.217-0.0175 0.262-0.0455 0.29-0.0485 0.0485-0.1835 0.0215-0.249-0.0505-0.0215-0.026-0.0235-0.034-0.0065-0.1395 0.0195-0.1285 0.0445-0.2085 0.1-0.3185 0.0405-0.079 0.0785-0.1285 0.107-0.1345zm-2.663 0.01c0.0065-5e-4 0.017 0 0.027 1e-3 0.075 6e-3 0.145 0.055 0.207 0.145l0.05 0.0735c0.0045 0.1205 0 0.2475-0.0215 0.3595-0.013 0.0065-0.067 0.0165-0.12 0.0215-0.092 0.0085-0.1005 0.0065-0.1325-0.0215-0.0445-0.038-0.057-0.1085-0.068-0.3425-0.0065-0.191-2e-3 -0.2335 0.058-0.2365zm1.1345 0.04c0.0805 0.017 0.1315 0.06 0.154 0.1305 0.018 0.0605 0.029 0.399 0.0115 0.4225-6e-3 0.01-0.044 0.0225-0.0875 0.028-0.162 0.0205-0.305 5e-3 -0.319-0.0335-0.018-0.044 0.1025-0.48 0.147-0.534 0.019-0.042 0.065-0.0095 0.094-0.0135zm1.049 3e-3c0.0355-0.0035 0.0735 0.0305 0.1105 0.103 0.03 0.0605 0.0345 0.0815 0.0345 0.217 0 0.108-0.0065 0.1545-0.018 0.1645-0.01 8e-3 -0.0505 0.0225-0.0935 0.0335-0.075 0.0195-0.0915 0.0215-0.115 0.0135l-0.1125-0.0205 0.0085-0.067c8e-3 -0.0875 0.0655-0.2815 0.106-0.3655 0.024-0.05 0.0515-0.075 0.0795-0.0785zm-0.489 0.0015c0.0235-1e-3 0.0345 0.0045 0.0495 0.021 0.0355 0.042 0.0805 0.166 0.109 0.2985 0.038 0.1865 0.038 0.186-0.0435 0.2105-0.0355 0.011-0.1105 0.0225-0.164 0.0255-0.1765 9e-3 -0.19-0.0015-0.1685-0.1575 0.017-0.139 0.0855-0.358 0.115-0.374 0.032-0.017 0.069-0.0165 0.1025-0.024zm-8.9965 0.7045c0.0015-5e-4 0.0035 0 0.0035 0 0.0045 0.0975 0.0045 0.196 0.0065 0.294-0.2475-0.019-0.4295-0.078-0.4295-0.1475 0-0.0685 0.1755-0.127 0.4195-0.1465zm0.4325 0.0085c0.2005 0.025 0.339 0.0775 0.3365 0.138 0 0.061-0.134 0.113-0.333 0.1375-2e-3 -0.0915-2e-3 -0.1835-0.0035-0.2755zm9.363 0.2665c0.017-0.0015 0.0245-3e-3 0.0505-5e-4 0.104 0.0105 0.119 0.017 0.119 0.052 0 0.046-0.079 0.1845-0.1325 0.2325-0.025 0.024-0.0595 0.044-0.0715 0.044-0.06 0-0.095-0.1265-0.067-0.243 0.017-0.063 0.048-0.0825 0.1015-0.085zm-0.3775 0.0415c0.0465-4e-3 0.0915 0.0085 0.1365 0.0145-0.013 0.1315-0.072 0.239-0.1815 0.3105-0.027 0-0.0405-0.0515-0.0405-0.164 0-0.134 7e-3 -0.1595 0.0855-0.161zm-0.414 0.0485c0.0965 0 0.1815 0.0045 0.1855 0.01 0.018 0.017-0.034 0.146-0.1105 0.277-0.0655 0.1165-0.075 0.125-0.1155 0.128-0.159-0.018-0.1545-0.2045-0.179-0.3325 0-0.076 0.017-0.0825 0.2195-0.0825zm-1.5045 0.0145c0.1105 2e-3 0.1535 0.0185 0.1535 0.061 0 0.054-0.041 0.1615-0.0645 0.175-0.0355 0.0195-0.0385 0.0185-0.1085-0.0545-0.1105-0.124-0.123-0.147 0.0195-0.1815zm0.532 0.0055c2e-3 3e-3 0.0235 0.042 0.045 0.086 0.047 0.0915 0.0505 0.1315 0.017 0.162-0.079 0.045-0.0955 0.0195-0.167-0.026-0.083-0.0785-0.1485-0.184-0.127-0.206 0.074-0.0265 0.1555-0.0165 0.232-0.016zm0.211 0.0025 0.1975 0.0035c0.077 0 0.1435 4e-3 0.147 0.01 0.0135 0.012-0.03 0.269-0.0535 0.327-0.027 0.065-0.1215 0.0655-0.1705-0.0115-0.07-0.1105-0.116-0.2035-0.1175-0.2675z' + +export const IconWrapper = ({ + className = 'w-6 h-6', + stroke = 2, + children = null, + fill = false, + fillOpacity = 1, + dasharray = null, + wrapped = true, +}) => + wrapped ? ( + + {children} + + ) : ( + <> {children} + ) + +// Looks like a play triangle +export const ApplyIcon = (props) => ( + + + +) + +// Looks like a lab beaker +export const BeakerIcon = (props) => ( + + + +) + +// Looks like a left U-turn that we slightly rotate +export const BackIcon = (props) => ( + + + +) + +// Looks like a red X +export const BoolNoIcon = ({ size = 6 }) => ( + +) + +// Looks like a green checkbox +export const BoolYesIcon = ({ size = 6 }) => ( + +) + +// Looks like a bookmark +export const BookmarkIcon = (props) => ( + + + +) + +// Looks lik a speech bubble +export const ChatIcon = (props) => ( + + + +) + +// Looks like a circle +export const CircleIcon = (props) => ( + + + +) + +// Looks like a X +export const CloseIcon = (props) => ( + + + +) + +// Looks like a museum building +export const CuratedMeasurementsSetIcon = (props) => ( + + + +) + +// Looks like a coathanger +export const DesignIcon = (props) => ( + + + +) + +// Looks like a left and right pane with different level of detail +export const DetailIcon = (props) => ( + + + +) + +// Looks like a document icon +export const DocsIcon = (props) => ( + + + +) + +// Looks like a down pointing chevron +export const DownIcon = (props) => ( + + + +) + +// Looks like a pencil +export const EditIcon = (props) => ( + + + +) + +// Looks like FIXME +export const ErrorIcon = (props) => ( + + + +) + +// Looks like arrows pointing outwards +export const ExpandIcon = (props) => ( + + + +) + +// Looks like a file/sheet with an arrow pointing downwards +export const ExportIcon = (props) => ( + + + +) + +// Looks like a ! in a triangle, is intended to be shown on an error background +export const FailureIcon = ({ size = 6 }) => ( + +) + +// Looks lik a flag +export const FlagIcon = (props) => ( + + + +) + +// Looks like a gauge or speedometer +export const GaugeIcon = (props) => ( + + + + +) + +// Looks like abox +export const GroupIcon = (props) => ( + + + +) + +// Looks like a question mark in a circle +export const HelpIcon = (props) => ( + + + +) + +// Looks like a pie with a slice a bit out of it +export const IncludeIcon = (props) => ( + + + + +) + +// Looks like a rectangle with rounded corners (like a full screen display) +export const KioskIcon = (props) => ( + + + +) + +// Looks like a left pointing chevron +export const LeftIcon = (props) => ( + + + +) + +// Looks like a bullet list +export const ListIcon = (props) => ( + + + +) + +// Looks like a padlock +export const LockIcon = (props) => ( + + + +) + +// Looks like 4 boxes spaces out +export const MarginIcon = (props) => ( + + + +) + +// Looks like a tape measure +export const MeasurementsIcon = (props) => ( + + + +) + +// Looks like two people's heads next/behinf to each other, one bigger, one smaller +export const MeasurementsSetIcon = (props) => ( + + + +) + +// Looks like 3 horizontal lines (hamburger menu) +export const MenuIcon = (props) => ( + + + +) + +// Looks like a X +export const NoIcon = (props) => ( + + + +) + +// Looks like a checkmark +export const OkIcon = (props) => ( + + + +) + +// Looks like sliders on a mixing panel +export const OptionsIcon = (props) => ( + + + +) + +// Looks like a grid +export const PaperlessIcon = (props) => ( + + + + +) + +// Looks like a + +export const PlusIcon = (props) => ( + + + +) + +// Looks like a printer +export const PrintIcon = (props) => ( + + + +) + +// Looks like a single rewind arrow +export const ResetIcon = (props) => ( + + + +) + +// Looks like a double rewind arrow +export const ResetAllIcon = (props) => ( + + + +) + +// Looks like a right pointing chevron +export const RightIcon = (props) => ( + + + +) + +// Looks like a rocket +export const RocketIcon = (props) => ( + + + +) + +// Looks like two arrows in a circular layout +export const RotateIcon = (props) => ( + + + +) + +// Looks like a cloud with a plus sign in it +export const SaveIcon = (props) => ( + + + +) + +// Looks like a cloud with a plus sign in it +export const SaveAsIcon = (props) => ( + + + +) + +// Looks like a small solid circle with a larger dashed circle around it +export const SaIcon = (props) => ( + + + + +) + +// Looks like lines of varying thickness +export const ScaleIcon = (props) => ( + + + + + +) + +// Looks like a gear +export const SettingsIcon = (props) => ( + + + + +) + +// Looks like a spinning circle +export const SpinnerIcon = (props) => ( + + + + +) + +// Looks like a white checkbox, intended to be shown on a success-colored background +export const SuccessIcon = ({ size = 6 }) => ( + +) + +// Looks like a light bulb +export const TipIcon = (props) => ( + + + +) + +// Looks like a trashcan +export const TrashIcon = (props) => ( + + + +) + +// Looks like a desktop screen +export const UiIcon = (props) => ( + + + +) + +// Looks like a rewind arrow, but takes text to go inside it +export const UndoIcon = (props) => ( + + + {props.text ? ( + + {props.text} + + ) : null} + +) + +// Looks like a bit of measuring tape +export const UnitsIcon = (props) => ( + + + + +) + +// Looks like an up pointing chevron +export const UpIcon = (props) => ( + + + +) +// Looks like a cloud with an arrow pointing upwards in it +export const UploadIcon = (props) => ( + + + +) + +// Looks like old-timey scales +export const UxIcon = (props) => ( + + + +) + +// Looks like an ! in a triangle +export const WarningIcon = (props) => ( + + + +) + +// Looks like a wrench +export const WrenchIcon = (props) => ( + + + +) + +// Looks like a box in dashed lines +export const XrayIcon = (props) => ( + + + +) + +// These icons all reuse existing icons +export const ViewDraftIcon = OptionsIcon +export const ViewMeasurementsIcon = MeasurementsIcon +export const ViewTestIcon = BeakerIcon +export const ViewTimingIcon = GaugeIcon +export const ViewPrintLayoutIcon = PrintIcon +export const ViewSaveIcon = SaveIcon +export const ViewExportIcon = ExportIcon +export const ViewEditSettingsIcon = EditIcon +export const ViewLogsIcon = ListIcon +export const ViewInspectIcon = XrayIcon +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 + +/* + * Not used inside the pattern editor +// Used in several icons +const page = + 'M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z' + +export const BeakerIcon = (props) => ( + + + +) + +export const BioIcon = (props) => ( + + + +) + + +export const BoxIcon = (props) => ( + + + +) + +export const BriefcaseIcon = (props) => ( + + + +) + +export const BugIcon = (props) => ( + + + +) + +export const BulletIcon = (props) => ( + + + +) + +export const CalendarIcon = (props) => ( + + + +) + +export const CameraIcon = (props) => ( + + + + +) + +export const CcByIcon = (props) => ( + + + +) + +export const CisFemaleIcon = (props) => ( + + + +) + +export const CisMaleIcon = (props) => ( + + + +) + +export const ClearIcon = (props) => ( + + + +) + +export const CloneIcon = (props) => ( + + + +) + +export const CodeIcon = (props) => ( + + + +) + +export const CogIcon = (props) => ( + + + +) + +export const CommunityIcon = (props) => ( + + + +) + +export const CompareIcon = (props) => ( + + + +) + +export const CopyIcon = (props) => ( + + + +) + +export const CoverPageIcon = (props) => ( + + + + + + +) + +export const CsetIcon = (props) => ( + + + +) +export const CuratedMeasurementsSetIcon = CsetIcon + +export const CloudIcon = (props) => ( + + + +) + +export const CutIcon = (props) => ( + + + +) + +export const CuttingLayoutIcon = (props) => ( + + + + +) + +export const DetailIcon = (props) => ( + + + + +) + +export const DiscordIcon = (props) => ( + + + +) + +export const DoubleLeftIcon = (props) => ( + + + +) +export const DoubleRightIcon = (props) => ( + + + +) + +export const DownloadIcon = (props) => ( + + + +) + +export const EmailIcon = (props) => ( + + + +) + +export const FacebookIcon = (props) => ( + + + +) + +export const FilterIcon = (props) => ( + + + +) + +export const FingerprintIcon = (props) => ( + + + +) + +export const ForbiddenIcon = (props) => ( + + + +) + +export const FreeSewingIcon = (props) => ( + + + +) + +export const GaugeIcon = (props) => ( + + + + +) + +export const GitHubIcon = (props) => ( + + + +) + +export const GuideIcon = (props) => ( + + + +) + +export const GoogleIcon = (props) => ( + + + +) + +export const HeartIcon = (props) => ( + + + +) + +export const HomeIcon = (props) => ( + + + +) + +export const I18nIcon = (props) => ( + + + +) + +export const ImportIcon = (props) => ( + + + +) + +export const IncludeIcon = (props) => ( + + + + +) + +export const InstagramIcon = (props) => ( + + + +) + +export const KeyIcon = (props) => ( + + + +) + +export const LabelIcon = (props) => ( + + + + +) + +export const LeftRightIcon = (props) => ( + + + +) + +export const LinkIcon = (props) => ( + + + +) + +export const MastodonIcon = (props) => ( + + + +) + +export const MarginIcon = (props) => ( + + + +) + +export const MeasieIcon = (props) => ( + + + +) + +export const MegaphoneIcon = (props) => ( + + + +) + +export const MenuAltIcon = (props) => ( + + + +) + +export const MeasurementsSetIcon = MsetIcon + +export const MsfIcon = (props) => ( + + + +) + +// NarrowIcon - Looks like two arrow pointing inwards +export const NarrowIcon = (props) => ( + + + +) + +export const NewMsetIcon = (props) => ( + + + +) + +export const NewsletterIcon = (props) => ( + + + +) + +export const NoteIcon = (props) => ( + + + +) + +export const OpackIcon = (props) => ( + + + +) +export const OptionPackIcon = OpackIcon + +export const OpenSourceIcon = (props) => ( + + + +) + +export const PageMarginIcon = (props) => ( + + + + +) + +export const PageOrientationIcon = (props) => ( + + + + +) + +export const PageSizeIcon = (props) => ( + + + + +) + +export const PatternIcon = (props) => ( + + + +) + +export const NewPatternIcon = (props) => ( + + + +) + +export const PluginIcon = (props) => ( + + + +) + + +export const PrintIcon = (props) => ( + + + +) + +export const PrivacyIcon = (props) => ( + + + +) + +export const RedditIcon = (props) => ( + + + +) + +export const ReloadIcon = (props) => ( + + + +) + +export const RssIcon = (props) => ( + + + +) + +export const ScaleIcon = (props) => ( + + + + + +) + +export const SearchIcon = (props) => ( + + + +) + +export const ShieldIcon = (props) => ( + + + +) + +export const ShowcaseIcon = (props) => ( + + + + +) + +export const SigninIcon = (props) => ( + + + +) + +export const SignoutIcon = (props) => ( + + + +) + +export const StarIcon = (props) => ( + + + +) + +export const ThemeIcon = (props) => ( + + + +) + +export const TikTokIcon = (props) => ( + + + +) + +export const TutorialIcon = (props) => ( + + + + + +) + +export const TrophyIcon = (props) => ( + + + +) + +export const TwitchIcon = (props) => ( + + + +) + +export const TwitterIcon = (props) => ( + + + +) + + +export const UserIcon = (props) => ( + + + +) + +export const VersionIcon = (props) => ( + + + +) + +// WideIcon - Looks like two arrow pointing outwards +export const WideIcon = (props) => ( + + + +) + +export const XrayIcon = (props) => ( + + + +) + +export const YouTubeIcon = (props) => ( + + + +) + +export const ZoomInIcon = (props) => ( + + + +) + +export const ZoomOutIcon = (props) => ( + + + +) +*/ diff --git a/packages/react-components/src/editor/swizzle/components/index.mjs b/packages/react-components/src/editor/swizzle/components/index.mjs new file mode 100644 index 00000000000..9bf1262b860 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/index.mjs @@ -0,0 +1,453 @@ +/************************************************************************* + * * + * FreeSewing's pattern editor allows swizzling components * + * * + * To 'swizzle' means to replace the default implementation of a * + * component with a custom one. It allows one to customize * + * the pattern editor. * + * * + * This file holds the 'swizzleComponents' method that will return * + * the various components that can be swizzled, or their default * + * implementation. * + * * + * To use a custom version, simply pas it as a prop into the editor * + * under the 'components' key. So to pass a custom 'TemporaryLoader' * + * component, you do: * + * * + * * + * * + *************************************************************************/ + +/* + * Import of components that can be swizzled + */ +import { Link, AnchorLink, PageLink, WebLink, CardLink } from './link.mjs' +// Accordion +import { BaseAccordion, SubAccordion, Accordion } from './accordion.mjs' +// Auth wrapper +import { + AuthWrapper, + AuthMessageWrapper, + ContactSupport, + AuthRequired, + AccountInactive, + AccountDisabled, + AccountProhibited, + AccountStatusUnknown, + RoleLacking, + ConsentLacking, +} from './auth-wrapper.mjs' +// Ux +import { Ux } from './ux.mjs' +// Popout +import { Popout } from './popout.mjs' +// Loader +import { TemporaryLoader } from './loaders.mjs' +// Measurements Sets +import { UserSetPicker, BookmarkedSetPicker, CuratedSetPicker } from './sets.mjs' +// Curated Measurements Sets +import { CuratedMeasurementsSetLineup } from './curated-sets.mjs' +import { MeasurementsSetCard } from './measurements-set-card.mjs' +// Icons +import { + ApplyIcon, + BackIcon, + BeakerIcon, + BookmarkIcon, + BoolNoIcon, + BoolYesIcon, + CircleIcon, + CloseIcon, + CuratedMeasurementsSetIcon, + DesignIcon, + DetailIcon, + DocsIcon, + DownIcon, + EditIcon, + ExpandIcon, + ExportIcon, + FailureIcon, + FlagIcon, + FlagNoteIcon, + FlagInfoIcon, + FlagTipIcon, + FlagWarningIcon, + FlagErrorIcon, + FlagFixmeIcon, + FlagExpandIcon, + FlagOtionsIcon, + GaugeIcon, + GroupIcon, + IncludeIcon, + HelpIcon, + KioskIcon, + LeftIcon, + ListIcon, + LockIcon, + MarginIcon, + MeasurementsIcon, + MeasurementsSetIcon, + MenuIcon, + NoIcon, + OkIcon, + OptionsIcon, + PaperlessIcon, + PlusIcon, + PrintIcon, + ResetAllIcon, + ResetIcon, + RightIcon, + RocketIcon, + RotateIcon, + SaIcon, + SaveIcon, + SaveAsIcon, + ScaleIcon, + SettingsIcon, + SpinnerIcon, + SuccessIcon, + TipIcon, + TrashIcon, + UiIcon, + UndoIcon, + UnitsIcon, + UpIcon, + UploadIcon, + UxIcon, + XrayIcon, + ViewDraftIcon, + ViewMeasurementsIcon, + ViewTestIcon, + ViewTimingIcon, + ViewPrintLayoutIcon, + ViewSaveIcon, + ViewExportIcon, + ViewEditSettingsIcon, + ViewLogsIcon, + ViewInspectIcon, + ViewDocsIcon, + ViewDesignsIcon, + ViewViewPickerIcon, + ViewUndosIcon, +} from './icons.mjs' +// Measurements Editor +import { MeasurementsEditor } from './measurements-editor.mjs' +// Zoomable pattern +import { ZoomablePattern, ZoomContextProvider } from './zoomable-pattern.mjs' +import { PatternLayout } from './pattern-layout.mjs' +// inputs +import { + FormControl, + ButtonFrame, + NumberInput, + StringInput, + ListInput, + MarkdownInput, + MeasurementInput, + ToggleInput, +} from './inputs.mjs' +// Views +import { DesignsView } from './designs-view.mjs' +import { DraftView } from './draft-view.mjs' +import { ErrorView } from './error-view.mjs' +import { MeasurementsView } from './measurements-view.mjs' +import { SaveView } from './save-view.mjs' +import { ViewPicker } from './view-picker.mjs' +import { UndoStep, UndoStepTimeAgo, UndosView } from './undos-view.mjs' +// Pattern +import { Pattern } from '@freesewing/react-components/pattern' +// Menus +import { DraftMenu } from './menus/draft-menu.mjs' +import { CoreSettingsMenu, CoreSetting } from './menus/core-settings-menu.mjs' +import { DesignOptionsMenu, DesignOption } from './menus/design-options-menu.mjs' +import { UiPreferencesMenu, UiPreference } from './menus/ui-preferences-menu.mjs' +import { MenuItem, MenuItemGroup, MenuItemTitle } from './menus/containers.mjs' +import { + MenuBoolInput, + MenuConstantInput, + MenuDegInput, + MenuEditOption, + MenuListInput, + MenuListToggle, + MenuMmInput, + //MenuNumberInput, + MenuUxSettingInput, + MenuOnlySettingInput, + MenuPctInput, + MenuSliderInput, +} from './menus/shared-inputs.mjs' +import { + MenuBoolValue, + MenuConstantOptionValue, + MenuCountOptionValue, + MenuDegOptionValue, + MenuHighlightValue, + MenuListOptionValue, + MenuListValue, + MenuMmOptionValue, + MenuMmValue, + MenuOnlySettingValue, + MenuPctOptionValue, + MenuScaleSettingValue, + MenuShowValue, +} from './menus/shared-values.mjs' +import { + HeaderMenu, + HeaderMenuAllViews, + HeaderMenuDraftView, + HeaderMenuButton, + HeaderMenuDropdown, + HeaderMenuDraftViewDesignOptions, + HeaderMenuDraftViewCoreSettings, + HeaderMenuDraftViewUiPreferences, + HeaderMenuDraftViewFlags, + HeaderMenuDraftViewIcons, + HeaderMenuIcon, + HeaderMenuIconSpacer, + HeaderMenuSaveIcons, + HeaderMenuUndoIcons, + HeaderMenuViewMenu, +} from './header-menu.mjs' +// Flags +import { Flag, FlagTypeIcon, FlagsAccordionTitle, FlagsAccordionEntries } from './flags.mjs' +// View Menu +import { + AsideViewMenu, + AsideViewMenuIcons, + AsideViewMenuButton, + AsideViewMenuSpacer, + ViewTypeIcon, +} from './aside-view-menu.mjs' +import { Null } from './null.mjs' +import { LargeScreenOnly } from './large-screen-only.mjs' +import { Tooltip } from './tooltip.mjs' +import { LoadingStatus } from './loading-status.mjs' +import { Spinner, Loading } from './spinner.mjs' +import { Tab, Tabs } from './tabs.mjs' +import { Markdown } from './markdown.mjs' +import { HtmlSpan } from './html-span.mjs' +/** + * This object holds all components that can be swizzled + */ +const defaultComponents = { + Accordion, + AuthWrapper, + AuthMessageWrapper, + BackIcon, + ContactSupport, + AuthRequired, + AccountInactive, + AccountDisabled, + AccountProhibited, + AccountStatusUnknown, + AnchorLink, + AsideViewMenu, + AsideViewMenuIcons, + AsideViewMenuButton, + AsideViewMenuSpacer, + RoleLacking, + ConsentLacking, + BaseAccordion, + BookmarkedSetPicker, + ButtonFrame, + CardLink, + CircleIcon, + CoreSetting, + CoreSettingsMenu, + CuratedMeasurementsSetIcon, + CuratedMeasurementsSetLineup, + CuratedSetPicker, + DesignOption, + DesignOptionsMenu, + DesignsView, + DraftMenu, + DraftView, + ErrorView, + SaveView, + Flag, + FlagsAccordionTitle, + FlagsAccordionEntries, + FlagTypeIcon, + FormControl, + HeaderMenu, + HeaderMenuAllViews, + HeaderMenuDraftView, + HeaderMenuDraftViewDesignOptions, + HeaderMenuDraftViewCoreSettings, + HeaderMenuDraftViewUiPreferences, + HeaderMenuDraftViewFlags, + HeaderMenuDraftViewIcons, + HeaderMenuButton, + HeaderMenuDropdown, + HeaderMenuIcon, + HeaderMenuIconSpacer, + HeaderMenuSaveIcons, + HeaderMenuUndoIcons, + HtmlSpan, + LargeScreenOnly, + Link, + ListInput, + Loading, + LoadingStatus, + Markdown, + MarkdownInput, + MeasurementInput, + MeasurementsSetCard, + MeasurementsView, + MeasurementsEditor, + MenuIcon, + NumberInput, + Null, + PageLink, + Pattern, + PatternLayout, + Popout, + StringInput, + SubAccordion, + Spinner, + SpinnerIcon, + Tab, + Tabs, + TemporaryLoader, + ToggleInput, + Tooltip, + UiPreferencesMenu, + UiPreference, + UndoStep, + UndoStepTimeAgo, + UndosView, + UserSetPicker, + Ux, + HeaderMenuViewMenu, + ViewPicker, + ViewTypeIcon, + WebLink, + ZoomablePattern, + ZoomContextProvider, + // icons + ApplyIcon, + BeakerIcon, + BookmarkIcon, + BoolNoIcon, + BoolYesIcon, + CloseIcon, + DesignIcon, + DetailIcon, + DocsIcon, + DownIcon, + EditIcon, + ExpandIcon, + ExportIcon, + FailureIcon, + FlagIcon, + FlagNoteIcon, + FlagInfoIcon, + FlagTipIcon, + FlagWarningIcon, + FlagErrorIcon, + FlagFixmeIcon, + FlagExpandIcon, + FlagOtionsIcon, + GaugeIcon, + GroupIcon, + HelpIcon, + IncludeIcon, + KioskIcon, + LeftIcon, + ListIcon, + LockIcon, + MarginIcon, + MeasurementsIcon, + MeasurementsSetIcon, + NoIcon, + OkIcon, + OptionsIcon, + PaperlessIcon, + PlusIcon, + PrintIcon, + ResetAllIcon, + ResetIcon, + RightIcon, + RocketIcon, + RotateIcon, + SaIcon, + SaveIcon, + SaveAsIcon, + ScaleIcon, + SettingsIcon, + SuccessIcon, + TipIcon, + TrashIcon, + UiIcon, + UndoIcon, + UnitsIcon, + UpIcon, + UploadIcon, + UxIcon, + XrayIcon, + ViewDraftIcon, + ViewMeasurementsIcon, + ViewTestIcon, + ViewTimingIcon, + ViewPrintLayoutIcon, + ViewSaveIcon, + ViewExportIcon, + ViewEditSettingsIcon, + ViewLogsIcon, + ViewInspectIcon, + ViewDocsIcon, + ViewDesignsIcon, + ViewViewPickerIcon, + ViewUndosIcon, + // menus + MenuItem, + MenuItemGroup, + MenuItemTitle, + MenuBoolInput, + MenuConstantInput, + MenuDegInput, + MenuEditOption, + MenuListInput, + MenuListToggle, + MenuMmInput, + //MenuNumberInput, + MenuUxSettingInput, + MenuOnlySettingInput, + MenuPctInput, + MenuSliderInput, + MenuBoolValue, + MenuConstantOptionValue, + MenuCountOptionValue, + MenuDegOptionValue, + MenuHighlightValue, + MenuListOptionValue, + MenuListValue, + MenuMmOptionValue, + MenuMmValue, + MenuOnlySettingValue, + MenuPctOptionValue, + MenuScaleSettingValue, + MenuShowValue, +} + +/* + * This method returns a component that can be swizzled + * So either the passed-in component, or the default one + */ +export const swizzleComponents = (components = {}, Swizzled) => { + /* + * We need to return all resulting components, swizzled or not + * So we create this object so we can pass that down + */ + const all = {} + for (let [name, Component] of Object.entries(defaultComponents)) { + all[name] = components[name] + ? (props) => components[name]({ Swizzled, ...props }) + : (props) => + } + + /* + * Return all components + */ + return all +} diff --git a/packages/react-components/src/editor/swizzle/components/inputs.mjs b/packages/react-components/src/editor/swizzle/components/inputs.mjs new file mode 100644 index 00000000000..9fa1b579575 --- /dev/null +++ b/packages/react-components/src/editor/swizzle/components/inputs.mjs @@ -0,0 +1,349 @@ +// Hooks +import { useState } from 'react' + +/* + * Helper component to wrap a form control with a label + */ +export const FormControl = ({ + label, // the (top-left) label + children, // Children to go inside the form control + docs = false, // Optional top-right label + labelBL = false, // Optional bottom-left label + labelBR = false, // Optional bottom-right label + forId = false, // ID of the for element we are wrapping + Swizzled, // Object holding swizzled code +}) => { + if (labelBR && !labelBL) labelBL = + + const topLabelChildren = ( + <> + {label} + {docs ? ( + + + + ) : null} + + ) + const bottomLabelChildren = ( + <> + {labelBL ? {labelBL} : null} + {labelBR ? {labelBR} : null} + + ) + + return ( +
    + {forId ? ( + + ) : ( +
    {topLabelChildren}
    + )} + {children} + {labelBL || labelBR ? ( + forId ? ( + + ) : ( +
    {bottomLabelChildren}
    + ) + ) : null} +
    + ) +} + +/* + * Helper method to wrap content in a button + */ +export const ButtonFrame = ({ + children, // Children of the button + onClick, // onClick handler + active, // Whether or not to render the button as active/selected + accordion = false, // Set this to true to not set a background color when active + dense = false, // Use less padding +}) => ( + +) + +/* + * Input for integers + */ +export const NumberInput = ({ + label, // Label to use + update, // onChange handler + valid, // Method that should return whether the value is valid or not + current, // The current value + original, // The original value + placeholder, // The placeholder text + docs = false, // Docs to load, if any + id = '', // An id to tie the input to the label + labelBL = false, // Bottom-Left label + labelBR = false, // Bottom-Right label + max = 0, + min = 220, + step = 1, + Swizzled, // Object holding swizzled code +}) => ( + + update(evt.target.value)} + className={`input w-full input-bordered ${ + current === original ? 'input-secondary' : valid(current) ? 'input-success' : 'input-error' + }`} + {...{ max, min, step }} + /> + +) + +/* + * Input for strings + */ +export const StringInput = ({ + label, // Label to use + update, // onChange handler + valid, // Method that should return whether the value is valid or not + current, // The current value + original, // The original value + placeholder, // The placeholder text + docs = false, // Docs to load, if any + id = '', // An id to tie the input to the label + labelBL = false, // Bottom-Left label + labelBR = false, // Bottom-Right label + Swizzled, // Object holding swizzled code +}) => ( + + update(evt.target.value)} + className={`input w-full input-bordered ${ + current === original ? 'input-secondary' : valid(current) ? 'input-success' : 'input-error' + }`} + /> + +) + +/* + * Input for a list of things to pick from + */ +export const ListInput = ({ + update, // the onChange handler + label, // The label + list, // The list of items to present { val, label, desc } + current, // The (value of the) current item + docs = false, // Docs to load, if any + Swizzled, // Object holding swizzled code +}) => ( + + {list.map((item, i) => ( + update(item.val)} + > +
    +
    {item.label}
    + {item.desc ? ( +
    + {item.desc} +
    + ) : null} +
    +
    + ))} +
    +) + +/* + * Input for markdown content + */ +export const MarkdownInput = ({ + label, // The label + current, // The current value (markdown) + update, // The onChange handler + placeholder, // The placeholder content + docs = false, // Docs to load, if any + id = '', // An id to tie the input to the label + labelBL = false, // Bottom-Left label + labelBR = false, // Bottom-Right label + Swizzled, // Swizzled code +}) => ( + + + +
    +