feat[react]: Linting with eslint 9
This commit is contained in:
parent
14eab04d5b
commit
f69093b0dc
99 changed files with 1260 additions and 956 deletions
24
eslint.config.mjs
Normal file
24
eslint.config.mjs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import pluginReact from 'eslint-plugin-react'
|
||||||
|
import json from '@eslint/json'
|
||||||
|
import markdown from '@eslint/markdown'
|
||||||
|
import css from '@eslint/css'
|
||||||
|
import { defineConfig } from 'eslint/config'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{ files: ['**/*.{js,mjs,cjs,jsx}'], plugins: { js }, extends: ['js/recommended'] },
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,mjs,cjs,jsx}'],
|
||||||
|
languageOptions: { globals: { ...globals.browser, ...globals.node } },
|
||||||
|
},
|
||||||
|
pluginReact.configs.flat.recommended,
|
||||||
|
{ files: ['**/*.json'], plugins: { json }, language: 'json/json', extends: ['json/recommended'] },
|
||||||
|
{
|
||||||
|
files: ['**/*.md'],
|
||||||
|
plugins: { markdown },
|
||||||
|
language: 'markdown/commonmark',
|
||||||
|
extends: ['markdown/recommended'],
|
||||||
|
},
|
||||||
|
{ files: ['**/*.css'], plugins: { css }, language: 'css/css', extends: ['css/recommended'] },
|
||||||
|
])
|
1050
package-lock.json
generated
1050
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -61,17 +61,23 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^19.0.3",
|
"@commitlint/cli": "^19.0.3",
|
||||||
"@commitlint/config-conventional": "^19.0.3",
|
"@commitlint/config-conventional": "^19.0.3",
|
||||||
|
"@eslint/css": "^0.8.1",
|
||||||
|
"@eslint/js": "^9.27.0",
|
||||||
|
"@eslint/json": "^0.12.0",
|
||||||
|
"@eslint/markdown": "^6.4.0",
|
||||||
"@nx/eslint": "20.2.1",
|
"@nx/eslint": "20.2.1",
|
||||||
"all-contributors-cli": "^6.26.1",
|
"all-contributors-cli": "^6.26.1",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^8.23.1",
|
"eslint": "^9.27.0",
|
||||||
"eslint-plugin-jsonc": "^2.4.0",
|
"eslint-plugin-jsonc": "^2.4.0",
|
||||||
"eslint-plugin-markdown": "^5.0.0",
|
"eslint-plugin-markdown": "^5.0.0",
|
||||||
"eslint-plugin-mongo": "^1.0.5",
|
"eslint-plugin-mongo": "^1.0.5",
|
||||||
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-yaml": "^0.5.0",
|
"eslint-plugin-yaml": "^0.5.0",
|
||||||
"execa": "^9.3.1",
|
"execa": "^9.3.1",
|
||||||
|
"globals": "^16.2.0",
|
||||||
"husky": "^9.0.10",
|
"husky": "^9.0.10",
|
||||||
"js-yaml": "^4.0.0",
|
"js-yaml": "^4.0.0",
|
||||||
"lerna": "^8.0.0",
|
"lerna": "^8.0.0",
|
||||||
|
|
|
@ -1,49 +1,34 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { DateTime } from 'luxon'
|
import { DateTime } from 'luxon'
|
||||||
import orderBy from 'lodash/orderBy.js'
|
import orderBy from 'lodash/orderBy.js'
|
||||||
import { capitalize, shortDate } from '@freesewing/utils'
|
import { shortDate } from '@freesewing/utils'
|
||||||
import { apikeyLevels } from '@freesewing/config'
|
import { apikeyLevels } from '@freesewing/config'
|
||||||
// Context
|
// Context
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useEffect, useContext } from 'react'
|
import React, { useState, useEffect, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { TableWrapper } from '@freesewing/react/components/Table'
|
import { TableWrapper } from '@freesewing/react/components/Table'
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import {
|
import { PlusIcon, RightIcon, TrashIcon } from '@freesewing/react/components/Icon'
|
||||||
BoolNoIcon,
|
|
||||||
BoolYesIcon,
|
|
||||||
PlusIcon,
|
|
||||||
RightIcon,
|
|
||||||
TrashIcon,
|
|
||||||
} from '@freesewing/react/components/Icon'
|
|
||||||
import { Uuid } from '@freesewing/react/components/Uuid'
|
import { Uuid } from '@freesewing/react/components/Uuid'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
import { NumberCircle } from '@freesewing/react/components/Number'
|
import { NumberCircle } from '@freesewing/react/components/Number'
|
||||||
import { StringInput, Fieldset, ListInput } from '@freesewing/react/components/Input'
|
import { StringInput, Fieldset, ListInput } from '@freesewing/react/components/Input'
|
||||||
import { DisplayRow } from './shared.mjs'
|
|
||||||
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
||||||
import { TimeAgo, TimeToGo } from '@freesewing/react/components/Time'
|
import { TimeAgo, TimeToGo } from '@freesewing/react/components/Time'
|
||||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||||
|
|
||||||
const t = (input) => {
|
|
||||||
console.log('t called', input)
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
id: 'Key',
|
id: 'Key',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
calls: 'Calls',
|
calls: 'Calls',
|
||||||
level: 'Level',
|
level: 'Level',
|
||||||
level: 'Level',
|
|
||||||
createdAt: 'Created',
|
createdAt: 'Created',
|
||||||
expiresAt: 'Expires',
|
expiresAt: 'Expires',
|
||||||
}
|
}
|
||||||
|
@ -242,13 +227,6 @@ const NewApikey = ({ onCreate = false }) => {
|
||||||
} else setLoadingStatus([true, 'An error occured. Please report this', true, false])
|
} else setLoadingStatus([true, 'An error occured. Please report this', true, false])
|
||||||
}
|
}
|
||||||
|
|
||||||
const clear = () => {
|
|
||||||
setApikey(false)
|
|
||||||
setGenerate(false)
|
|
||||||
setName('')
|
|
||||||
setLevel(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tw:w-full">
|
<div className="tw:w-full">
|
||||||
<h2>New API key {apikey ? `: ${apikey.name}` : ''}</h2>
|
<h2>New API key {apikey ? `: ${apikey.name}` : ''}</h2>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
// Components
|
// Components
|
||||||
import { BookmarkIcon, LeftIcon, PlusIcon, TrashIcon } from '@freesewing/react/components/Icon'
|
import { BookmarkIcon, PlusIcon, TrashIcon } from '@freesewing/react/components/Icon'
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
import { StringInput } from '@freesewing/react/components/Input'
|
import { StringInput } from '@freesewing/react/components/Input'
|
||||||
|
@ -33,7 +33,7 @@ const types = {
|
||||||
export const Bookmarks = () => {
|
export const Bookmarks = () => {
|
||||||
// Hooks & Context
|
// Hooks & Context
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
const { setModal, clearModal } = useContext(ModalContext)
|
const { setModal } = useContext(ModalContext)
|
||||||
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
|
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
@ -194,7 +194,7 @@ const NewBookmark = ({ onCreated = false }) => {
|
||||||
// This method will create the bookmark
|
// This method will create the bookmark
|
||||||
const createBookmark = async () => {
|
const createBookmark = async () => {
|
||||||
setLoadingStatus([true, 'Processing update'])
|
setLoadingStatus([true, 'Processing update'])
|
||||||
const [status, body] = await backend.createBookmark({
|
const [status] = await backend.createBookmark({
|
||||||
title,
|
title,
|
||||||
url,
|
url,
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { welcomeSteps } from './shared.mjs'
|
import { welcomeSteps } from './shared.mjs'
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { NoIcon, OkIcon, RightIcon } from '@freesewing/react/components/Icon'
|
||||||
import { NoIcon, OkIcon, SaveIcon, RightIcon } from '@freesewing/react/components/Icon'
|
|
||||||
import { ListInput } from '@freesewing/react/components/Input'
|
import { ListInput } from '@freesewing/react/components/Input'
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
import { WelcomeIcons } from './shared.mjs'
|
import { WelcomeIcons } from './shared.mjs'
|
||||||
|
|
|
@ -1,36 +1,15 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { welcomeSteps } from './shared.mjs'
|
import { navigate } from '@freesewing/utils'
|
||||||
import { linkClasses, navigate } from '@freesewing/utils'
|
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { NoIcon, OkIcon, SaveIcon } from '@freesewing/react/components/Icon'
|
|
||||||
import { ListInput } from '@freesewing/react/components/Input'
|
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
|
|
||||||
const strings = {
|
|
||||||
yes: {
|
|
||||||
title: 'Yes, in case it may help me',
|
|
||||||
desc:
|
|
||||||
'Allowing us to compare your measurments to a baseline or others measurements sets ' +
|
|
||||||
'allows us to detect potential problems in your measurements or patterns.',
|
|
||||||
},
|
|
||||||
no: {
|
|
||||||
title: 'No, never compare',
|
|
||||||
desc:
|
|
||||||
'We get it, comparison is the thief of joy. Just be aware that this limits our ability ' +
|
|
||||||
'to warn you about potential problems in your measurements sets or patterns.',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component to manage the user's consent setting
|
* A component to manage the user's consent setting
|
||||||
*
|
*
|
||||||
|
@ -79,7 +58,7 @@ export const Consent = ({ signUp = false, Link = false, title = false }) => {
|
||||||
// Helper method to remove the account
|
// Helper method to remove the account
|
||||||
const removeAccount = async () => {
|
const removeAccount = async () => {
|
||||||
setLoadingStatus([true, 'One moment please'])
|
setLoadingStatus([true, 'One moment please'])
|
||||||
const [status, body] = await backend.removeAccount()
|
const [status] = await backend.removeAccount()
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
setLoadingStatus([true, 'All done, farewell', true, true])
|
setLoadingStatus([true, 'All done, farewell', true, true])
|
||||||
setToken(null)
|
setToken(null)
|
||||||
|
|
|
@ -1,39 +1,16 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { welcomeSteps } from './shared.mjs'
|
import { welcomeSteps } from './shared.mjs'
|
||||||
import { controlDesc } from '@freesewing/config'
|
import { controlDesc } from '@freesewing/config'
|
||||||
|
|
||||||
// Context
|
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
|
||||||
import { useControl } from '@freesewing/react/hooks/useControl'
|
import { useControl } from '@freesewing/react/hooks/useControl'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { RightIcon } from '@freesewing/react/components/Icon'
|
||||||
import { RightIcon, NoIcon, OkIcon, SaveIcon } from '@freesewing/react/components/Icon'
|
|
||||||
import { ListInput } from '@freesewing/react/components/Input'
|
import { ListInput } from '@freesewing/react/components/Input'
|
||||||
import { ControlScore } from '@freesewing/react/components/Control'
|
import { ControlScore } from '@freesewing/react/components/Control'
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
import { WelcomeIcons } from './shared.mjs'
|
import { WelcomeIcons } from './shared.mjs'
|
||||||
|
|
||||||
const strings = {
|
|
||||||
1: {
|
|
||||||
title: 'Keep it as simple as possible',
|
|
||||||
desc:
|
|
||||||
'Allowing us to compare your measurments to a baseline or others measurements sets ' +
|
|
||||||
'allows us to detect potential problems in your measurements or patterns.',
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
title: 'No, never compare',
|
|
||||||
desc:
|
|
||||||
'We get it, comparison is the thief of joy. Just be aware that this limits our ability ' +
|
|
||||||
'to warn you about potential problems in your measurements sets or patterns.',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component to manage the user's control/UX setting
|
* A component to manage the user's control/UX setting
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { welcomeSteps } from './shared.mjs'
|
import { validateEmail, validateTld, getSearchParam, navigate } from '@freesewing/utils'
|
||||||
import { validateEmail, validateTld, getSearchParam } from '@freesewing/utils'
|
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { SaveIcon } from '@freesewing/react/components/Icon'
|
import { SaveIcon } from '@freesewing/react/components/Icon'
|
||||||
|
@ -22,11 +18,10 @@ import { Spinner } from '@freesewing/react/components/Spinner'
|
||||||
*
|
*
|
||||||
* @component
|
* @component
|
||||||
* @param {object} props - All component props
|
* @param {object} props - All component props
|
||||||
* @param {bool} [props.welcome = false] - Set to true to render the welcome/onboarding view
|
|
||||||
* @param {React.Component} props.Link - A framework specific Link component for client-side routing
|
* @param {React.Component} props.Link - A framework specific Link component for client-side routing
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const Email = ({ welcome = false, Link = false }) => {
|
export const Email = ({ Link = false }) => {
|
||||||
if (!Link) Link = WebLink
|
if (!Link) Link = WebLink
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
|
@ -104,7 +99,7 @@ export const EmailChangeConfirmation = ({ onSuccess = false }) => {
|
||||||
const [check, setCheck] = useState()
|
const [check, setCheck] = useState()
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
const { setAccount, setToken } = useAccount()
|
const { setAccount } = useAccount()
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
|
@ -134,7 +129,7 @@ export const EmailChangeConfirmation = ({ onSuccess = false }) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// If it works, store account, which runs the onSuccess handler
|
// If it works, store account, which runs the onSuccess handler
|
||||||
if (body.result === 'success' && body.account) return storeAccount(body)
|
if (status === 200 && body.result === 'success' && body.account) return storeAccount(body)
|
||||||
// If we get here, we're not sure what's wrong
|
// If we get here, we're not sure what's wrong
|
||||||
if (body.error) return setError(body.error)
|
if (body.error) return setError(body.error)
|
||||||
return setError(true)
|
return setError(true)
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { DownloadIcon } from '@freesewing/react/components/Icon'
|
import { DownloadIcon } from '@freesewing/react/components/Icon'
|
||||||
|
|
|
@ -1,24 +1,16 @@
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState } from 'react'
|
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
|
|
||||||
// Components
|
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component to display the user's ID
|
* A component to display the current user's ID
|
||||||
*
|
*
|
||||||
* @component
|
* @component
|
||||||
* @param {object} props - All component props
|
* @param {object} props - All component props
|
||||||
* @param {React.Component} props.Link - A framework specific Link component for client-side routing
|
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const UserId = ({ Link = false }) => {
|
export const UserId = () => {
|
||||||
if (!Link) Link = WebLink
|
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
const { account } = useAccount()
|
const { account } = useAccount()
|
||||||
const [id, setId] = useState(account.id)
|
|
||||||
|
|
||||||
return id || null
|
return account.id || null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
|
// Dependencies
|
||||||
|
import yaml from 'js-yaml'
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
|
||||||
import { SaveIcon } from '@freesewing/react/components/Icon'
|
|
||||||
import { FileInput } from '@freesewing/react/components/Input'
|
import { FileInput } from '@freesewing/react/components/Input'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
import { Yaml } from '@freesewing/react/components/Yaml'
|
import { Yaml } from '@freesewing/react/components/Yaml'
|
||||||
|
@ -41,7 +39,7 @@ export const ImportSet = () => {
|
||||||
if (set.measurements || set.measies) {
|
if (set.measurements || set.measies) {
|
||||||
const name = set.name || 'J. Doe'
|
const name = set.name || 'J. Doe'
|
||||||
setLoadingStatus([true, `Importing ${name}`])
|
setLoadingStatus([true, `Importing ${name}`])
|
||||||
const [status, body] = await backend.createSet({
|
const [status] = await backend.createSet({
|
||||||
name: set.name || 'J. Doe',
|
name: set.name || 'J. Doe',
|
||||||
units: set.units || 'metric',
|
units: set.units || 'metric',
|
||||||
notes: set.notes || '',
|
notes: set.notes || '',
|
||||||
|
|
|
@ -93,8 +93,6 @@ const titles = {
|
||||||
|
|
||||||
const YesNo = ({ check }) => (check ? <BoolYesIcon /> : <BoolNoIcon />)
|
const YesNo = ({ check }) => (check ? <BoolYesIcon /> : <BoolNoIcon />)
|
||||||
|
|
||||||
const t = (input) => input
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component to manage the user's Instagram handle in their account data
|
* A component to manage the user's Instagram handle in their account data
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { horFlexClasses } from '@freesewing/utils'
|
import { horFlexClasses } from '@freesewing/utils'
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
|
||||||
import { NoIcon, LockIcon } from '@freesewing/react/components/Icon'
|
import { NoIcon, LockIcon } from '@freesewing/react/components/Icon'
|
||||||
import { PasswordInput } from '@freesewing/react/components/Input'
|
import { PasswordInput } from '@freesewing/react/components/Input'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
import { NumberCircle } from '@freesewing/react/components/Number'
|
|
||||||
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { welcomeSteps } from './shared.mjs'
|
import { welcomeSteps } from './shared.mjs'
|
||||||
import { linkClasses } from '@freesewing/utils'
|
import { linkClasses } from '@freesewing/utils'
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { NoIcon, OkIcon, SaveIcon, RightIcon } from '@freesewing/react/components/Icon'
|
import { NoIcon, OkIcon, RightIcon } from '@freesewing/react/components/Icon'
|
||||||
import { ListInput } from '@freesewing/react/components/Input'
|
import { ListInput } from '@freesewing/react/components/Input'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
// Dependencies
|
|
||||||
import { welcomeSteps } from './shared.mjs'
|
|
||||||
import { horFlexClasses } from '@freesewing/utils'
|
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { RightIcon, SaveIcon } from '@freesewing/react/components/Icon'
|
import { RightIcon, SaveIcon } from '@freesewing/react/components/Icon'
|
||||||
|
@ -25,7 +19,7 @@ import { Popout } from '@freesewing/react/components/Popout'
|
||||||
* @param {React.Component} props.Link - A framework specific Link component for client-side routing
|
* @param {React.Component} props.Link - A framework specific Link component for client-side routing
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const Password = ({ welcome = false, Link = false }) => {
|
export const Password = ({ Link = false }) => {
|
||||||
if (!Link) Link = WebLink
|
if (!Link) Link = WebLink
|
||||||
// Hooks
|
// Hooks
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
|
|
|
@ -1,25 +1,13 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import orderBy from 'lodash/orderBy.js'
|
import { cloudflareImageUrl, horFlexClasses, patternUrlFromState } from '@freesewing/utils'
|
||||||
import {
|
|
||||||
cloudflareImageUrl,
|
|
||||||
capitalize,
|
|
||||||
shortDate,
|
|
||||||
horFlexClasses,
|
|
||||||
newPatternUrl,
|
|
||||||
patternUrlFromState,
|
|
||||||
} from '@freesewing/utils'
|
|
||||||
import { urls, control as controlConfig } from '@freesewing/config'
|
import { urls, control as controlConfig } from '@freesewing/config'
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useEffect, useContext } from 'react'
|
import React, { useState, useEffect, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Markdown from 'react-markdown'
|
import Markdown from 'react-markdown'
|
||||||
import {
|
import {
|
||||||
|
@ -28,7 +16,7 @@ import {
|
||||||
PassiveImageInput,
|
PassiveImageInput,
|
||||||
ListInput,
|
ListInput,
|
||||||
} from '@freesewing/react/components/Input'
|
} from '@freesewing/react/components/Input'
|
||||||
import { Link as WebLink, AnchorLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import {
|
import {
|
||||||
BoolNoIcon,
|
BoolNoIcon,
|
||||||
BoolYesIcon,
|
BoolYesIcon,
|
||||||
|
@ -42,7 +30,6 @@ import {
|
||||||
ResetIcon,
|
ResetIcon,
|
||||||
UploadIcon,
|
UploadIcon,
|
||||||
} from '@freesewing/react/components/Icon'
|
} from '@freesewing/react/components/Icon'
|
||||||
import { DisplayRow } from './shared.mjs'
|
|
||||||
import { TimeAgo } from '@freesewing/react/components/Time'
|
import { TimeAgo } from '@freesewing/react/components/Time'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
|
@ -348,16 +335,7 @@ const BadgeLink = ({ label, href }) => (
|
||||||
/**
|
/**
|
||||||
* Helper component to show the pattern title, image, and various buttons
|
* Helper component to show the pattern title, image, and various buttons
|
||||||
*/
|
*/
|
||||||
const PatternHeader = ({
|
const PatternHeader = ({ pattern, Link, account, setModal, setEdit, togglePublic, clone }) => (
|
||||||
pattern,
|
|
||||||
Link,
|
|
||||||
account,
|
|
||||||
setModal,
|
|
||||||
setEdit,
|
|
||||||
togglePublic,
|
|
||||||
save,
|
|
||||||
clone,
|
|
||||||
}) => (
|
|
||||||
<>
|
<>
|
||||||
<h2>{pattern.name}</h2>
|
<h2>{pattern.name}</h2>
|
||||||
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-2 tw:text-sm tw:items-center tw:mb-2">
|
<div className="tw:flex tw:flex-row tw:flex-wrap tw:gap-2 tw:text-sm tw:items-center tw:mb-2">
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import orderBy from 'lodash/orderBy.js'
|
import orderBy from 'lodash/orderBy.js'
|
||||||
import { capitalize, shortDate } from '@freesewing/utils'
|
import { capitalize, shortDate } from '@freesewing/utils'
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useEffect, useContext } from 'react'
|
import React, { useState, useEffect, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { TableWrapper } from '@freesewing/react/components/Table'
|
import { TableWrapper } from '@freesewing/react/components/Table'
|
||||||
import { PatternCard } from '@freesewing/react/components/Account'
|
import { PatternCard } from '@freesewing/react/components/Account'
|
||||||
|
@ -23,11 +19,6 @@ import {
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
} from '@freesewing/react/components/Icon'
|
} from '@freesewing/react/components/Icon'
|
||||||
|
|
||||||
const t = (input) => {
|
|
||||||
console.log('t called', input)
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component to display and manage the list of patterns in the user's account
|
* A component to display and manage the list of patterns in the user's account
|
||||||
*
|
*
|
||||||
|
@ -97,13 +88,15 @@ export const Patterns = ({ Link = false }) => {
|
||||||
onClick={removeSelectedPatterns}
|
onClick={removeSelectedPatterns}
|
||||||
disabled={count < 1}
|
disabled={count < 1}
|
||||||
>
|
>
|
||||||
<TrashIcon /> {count} {t('patterns')}
|
<TrashIcon /> {count} Patterns
|
||||||
</button>
|
</button>
|
||||||
<Link
|
<Link
|
||||||
className="tw:daisy-btn tw:daisy-btn-primary tw:capitalize tw:w-full tw:md:w-auto tw:hover:text-primary-content"
|
className="tw:daisy-btn tw:daisy-btn-primary tw:capitalize tw:w-full tw:md:w-auto tw:hover:text-primary-content"
|
||||||
href="/editor/"
|
href="/editor/"
|
||||||
>
|
>
|
||||||
<span className="tw:text-primary-content"><PlusIcon /></span>
|
<span className="tw:text-primary-content">
|
||||||
|
<PlusIcon />
|
||||||
|
</span>
|
||||||
<span className="tw:text-primary-content">Create a new pattern</span>
|
<span className="tw:text-primary-content">Create a new pattern</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
|
||||||
import { ReloadIcon } from '@freesewing/react/components/Icon'
|
import { ReloadIcon } from '@freesewing/react/components/Icon'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,16 +3,12 @@ import { navigate } from '@freesewing/utils'
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
|
||||||
import { BackIcon as ExitIcon, TrashIcon } from '@freesewing/react/components/Icon'
|
import { BackIcon as ExitIcon, TrashIcon } from '@freesewing/react/components/Icon'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,13 @@ import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { BackIcon, NoIcon } from '@freesewing/react/components/Icon'
|
import { BackIcon, NoIcon } from '@freesewing/react/components/Icon'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ export const Restrict = ({ Link = false }) => {
|
||||||
// Helper method to restrict the account
|
// Helper method to restrict the account
|
||||||
const restrictAccount = async () => {
|
const restrictAccount = async () => {
|
||||||
setLoadingStatus([true, 'Talking to the backend'])
|
setLoadingStatus([true, 'Talking to the backend'])
|
||||||
const [status, body] = await backend.restrictAccount()
|
const [status] = await backend.restrictAccount()
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
setLoadingStatus([true, 'Done. Consider yourself restrcited.', true, true])
|
setLoadingStatus([true, 'Done. Consider yourself restrcited.', true, true])
|
||||||
signOut()
|
signOut()
|
||||||
|
|
|
@ -58,11 +58,6 @@ import { bundlePatternTranslations, draft, flattenFlags } from '../Editor/lib/in
|
||||||
import { Bonny } from '@freesewing/bonny'
|
import { Bonny } from '@freesewing/bonny'
|
||||||
import { MiniNote, MiniTip } from '../Mini/index.mjs'
|
import { MiniNote, MiniTip } from '../Mini/index.mjs'
|
||||||
|
|
||||||
const t = (input) => {
|
|
||||||
console.log('t called', input)
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to show an individual measurements set
|
* Component to show an individual measurements set
|
||||||
*
|
*
|
||||||
|
@ -650,8 +645,8 @@ const SuggestCset = ({ mset, Link }) => {
|
||||||
const [name, setName] = useState('')
|
const [name, setName] = useState('')
|
||||||
const [notes, setNotes] = useState('')
|
const [notes, setNotes] = useState('')
|
||||||
const [submission, setSubmission] = useState(false)
|
const [submission, setSubmission] = useState(false)
|
||||||
|
// Context
|
||||||
console.log(mset)
|
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
|
@ -803,7 +798,7 @@ const RenderedCset = ({ mset, imperial }) => {
|
||||||
<strong>{formatMm(pattern.parts[0].front.points.head.y * -1, imperial)}</strong> high.
|
<strong>{formatMm(pattern.parts[0].front.points.head.y * -1, imperial)}</strong> high.
|
||||||
</p>
|
</p>
|
||||||
<p>Here is what the automated analysis found:</p>
|
<p>Here is what the automated analysis found:</p>
|
||||||
{Object.entries(flattenFlags(flags)).map(([key, flag], i) => {
|
{Object.entries(flattenFlags(flags)).map(([key, flag]) => {
|
||||||
const desc = strings[flag.desc] || flag.desc
|
const desc = strings[flag.desc] || flag.desc
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -844,7 +839,7 @@ const RenderedCset = ({ mset, imperial }) => {
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
This preview is an <strong>approximation</strong>, not an exact representation. Bodies
|
This preview is an <strong>approximation</strong>, not an exact representation. Bodies
|
||||||
have many variations that can't be captured with just a few measurements. We are
|
have many variations that can't be captured with just a few measurements. We are
|
||||||
missing some information, like how weight is distributed or your posture.
|
missing some information, like how weight is distributed or your posture.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -881,7 +876,7 @@ export const NewSet = () => {
|
||||||
// Hooks
|
// Hooks
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
const { account } = useAccount()
|
const { account } = useAccount()
|
||||||
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
|
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const [name, setName] = useState('')
|
const [name, setName] = useState('')
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { measurements } from '@freesewing/config'
|
|
||||||
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
||||||
import { requiredMeasurements as designMeasurements } from '@freesewing/collection'
|
import { requiredMeasurements as designMeasurements } from '@freesewing/collection'
|
||||||
import { cloudflareImageUrl, capitalize, hasRequiredMeasurements } from '@freesewing/utils'
|
import { cloudflareImageUrl, hasRequiredMeasurements } from '@freesewing/utils'
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { welcomeSteps } from './shared.mjs'
|
import { welcomeSteps } from './shared.mjs'
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { RightIcon } from '@freesewing/react/components/Icon'
|
||||||
import { SaveIcon, RightIcon } from '@freesewing/react/components/Icon'
|
|
||||||
import { ListInput } from '@freesewing/react/components/Input'
|
import { ListInput } from '@freesewing/react/components/Input'
|
||||||
import { NumberCircle } from '@freesewing/react/components/Number'
|
import { NumberCircle } from '@freesewing/react/components/Number'
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
|
|
|
@ -57,10 +57,6 @@ export const Username = ({ welcome = false, Link = false }) => {
|
||||||
? '/welcome/' + welcomeSteps[account.control][5]
|
? '/welcome/' + welcomeSteps[account.control][5]
|
||||||
: '/docs/about/guide'
|
: '/docs/about/guide'
|
||||||
|
|
||||||
let btnClasses = 'daisy-btn mt-4 capitalize '
|
|
||||||
if (welcome) btnClasses += 'w-64 daisy-btn-secondary'
|
|
||||||
else btnClasses += 'w-full daisy-btn-primary'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tw:w-full">
|
<div className="tw:w-full">
|
||||||
<StringInput
|
<StringInput
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
import { AccountStatus } from './Status.mjs'
|
import { AccountStatus } from './Status.mjs'
|
||||||
import { Apikeys } from './Apikeys.mjs'
|
import { Apikeys } from './Apikeys.mjs'
|
||||||
import { Avatar } from './Avatar.mjs'
|
import { Avatar } from './Avatar.mjs'
|
||||||
|
|
|
@ -8,9 +8,6 @@ import {
|
||||||
CompareIcon,
|
CompareIcon,
|
||||||
DocsIcon,
|
DocsIcon,
|
||||||
UserIcon,
|
UserIcon,
|
||||||
LeftIcon,
|
|
||||||
OkIcon,
|
|
||||||
NoIcon,
|
|
||||||
ShowcaseIcon,
|
ShowcaseIcon,
|
||||||
} from '@freesewing/react/components/Icon'
|
} from '@freesewing/react/components/Icon'
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { uiRoles as roles } from '@freesewing/config'
|
|
||||||
import { userAvatarUrl } from '@freesewing/utils'
|
import { userAvatarUrl } from '@freesewing/utils'
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
// Context
|
// Context
|
||||||
|
@ -14,7 +13,6 @@ import { Spinner } from '@freesewing/react/components/Spinner'
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { SearchIcon } from '@freesewing/react/components/Icon'
|
import { SearchIcon } from '@freesewing/react/components/Icon'
|
||||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||||
import { Markdown } from '@freesewing/react/components/Markdown'
|
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
import { AccountStatus, UserRole } from '@freesewing/react/components/Account'
|
import { AccountStatus, UserRole } from '@freesewing/react/components/Account'
|
||||||
|
|
||||||
|
@ -313,95 +311,3 @@ const ImpersonateButton = ({ userId }) => {
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Row = ({ title, val }) => (
|
|
||||||
<tr className="py-1">
|
|
||||||
<td className="text-sm px-2 text-right font-bold">{title}</td>
|
|
||||||
<td className="text-sm">{val}</td>
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
|
|
||||||
const ManageUser = ({ userId }) => {
|
|
||||||
// Hooks
|
|
||||||
const backend = useBackend()
|
|
||||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
|
||||||
const { account } = useAccount()
|
|
||||||
const { role } = account
|
|
||||||
|
|
||||||
// State
|
|
||||||
const [user, setUser] = useState({})
|
|
||||||
const [patterns, setPatterns] = useState({})
|
|
||||||
const [sets, setSets] = useState({})
|
|
||||||
|
|
||||||
// Effect
|
|
||||||
useEffect(() => {
|
|
||||||
const loadUser = async () => {
|
|
||||||
const result = await backend.adminLoadUser(userId)
|
|
||||||
if (result.success) {
|
|
||||||
setUser(result.data.user)
|
|
||||||
setPatterns(result.data.patterns)
|
|
||||||
setSets(result.data.sets)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadUser()
|
|
||||||
}, [userId])
|
|
||||||
|
|
||||||
const updateUser = async (data) => {
|
|
||||||
setLoadingStatus([true, 'status:contactingBackend'])
|
|
||||||
const result = await backend.adminUpdateUser({ id: userId, data })
|
|
||||||
if (result.success) {
|
|
||||||
setLoadingStatus([true, 'status:settingsSaved', true, true])
|
|
||||||
setUser(result.data.user)
|
|
||||||
} else setLoadingStatus([true, 'status:backendError', true, false])
|
|
||||||
}
|
|
||||||
|
|
||||||
return user.id ? (
|
|
||||||
<div className="my-8">
|
|
||||||
<ShowUser
|
|
||||||
user={user}
|
|
||||||
button={role === 'admin' ? <ImpersonateButton userId={user.id} /> : null}
|
|
||||||
/>
|
|
||||||
{role === 'admin' ? (
|
|
||||||
<div className="flex flex-row flex-wrap gap-2 my-2">
|
|
||||||
{roles.map((role) => (
|
|
||||||
<button
|
|
||||||
key={role}
|
|
||||||
className="btn btn-primary btn-outline btn-sm"
|
|
||||||
onClick={() => updateUser({ role })}
|
|
||||||
disabled={role === user.role}
|
|
||||||
>
|
|
||||||
Assign {role} role
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className="flex flex-row flex-wrap gap-2 my-2 mb-4">
|
|
||||||
{user.mfaEnabled && (
|
|
||||||
<button
|
|
||||||
className="btn btn-warning btn-outline btn-sm"
|
|
||||||
onClick={() => updateUser({ mfaEnabled: false })}
|
|
||||||
>
|
|
||||||
Disable MFA
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{Object.keys(freeSewingConfig.statuses).map((status) => (
|
|
||||||
<button
|
|
||||||
key={status}
|
|
||||||
className="btn btn-warning btn-outline btn-sm"
|
|
||||||
onClick={() => updateUser({ status })}
|
|
||||||
disabled={Number(status) === user.status}
|
|
||||||
>
|
|
||||||
Set {freeSewingConfig.statuses[status].name.toUpperCase()} status
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<Tabs tabs="Account, Patterns, Sets">
|
|
||||||
<Tab tabId="Account">{user.id ? <Json js={user} /> : null}</Tab>
|
|
||||||
<Tab tabId="Patterns">{patterns ? <Json js={patterns} /> : null}</Tab>
|
|
||||||
<Tab id="Sets">{sets ? <Json js={sets} /> : null}</Tab>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Loading />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
import React, { useContext, useState } from 'react'
|
import React, { useContext, useState } from 'react'
|
||||||
import { copyToClipboard } from '@freesewing/utils'
|
import { copyToClipboard } from '@freesewing/utils'
|
||||||
import ReactDOMServer from 'react-dom/server'
|
|
||||||
import { CopyIcon, OkIcon } from '@freesewing/react/components/Icon'
|
import { CopyIcon, OkIcon } from '@freesewing/react/components/Icon'
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
const strip = (html) =>
|
|
||||||
typeof DOMParser === 'undefined'
|
|
||||||
? html
|
|
||||||
: new DOMParser().parseFromString(html, 'text/html').body.textContent || ''
|
|
||||||
|
|
||||||
const handleCopied = (content, setCopied, setLoadingStatus, label) => {
|
const handleCopied = (content, setCopied, setLoadingStatus, label) => {
|
||||||
copyToClipboard(content)
|
copyToClipboard(content)
|
||||||
setCopied(true)
|
setCopied(true)
|
||||||
|
@ -55,7 +49,7 @@ export const CopyToClipboardButton = ({ children, content, label = false, sup =
|
||||||
) : (
|
) : (
|
||||||
<CopyIcon className={`${style} tw:text-inherit tw:group-hover:text-secondary`} />
|
<CopyIcon className={`${style} tw:text-inherit tw:group-hover:text-secondary`} />
|
||||||
)}
|
)}
|
||||||
{sup ? null : children}
|
{sup ? null : children}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,32 +4,22 @@ import {
|
||||||
collection,
|
collection,
|
||||||
tags,
|
tags,
|
||||||
techniques,
|
techniques,
|
||||||
designers,
|
|
||||||
developers,
|
|
||||||
examples,
|
examples,
|
||||||
measurements,
|
|
||||||
requiredMeasurements,
|
requiredMeasurements,
|
||||||
optionalMeasurements,
|
optionalMeasurements,
|
||||||
} from '@freesewing/collection'
|
} from '@freesewing/collection'
|
||||||
import { capitalize, linkClasses, mutateObject } from '@freesewing/utils'
|
import { capitalize, linkClasses, mutateObject } from '@freesewing/utils'
|
||||||
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
||||||
|
|
||||||
// Context
|
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext, Fragment } from 'react'
|
import React, { useState, Fragment } from 'react'
|
||||||
import { useFilter } from '@freesewing/react/hooks/useFilter'
|
import { useFilter } from '@freesewing/react/hooks/useFilter'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink, AnchorLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import {
|
import {
|
||||||
CircleIcon,
|
CircleIcon,
|
||||||
CisFemaleIcon,
|
CisFemaleIcon,
|
||||||
DocsIcon,
|
DocsIcon,
|
||||||
FilterIcon,
|
FilterIcon,
|
||||||
HeartIcon,
|
|
||||||
NewPatternIcon,
|
NewPatternIcon,
|
||||||
ResetIcon,
|
ResetIcon,
|
||||||
ShowcaseIcon,
|
ShowcaseIcon,
|
||||||
|
@ -39,7 +29,6 @@ import {
|
||||||
lineDrawingsBack,
|
lineDrawingsBack,
|
||||||
} from '@freesewing/react/components/LineDrawing'
|
} from '@freesewing/react/components/LineDrawing'
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
|
||||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||||
import { MissingLinedrawing } from '../LineDrawing/missing.mjs'
|
import { MissingLinedrawing } from '../LineDrawing/missing.mjs'
|
||||||
|
|
||||||
|
@ -239,38 +228,6 @@ export const Collection = ({ Link = false, linkTo = 'about', editor = false, onC
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* A helper component to show a design technique
|
|
||||||
*
|
|
||||||
* @param {object} props - All React props
|
|
||||||
* @param {function} props.Link - A Link component, typically specific to the framework for client-side routing
|
|
||||||
* @param {string} props.technique - The technique name/id
|
|
||||||
*/
|
|
||||||
const Technique = ({ Link = WebLink, technique }) => (
|
|
||||||
<Link
|
|
||||||
href={`/designs/techniques/${technique}`}
|
|
||||||
className="tw:daisy-badge tw:daisy-badge-accent hover:tw:daisy-badge-secondary tw:hover:shadow tw:font-medium"
|
|
||||||
>
|
|
||||||
{technique}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A helper component to show a design tag
|
|
||||||
*
|
|
||||||
* @param {object} props - All React props
|
|
||||||
* @param {function} props.Link - A Link component, typically specific to the framework for client-side routing
|
|
||||||
* @param {string} props.tag - The tag name/id
|
|
||||||
*/
|
|
||||||
const Tag = ({ Link = WebLink, technique }) => (
|
|
||||||
<Link
|
|
||||||
href={`/designs/tags/${tag}`}
|
|
||||||
className="tw:daisy-badge tw:daisy-badge-primary hover:tw:daisy-badge-secondary tw:hover:shadow tw:font-medium"
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
|
|
||||||
const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
|
const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
|
||||||
if (!Link) Link = WebLink
|
if (!Link) Link = WebLink
|
||||||
|
|
||||||
|
@ -374,10 +331,6 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
|
||||||
// State
|
// State
|
||||||
const [back, setBack] = useState(false)
|
const [back, setBack] = useState(false)
|
||||||
|
|
||||||
// Context
|
|
||||||
const { setModal, clearModal } = useContext(ModalContext)
|
|
||||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
|
||||||
|
|
||||||
if (!design) return null
|
if (!design) return null
|
||||||
|
|
||||||
// Line drawings
|
// Line drawings
|
||||||
|
@ -391,13 +344,6 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
|
||||||
: [about[design].design]
|
: [about[design].design]
|
||||||
const tags = about[design].tags || []
|
const tags = about[design].tags || []
|
||||||
const techniques = about[design].techniques || []
|
const techniques = about[design].techniques || []
|
||||||
const colors = {
|
|
||||||
1: 'success',
|
|
||||||
2: 'success',
|
|
||||||
3: 'warning',
|
|
||||||
4: 'warning',
|
|
||||||
5: 'error',
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeButton = (
|
const makeButton = (
|
||||||
<div className={`tw:grid tw:grid-cols-1 tw:gap-2 tw:mb-4`}>
|
<div className={`tw:grid tw:grid-cols-1 tw:gap-2 tw:mb-4`}>
|
||||||
|
@ -554,21 +500,3 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SharingIsCaring = ({ design }) => (
|
|
||||||
<>
|
|
||||||
<h2>
|
|
||||||
Use <b>#FreeSewing{capitalize(design)}</b> to facilitate discovery
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Please use the{' '}
|
|
||||||
<b>
|
|
||||||
<code>#FreeSewing{capitalize(design)}</code>
|
|
||||||
</b>{' '}
|
|
||||||
hashtag when discussing FreeSewing's <b>{capitalize(design)}</b> pattern online.
|
|
||||||
<br />
|
|
||||||
Doing so can help others discover your post, which really is a win-win.
|
|
||||||
</p>
|
|
||||||
<p>If you like, you can copy the hashtag below:</p>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
|
@ -4,12 +4,12 @@ import React, { useState } from 'react'
|
||||||
* DaisyUI's accordion seems rather unreliable.
|
* DaisyUI's accordion seems rather unreliable.
|
||||||
* So instead, we handle this in React state
|
* So instead, we handle this in React state
|
||||||
*/
|
*/
|
||||||
const getProps = (isActive = false) => ({
|
const getProps = () => ({
|
||||||
className: `tw:p-0 tw:rounded-lg tw:bg-transparent tw:hover:cursor-pointer
|
className: `tw:p-0 tw:rounded-lg tw:bg-transparent tw:hover:cursor-pointer
|
||||||
tw:w-full tw:h-auto tw:content-start tw:text-left tw:list-none`,
|
tw:w-full tw:h-auto tw:content-start tw:text-left tw:list-none`,
|
||||||
})
|
})
|
||||||
|
|
||||||
const getSubProps = (isActive) => ({
|
const getSubProps = () => ({
|
||||||
className: `tw:p-0 tw:rounded-none tw:bg-transparent tw:w-full tw:h-auto
|
className: `tw:p-0 tw:rounded-none tw:bg-transparent tw:w-full tw:h-auto
|
||||||
tw:content-start tw:text-left tw:list-none`,
|
tw:content-start tw:text-left tw:list-none`,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import mustache from 'mustache'
|
import mustache from 'mustache'
|
||||||
import { flattenFlags, stripNamespace, bundlePatternTranslations } from '../lib/index.mjs'
|
import { flattenFlags } from '../lib/index.mjs'
|
||||||
import {
|
import {
|
||||||
ChatIcon,
|
ChatIcon,
|
||||||
ErrorIcon,
|
ErrorIcon,
|
||||||
|
@ -115,7 +115,7 @@ export const FlagsAccordionTitle = ({ flags }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FlagsAccordionEntries = ({ flags, update, pattern, strings }) => {
|
export const FlagsAccordionEntries = ({ flags, update, strings }) => {
|
||||||
const flagList = flattenFlags(flags)
|
const flagList = flattenFlags(flags)
|
||||||
|
|
||||||
if (Object.keys(flagList).length < 1) return null
|
if (Object.keys(flagList).length < 1) return null
|
||||||
|
|
|
@ -24,7 +24,6 @@ import {
|
||||||
ResetIcon,
|
ResetIcon,
|
||||||
RightIcon,
|
RightIcon,
|
||||||
RocketIcon,
|
RocketIcon,
|
||||||
RotateIcon,
|
|
||||||
SaIcon,
|
SaIcon,
|
||||||
SaveAsIcon,
|
SaveAsIcon,
|
||||||
SaveIcon,
|
SaveIcon,
|
||||||
|
@ -133,7 +132,7 @@ export const HeaderMenuTestViewDesignMeasurements = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeaderMenuDropdown = (props) => {
|
export const HeaderMenuDropdown = (props) => {
|
||||||
const { tooltip, toggle, open, setOpen, id, end = false } = props
|
const { toggle, open, setOpen, id } = props
|
||||||
const [localOpen, setLocalOpen] = useState(false)
|
const [localOpen, setLocalOpen] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -460,10 +459,8 @@ export const HeaderMenuUndoIcons = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeaderMenuTestIcons = (props) => {
|
export const HeaderMenuTestIcons = (props) => {
|
||||||
const { update, state, Design } = props
|
const { update } = props
|
||||||
const Button = HeaderMenuButton
|
const Button = HeaderMenuButton
|
||||||
const size = 'tw:w-5 tw:h-5'
|
|
||||||
const undos = state._?.undos && state._.undos.length > 0 ? state._.undos : false
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tw:flex tw:flex-row tw:flex-wrap tw:items-center tw:justify-center tw:px-0.5 tw:lg:px-1">
|
<div className="tw:flex tw:flex-row tw:flex-wrap tw:items-center tw:justify-center tw:px-0.5 tw:lg:px-1">
|
||||||
|
@ -660,11 +657,6 @@ export const HeaderMenuLayoutViewIcons = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pages = pattern.setStores[0].get('pages', {})
|
const pages = pattern.setStores[0].get('pages', {})
|
||||||
const format = state.ui.print?.pages?.size
|
|
||||||
? state.ui.print.pages.size
|
|
||||||
: settings.units === 'imperial'
|
|
||||||
? 'letter'
|
|
||||||
: 'a4'
|
|
||||||
const { cols, rows, count } = pages
|
const { cols, rows, count } = pages
|
||||||
const blank = cols * rows - count
|
const blank = cols * rows - count
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,6 @@ export const LoadingStatus = ({ state, update }) => {
|
||||||
|
|
||||||
if (!state._.loading || Object.keys(state._.loading).length < 1) return null
|
if (!state._.loading || Object.keys(state._.loading).length < 1) return null
|
||||||
|
|
||||||
const colorClasses = {
|
|
||||||
info: 'tw:bg-info tw:text-info-content',
|
|
||||||
primary: 'tw:bg-primary tw:text-primary-content',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tw:fixed tw:bottom-4 md:tw:buttom-28 tw:left-0 tw:w-full tw:z-30 tw:md:px-4 tw:md:mx-auto mb-4">
|
<div className="tw:fixed tw:bottom-4 md:tw:buttom-28 tw:left-0 tw:w-full tw:z-30 tw:md:px-4 tw:md:mx-auto mb-4">
|
||||||
<div className="tw:flex tw:flex-col tw:gap-2">
|
<div className="tw:flex tw:flex-col tw:gap-2">
|
||||||
|
|
|
@ -85,7 +85,7 @@ export const MovablePattern = ({
|
||||||
|
|
||||||
const sortedRenderProps = { ...renderProps, stacks: sortedStacks }
|
const sortedRenderProps = { ...renderProps, stacks: sortedStacks }
|
||||||
|
|
||||||
const Stack = ({ stackName, stack, settings, components, t }) => (
|
const Stack = ({ stackName, stack, settings, components }) => (
|
||||||
<MovableStack
|
<MovableStack
|
||||||
{...{
|
{...{
|
||||||
stackName,
|
stackName,
|
||||||
|
@ -397,7 +397,7 @@ function angle(pointA, pointB) {
|
||||||
|
|
||||||
const rectSize = 24
|
const rectSize = 24
|
||||||
|
|
||||||
const Button = ({ onClickCb, transform, Icon, children, title = '' }) => {
|
const Button = ({ onClickCb, transform, Icon, title = '' }) => {
|
||||||
const _onClick = (event) => {
|
const _onClick = (event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
onClickCb(event)
|
onClickCb(event)
|
||||||
|
@ -413,23 +413,6 @@ const Button = ({ onClickCb, transform, Icon, children, title = '' }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShowButtonsToggle = ({ ui, update }) => {
|
|
||||||
const hideButtons = (evt) => {
|
|
||||||
update.ui('hideMovableButtons', !evt.target.checked)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<label className="label cursor-pointer">
|
|
||||||
<span className="label-text text-lg mr-2">{t('showMovableButtons')}</span>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="toggle toggle-primary"
|
|
||||||
checked={!ui.hideMovableButtons}
|
|
||||||
onChange={hideButtons}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** buttons for manipulating the part */
|
/** buttons for manipulating the part */
|
||||||
export const Buttons = ({ transform, flip, rotate, resetPart, rotate90, iconSize }) => {
|
export const Buttons = ({ transform, flip, rotate, resetPart, rotate90, iconSize }) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useDesignTranslation } from '@freesewing/react/hooks/useDesignTranslation'
|
|
||||||
import { ZoomContextProvider } from './ZoomablePattern.mjs'
|
import { ZoomContextProvider } from './ZoomablePattern.mjs'
|
||||||
import {
|
import { HeaderMenu } from './HeaderMenu.mjs'
|
||||||
HeaderMenu,
|
|
||||||
HeaderMenuDraftViewDesignOptions,
|
|
||||||
HeaderMenuDraftViewCoreSettings,
|
|
||||||
HeaderMenuDraftViewUiPreferences,
|
|
||||||
HeaderMenuDraftViewFlags,
|
|
||||||
} from './HeaderMenu.mjs'
|
|
||||||
import { DesignOptionsMenu } from './menus/DesignOptionsMenu.mjs'
|
import { DesignOptionsMenu } from './menus/DesignOptionsMenu.mjs'
|
||||||
import { CoreSettingsMenu } from './menus/CoreSettingsMenu.mjs'
|
import { CoreSettingsMenu } from './menus/CoreSettingsMenu.mjs'
|
||||||
import { UiPreferencesMenu } from './menus/UiPreferencesMenu.mjs'
|
import { UiPreferencesMenu } from './menus/UiPreferencesMenu.mjs'
|
||||||
import { Accordion } from './Accordion.mjs'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A layout for views that include a drafted pattern
|
* A layout for views that include a drafted pattern
|
||||||
|
@ -24,9 +16,7 @@ import { Accordion } from './Accordion.mjs'
|
||||||
* @param {object] pattern - The drafted pattern
|
* @param {object] pattern - The drafted pattern
|
||||||
*/
|
*/
|
||||||
export const PatternLayout = (props) => {
|
export const PatternLayout = (props) => {
|
||||||
const { menu = null, Design, pattern, update, config, state } = props
|
const { Design, pattern, update, config, state } = props
|
||||||
const i18n = useDesignTranslation(Design.designConfig.data.id)
|
|
||||||
const flags = props.pattern?.setStores?.[0]?.plugins?.['plugin-annotations']?.flags
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ZoomContextProvider>
|
<ZoomContextProvider>
|
||||||
|
|
|
@ -58,7 +58,7 @@ export const UserSetPicker = ({
|
||||||
href={config.hrefNewSet}
|
href={config.hrefNewSet}
|
||||||
className="tw:daisy-btn tw:daisy-btn-accent tw:capitalize"
|
className="tw:daisy-btn tw:daisy-btn-accent tw:capitalize"
|
||||||
target="_BLANK"
|
target="_BLANK"
|
||||||
rel="nofollow"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
Create a new measurements set
|
Create a new measurements set
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { ZoomInIcon, ZoomOutIcon, RotateIcon } from '@freesewing/react/component
|
||||||
* A pattern you can pan and zoom
|
* A pattern you can pan and zoom
|
||||||
*/
|
*/
|
||||||
export const ZoomablePattern = forwardRef(function ZoomablePatternRef(props, ref) {
|
export const ZoomablePattern = forwardRef(function ZoomablePatternRef(props, ref) {
|
||||||
const { renderProps, rotate, update, components = {}, strings = {} } = props
|
const { renderProps, rotate, components = {}, strings = {} } = props
|
||||||
const { onTransformed, zoomFunctions, setZoomFunctions } = useContext(ZoomContext)
|
const { onTransformed, zoomFunctions, setZoomFunctions } = useContext(ZoomContext)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -36,10 +36,8 @@ export const MenuItem = ({
|
||||||
allowOverride = false,
|
allowOverride = false,
|
||||||
ux = 5,
|
ux = 5,
|
||||||
state,
|
state,
|
||||||
docs,
|
|
||||||
config,
|
config,
|
||||||
Design,
|
Design,
|
||||||
i18n,
|
|
||||||
}) => {
|
}) => {
|
||||||
// Local state - whether the override input should be shown
|
// Local state - whether the override input should be shown
|
||||||
const [override, setOverride] = useState(false)
|
const [override, setOverride] = useState(false)
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
MenuScaleSettingValue,
|
MenuScaleSettingValue,
|
||||||
} from './Value.mjs'
|
} from './Value.mjs'
|
||||||
import { MenuItemGroup, MenuItem } from './Container.mjs'
|
import { MenuItemGroup, MenuItem } from './Container.mjs'
|
||||||
import { SettingsIcon } from '@freesewing/react/components/Icon'
|
import { SettingsIcon, TrashIcon } from '@freesewing/react/components/Icon'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The core settings menu
|
* The core settings menu
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { OptionsIcon, SettingsIcon, UiIcon } from '@freesewing/react/components/
|
||||||
import { DesignOptionsMenu } from './DesignOptionsMenu.mjs'
|
import { DesignOptionsMenu } from './DesignOptionsMenu.mjs'
|
||||||
import { CoreSettingsMenu } from './CoreSettingsMenu.mjs'
|
import { CoreSettingsMenu } from './CoreSettingsMenu.mjs'
|
||||||
import { UiPreferencesMenu } from './UiPreferencesMenu.mjs'
|
import { UiPreferencesMenu } from './UiPreferencesMenu.mjs'
|
||||||
import { FlagsAccordionEntries } from '../Flag.mjs'
|
import { FlagsAccordionEntries, FlagsAccordionTitle } from '../Flag.mjs'
|
||||||
import { Accordion } from '../Accordion.mjs'
|
import { Accordion } from '../Accordion.mjs'
|
||||||
|
|
||||||
export const DraftMenu = ({ Design, pattern, state, update, i18n }) => {
|
export const DraftMenu = ({ Design, pattern, state, update, i18n }) => {
|
||||||
|
|
|
@ -16,12 +16,7 @@ import { mergeOptions } from '@freesewing/core'
|
||||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||||
|
|
||||||
/** A boolean version of {@see MenuListInput} that sets up the necessary configuration */
|
/** A boolean version of {@see MenuListInput} that sets up the necessary configuration */
|
||||||
export const MenuBoolInput = (props) => {
|
export const MenuBoolInput = (props) => <MenuListInput {...props} />
|
||||||
const { name, config } = props
|
|
||||||
//const boolConfig = useBoolConfig(name, config)
|
|
||||||
|
|
||||||
return <MenuListInput {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A placeholder for an input to handle constant values */
|
/** A placeholder for an input to handle constant values */
|
||||||
export const MenuConstantInput = ({
|
export const MenuConstantInput = ({
|
||||||
|
@ -59,41 +54,6 @@ export const MenuDegInput = (props) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTitleAndDesc = (config = {}, i18n = {}, isDesignOption = false) => {
|
|
||||||
if (config.choiceTitles && config.choiceDescriptions) {
|
|
||||||
const current = typeof config.current === 'undefined' ? config.dflt : config.current
|
|
||||||
return {
|
|
||||||
title: config.choiceTitles[current],
|
|
||||||
desc: config.choiceDescriptions[current],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let titleKey = config.choiceTitles
|
|
||||||
? 'fixme' //config.choiceTitles[entry]
|
|
||||||
: isDesignOption
|
|
||||||
? i18n?.en?.o?.[name] || name
|
|
||||||
: `${name}.o.${entry}`
|
|
||||||
if (!config.choiceTitles && i18n && i18n.en.o[`${name}.${entry}`])
|
|
||||||
titleKey = i18n.en.o[`${name}.${entry}`]
|
|
||||||
const title = config.title
|
|
||||||
? config.title
|
|
||||||
: config.titleMethod
|
|
||||||
? config.titleMethod(entry)
|
|
||||||
: typeof titleKey === 'string'
|
|
||||||
? i18n.en.o[titleKey]?.t
|
|
||||||
: titleKey.t
|
|
||||||
const desc = config.valueMethod
|
|
||||||
? config.valueMethod(entry)
|
|
||||||
: typeof titleKey === 'string'
|
|
||||||
? i18n.en.o[titleKey]?.d
|
|
||||||
: titleKey.d
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: 'fixmeTitle',
|
|
||||||
desc: 'fixmeDesc',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An input for selecting and item from a list
|
* An input for selecting and item from a list
|
||||||
* @param {String} options.name the name of the property this input changes
|
* @param {String} options.name the name of the property this input changes
|
||||||
|
@ -111,11 +71,9 @@ export const MenuListInput = ({
|
||||||
current,
|
current,
|
||||||
updateHandler,
|
updateHandler,
|
||||||
compact = false,
|
compact = false,
|
||||||
t,
|
|
||||||
changed,
|
changed,
|
||||||
design,
|
design,
|
||||||
isDesignOption = false,
|
isDesignOption = false,
|
||||||
i18n,
|
|
||||||
}) => {
|
}) => {
|
||||||
const handleChange = useSharedHandlers({
|
const handleChange = useSharedHandlers({
|
||||||
dflt: config.dflt,
|
dflt: config.dflt,
|
||||||
|
@ -125,7 +83,7 @@ export const MenuListInput = ({
|
||||||
})
|
})
|
||||||
|
|
||||||
return config.list.map((entry) => {
|
return config.list.map((entry) => {
|
||||||
const { title = false, about = false } = config //getTitleAndDesc(config, i18n, isDesignOption)
|
const { title = false, about = false } = config
|
||||||
if (!title || !about) console.log('No title or about in', { name, config, design })
|
if (!title || !about) console.log('No title or about in', { name, config, design })
|
||||||
const sideBySide = config.sideBySide || about.length + title.length < 42
|
const sideBySide = config.sideBySide || about.length + title.length < 42
|
||||||
|
|
||||||
|
@ -286,7 +244,6 @@ export const MenuSliderInput = ({
|
||||||
setReset,
|
setReset,
|
||||||
children,
|
children,
|
||||||
changed,
|
changed,
|
||||||
i18n,
|
|
||||||
state,
|
state,
|
||||||
Design,
|
Design,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
|
@ -92,7 +92,7 @@ const SampleOptionButton = ({ name, i18n, update }) => (
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
const SampleMeasurementButton = ({ name, i18n, update }) => (
|
const SampleMeasurementButton = ({ name, update }) => (
|
||||||
<button
|
<button
|
||||||
className={
|
className={
|
||||||
'tw:daisy-btn tw:daisy-btn-outline tw:daisy-btn-sm tw:mx-2 ' +
|
'tw:daisy-btn tw:daisy-btn-outline tw:daisy-btn-sm tw:mx-2 ' +
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const UiPreferencesMenu = ({ update, state, Design }) => {
|
||||||
}
|
}
|
||||||
const values = {
|
const values = {
|
||||||
aside: MenuListValue,
|
aside: MenuListValue,
|
||||||
ux: (props) => <span>{state.ui.ux}/5</span>,
|
ux: () => <span>{state.ui.ux}/5</span>,
|
||||||
rotate: MenuListValue,
|
rotate: MenuListValue,
|
||||||
renderer: MenuListValue,
|
renderer: MenuListValue,
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,11 +160,10 @@ export const MenuScaleSettingValue = ({ current, config, changed }) => (
|
||||||
/**
|
/**
|
||||||
* Displays the value for core's only setting
|
* Displays the value for core's only setting
|
||||||
*
|
*
|
||||||
* @param {object} config - The option config
|
|
||||||
* @param {number} current - The current (count) value
|
* @param {number} current - The current (count) value
|
||||||
* @param {bool} changed - Whether or not the value is non-default
|
* @param {bool} changed - Whether or not the value is non-default
|
||||||
*/
|
*/
|
||||||
export const MenuOnlySettingValue = ({ current, config }) => (
|
export const MenuOnlySettingValue = ({ current }) => (
|
||||||
<MenuHighlightValue changed={current !== undefined}>
|
<MenuHighlightValue changed={current !== undefined}>
|
||||||
{current === undefined ? '-' : current.length}
|
{current === undefined ? '-' : current.length}
|
||||||
</MenuHighlightValue>
|
</MenuHighlightValue>
|
||||||
|
|
|
@ -5,10 +5,9 @@ import { Collection } from '@freesewing/react/components/Collection'
|
||||||
* The designs view is loaded if and only if no design name is passed to the editor
|
* The designs view is loaded if and only if no design name is passed to the editor
|
||||||
*
|
*
|
||||||
* @param {Object} props - All the props
|
* @param {Object} props - All the props
|
||||||
* @param {Object} designs - Object holding all designs
|
|
||||||
* @param {Object} update - ViewWrapper state update object
|
* @param {Object} update - ViewWrapper state update object
|
||||||
*/
|
*/
|
||||||
export const DesignsView = ({ designs = {}, update }) => (
|
export const DesignsView = ({ update }) => (
|
||||||
<div className="tw:text-center tw:mt-8 tw:mb-24 tw:p-2 lg: tw:p-8">
|
<div className="tw:text-center tw:mt-8 tw:mb-24 tw:p-2 lg: tw:p-8">
|
||||||
<h1>Choose a design from the FreeSewing collection</h1>
|
<h1>Choose a design from the FreeSewing collection</h1>
|
||||||
<Collection
|
<Collection
|
||||||
|
|
|
@ -27,8 +27,8 @@ export const DraftErrorHandler = ({ failure, errors }) => {
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If you believe your measurements are correct and/or if you'd like further assistance, you
|
If you believe your measurements are correct and/or if you'd like further assistance,
|
||||||
can ask for help <Link href="https://forum.freesewing.eu">on our forum</Link>,{' '}
|
you can ask for help <Link href="https://forum.freesewing.eu">on our forum</Link>,{' '}
|
||||||
<Link href="https://discord.freesewing.org">our Discord server</Link>, or{' '}
|
<Link href="https://discord.freesewing.org">our Discord server</Link>, or{' '}
|
||||||
<Link href="https://codeberg.org/freesewing/freesewing/issues">report an issue</Link>.
|
<Link href="https://codeberg.org/freesewing/freesewing/issues">report an issue</Link>.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -23,7 +23,6 @@ import { yaml as yamlLang } from '@codemirror/lang-yaml'
|
||||||
* @param {Object} props.update - Helper object for updating the editor state
|
* @param {Object} props.update - Helper object for updating the editor state
|
||||||
*/
|
*/
|
||||||
export const EditSettingsView = (props) => {
|
export const EditSettingsView = (props) => {
|
||||||
const [settings, setSettings] = useState(props.state?.settings || {})
|
|
||||||
const { state, config, update } = props
|
const { state, config, update } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -96,7 +95,7 @@ export const PrimedSettingsEditor = (props) => {
|
||||||
setSettings(newSettings)
|
setSettings(newSettings)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// This is fine
|
console.log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,10 @@ import { EditIcon, CodeIcon, TipIcon, PrintIcon } from '@freesewing/react/compon
|
||||||
export const ExportView = (props) => {
|
export const ExportView = (props) => {
|
||||||
const { config, state, update } = props
|
const { config, state, update } = props
|
||||||
const { settings = {} } = state // Guard against undefined settings
|
const { settings = {} } = state // Guard against undefined settings
|
||||||
const [link, setLink] = useState(false)
|
const setLink = useState(false)[1]
|
||||||
const [format, setFormat] = useState(false)
|
const setFormat = useState(false)[1]
|
||||||
|
|
||||||
const { protocol, hostname, port } = window.location
|
const { protocol, port } = window.location
|
||||||
const site =
|
const site =
|
||||||
(protocol === 'https:' && port === 443) || (protocol === 'http:' && port === 80)
|
(protocol === 'https:' && port === 443) || (protocol === 'http:' && port === 80)
|
||||||
? `${window.location.protocol}//${window.location.hostname}`
|
? `${window.location.protocol}//${window.location.hostname}`
|
||||||
|
@ -102,6 +102,7 @@ export const ExportView = (props) => {
|
||||||
<H3>ISO paper sizes</H3>
|
<H3>ISO paper sizes</H3>
|
||||||
{['a4', 'a3', 'a2', 'a1', 'a0'].map((format) => (
|
{['a4', 'a3', 'a2', 'a1', 'a0'].map((format) => (
|
||||||
<button
|
<button
|
||||||
|
key={format}
|
||||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||||
onClick={() => exportPattern({ ...exportProps, format })}
|
onClick={() => exportPattern({ ...exportProps, format })}
|
||||||
>
|
>
|
||||||
|
@ -114,6 +115,7 @@ export const ExportView = (props) => {
|
||||||
<H3>Other paper sizes</H3>
|
<H3>Other paper sizes</H3>
|
||||||
{['letter', 'legal', 'tabloid'].map((format) => (
|
{['letter', 'legal', 'tabloid'].map((format) => (
|
||||||
<button
|
<button
|
||||||
|
key={format}
|
||||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||||
onClick={() => exportPattern({ ...exportProps, format })}
|
onClick={() => exportPattern({ ...exportProps, format })}
|
||||||
>
|
>
|
||||||
|
@ -130,6 +132,7 @@ export const ExportView = (props) => {
|
||||||
<div className="tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2 tw:mt-2">
|
<div className="tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2 tw:mt-2">
|
||||||
{['svg', 'pdf'].map((format) => (
|
{['svg', 'pdf'].map((format) => (
|
||||||
<button
|
<button
|
||||||
|
key={format}
|
||||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||||
onClick={() => exportPattern({ ...exportProps, format })}
|
onClick={() => exportPattern({ ...exportProps, format })}
|
||||||
>
|
>
|
||||||
|
@ -143,6 +146,7 @@ export const ExportView = (props) => {
|
||||||
<div className="tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2 tw:mt-2">
|
<div className="tw:grid tw:grid-cols-1 tw:lg:grid-cols-2 tw:gap-2 tw:mt-2">
|
||||||
{['json', 'yaml'].map((format) => (
|
{['json', 'yaml'].map((format) => (
|
||||||
<button
|
<button
|
||||||
|
key={format}
|
||||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||||
onClick={() => exportPattern({ ...exportProps, format })}
|
onClick={() => exportPattern({ ...exportProps, format })}
|
||||||
>
|
>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { DraftErrorHandler } from './DraftErrorHandler.mjs'
|
||||||
|
|
||||||
export const LayoutView = (props) => {
|
export const LayoutView = (props) => {
|
||||||
const { config, state, update, Design } = props
|
const { config, state, update, Design } = props
|
||||||
const { ui, settings } = state
|
const { settings } = state
|
||||||
const defaultSettings = defaultPrintSettings(settings?.units)
|
const defaultSettings = defaultPrintSettings(settings?.units)
|
||||||
|
|
||||||
// Settings for the tiler plugin
|
// Settings for the tiler plugin
|
||||||
|
|
|
@ -8,8 +8,8 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
// Components
|
// Components
|
||||||
import { RoleBlock } from '@freesewing/react/components/Role'
|
import { RoleBlock } from '@freesewing/react/components/Role'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
import { StringInput } from '@freesewing/react/components/Input'
|
import { StringInput, MarkdownInput } from '@freesewing/react/components/Input'
|
||||||
import { SaveAsIcon } from '@freesewing/react/components/Icon'
|
import { SaveAsIcon, SaveIcon } from '@freesewing/react/components/Icon'
|
||||||
import { H1 } from '@freesewing/react/components/Heading'
|
import { H1 } from '@freesewing/react/components/Heading'
|
||||||
import { Link, SuccessLink } from '@freesewing/react/components/Link'
|
import { Link, SuccessLink } from '@freesewing/react/components/Link'
|
||||||
import { HeaderMenu } from '../HeaderMenu.mjs'
|
import { HeaderMenu } from '../HeaderMenu.mjs'
|
||||||
|
@ -67,7 +67,8 @@ export const SaveView = ({ config, state, update }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const savePattern = async () => {
|
const savePattern = async () => {
|
||||||
setLoadingStatus([true, 'Saving pattern...'])
|
const loadingId = 'savePattern'
|
||||||
|
update.startLoading(loadingId)
|
||||||
const patternData = {
|
const patternData = {
|
||||||
design: state.design,
|
design: state.design,
|
||||||
settings,
|
settings,
|
||||||
|
@ -77,8 +78,9 @@ export const SaveView = ({ config, state, update }) => {
|
||||||
}
|
}
|
||||||
const result = await backend.updatePattern(saveAs.pattern, patternData)
|
const result = await backend.updatePattern(saveAs.pattern, patternData)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
update.stopLoading(loadingId)
|
||||||
setSavedId(saveAs.pattern)
|
setSavedId(saveAs.pattern)
|
||||||
update.notify({ color: 'success', msg: 'boom' }, saveAs.pattern)
|
update.notifySuccess('Pattern saved', loadingId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,11 +98,11 @@ export const SaveView = ({ config, state, update }) => {
|
||||||
</Popout>
|
</Popout>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
className={`${classeshorFlexNoSm} tw:btn tw:btn-primary tw:btn-lg tw:w-full tw:mt-2 tw:my-8`}
|
className={`tw:flex tw:flex-row tw:items-center tw:gap-2 tw:btn tw:btn-primary tw:btn-lg tw:w-full tw:mt-2 tw:my-8`}
|
||||||
onClick={savePattern}
|
onClick={savePattern}
|
||||||
>
|
>
|
||||||
<SaveIcon className="tw:h-8 tw:w-8" />
|
<SaveIcon className="tw:h-8 tw:w-8" />
|
||||||
Save Patter #{saveAs.pattern}
|
Save Pattern #{saveAs.pattern}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -137,11 +139,7 @@ export const SaveView = ({ config, state, update }) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{withNotes ? (
|
{withNotes ? (
|
||||||
<Swizzled.components.MarkdownInput
|
<MarkdownInput label="Pattern notes" current={notes} update={setNotes} />
|
||||||
label="Pattern notes"
|
|
||||||
current={notes}
|
|
||||||
update={setNotes}
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
<div className="tw:flex tw:flex-row tw:gap-2 tw:mt-8">
|
<div className="tw:flex tw:flex-row tw:gap-2 tw:mt-8">
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -69,7 +69,6 @@ export const TestView = ({ Design, state, update, config }) => {
|
||||||
patternLocale={state.locale || 'en'}
|
patternLocale={state.locale || 'en'}
|
||||||
rotate={state.ui.rotate}
|
rotate={state.ui.rotate}
|
||||||
strings={strings}
|
strings={strings}
|
||||||
rotate={state.ui.rotate}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import * as echarts from 'echarts'
|
|
||||||
import { timingPlugin } from '@freesewing/plugin-timing'
|
import { timingPlugin } from '@freesewing/plugin-timing'
|
||||||
// Components
|
// Components
|
||||||
import { ChartWrapper } from '@freesewing/react/components/Echart'
|
import { ChartWrapper } from '@freesewing/react/components/Echart'
|
||||||
|
@ -36,18 +35,6 @@ const TimingHeader = ({ timing, parts }) => {
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveColor = (color) => {
|
|
||||||
const [c, i] = color.split('-')
|
|
||||||
|
|
||||||
return tailwindColors[c][i]
|
|
||||||
}
|
|
||||||
|
|
||||||
const getColor = (i, colors) =>
|
|
||||||
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{ offset: 0, color: resolveColor(colors[i % colors.length]) + 'dd' },
|
|
||||||
{ offset: 1, color: resolveColor(colors[i % colors.length]) },
|
|
||||||
])
|
|
||||||
|
|
||||||
const timeScore = (took) => {
|
const timeScore = (took) => {
|
||||||
if (took < 25) return 'Very Fast'
|
if (took < 25) return 'Very Fast'
|
||||||
if (took < 50) return 'Fast'
|
if (took < 50) return 'Fast'
|
||||||
|
@ -58,7 +45,6 @@ const timeScore = (took) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const option = (parts, data, took, setData) => ({
|
const option = (parts, data, took, setData) => ({
|
||||||
//color: colors.map((color) => resolveColor(color)),
|
|
||||||
title: {
|
title: {
|
||||||
text: `Timing of most recent draft: ${timeScore(took)}`,
|
text: `Timing of most recent draft: ${timeScore(took)}`,
|
||||||
left: 'center',
|
left: 'center',
|
||||||
|
@ -127,7 +113,7 @@ const option = (parts, data, took, setData) => ({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
yAxis: [{ type: 'value' }],
|
yAxis: [{ type: 'value' }],
|
||||||
series: parts.map((name, i) => ({
|
series: parts.map((name) => ({
|
||||||
name,
|
name,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
stack: 'Total',
|
stack: 'Total',
|
||||||
|
|
|
@ -70,7 +70,7 @@ export const ViewPicker = ({ Design, update, state }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MainCard = ({ view, update, Design }) => {
|
const MainCard = ({ view, update }) => {
|
||||||
const Icon = viewIcons[view]
|
const Icon = viewIcons[view]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -37,6 +37,7 @@ export const useEditorState = (init = {}, setEphemeralState, config) => {
|
||||||
if (typeof data.s === 'object') setState(data.s)
|
if (typeof data.s === 'object') setState(data.s)
|
||||||
else setState(init)
|
else setState(init)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
setState(init)
|
setState(init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,31 +46,6 @@ export const useEditorState = (init = {}, setEphemeralState, config) => {
|
||||||
return [state, setState, update]
|
return [state, setState, update]
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Our URL state library does not support storing Javascript objects out of the box.
|
|
||||||
* But it allows us to pass a customer parser to handle them, so this is that parser
|
|
||||||
*/
|
|
||||||
const pojoParser = {
|
|
||||||
parse: (v) => {
|
|
||||||
let val
|
|
||||||
try {
|
|
||||||
val = JSON.parse(v)
|
|
||||||
} catch (err) {
|
|
||||||
val = null
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
},
|
|
||||||
serialize: (v) => {
|
|
||||||
let val
|
|
||||||
try {
|
|
||||||
val = JSON.stringify(v)
|
|
||||||
} catch (err) {
|
|
||||||
val = null
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHashData() {
|
function getHashData() {
|
||||||
if (!window) return false
|
if (!window) return false
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ export function menuCoreSettingsStructure({
|
||||||
title: 'Seam Allowance Size',
|
title: 'Seam Allowance Size',
|
||||||
about: (
|
about: (
|
||||||
<>
|
<>
|
||||||
Controls the size of the pattern's seam allowance.
|
Controls the size of the pattern's seam allowance.
|
||||||
<CoreDocsLink item="sa" />
|
<CoreDocsLink item="sa" />
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
|
|
@ -9,6 +9,7 @@ const DesignDocsLink = ({ design, item }) => (
|
||||||
href={`/docs/designs/${design}/options/#${item.toLowerCase()}`}
|
href={`/docs/designs/${design}/options/#${item.toLowerCase()}`}
|
||||||
className={`${linkClasses} tw:px-2`}
|
className={`${linkClasses} tw:px-2`}
|
||||||
target="_BLANK"
|
target="_BLANK"
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
Learn more
|
Learn more
|
||||||
</a>
|
</a>
|
||||||
|
@ -62,14 +63,14 @@ export function menuDesignOptionsStructure(design, options, settings, asFullList
|
||||||
option.valueTitles = {}
|
option.valueTitles = {}
|
||||||
option.choiceTitles = {}
|
option.choiceTitles = {}
|
||||||
option.choiceDescriptions = {}
|
option.choiceDescriptions = {}
|
||||||
for (const entry of option.list) {
|
option.list.forEach(() => {
|
||||||
option.choiceTitles.false = eno[`${option.name}No`]?.t || option.name
|
option.choiceTitles.false = eno[`${option.name}No`]?.t || option.name
|
||||||
option.choiceDescriptions.false = eno[`${option.name}No`]?.d || 'No'
|
option.choiceDescriptions.false = eno[`${option.name}No`]?.d || 'No'
|
||||||
option.valueTitles.false = 'No'
|
option.valueTitles.false = 'No'
|
||||||
option.choiceTitles.true = eno[`${option.name}Yes`]?.t || 'Yes'
|
option.choiceTitles.true = eno[`${option.name}Yes`]?.t || 'Yes'
|
||||||
option.choiceDescriptions.true = eno[`${option.name}Yes`]?.d || 'No'
|
option.choiceDescriptions.true = eno[`${option.name}Yes`]?.d || 'No'
|
||||||
option.valueTitles.true = 'Yes'
|
option.valueTitles.true = 'Yes'
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
if (typeof option.menu === 'function')
|
if (typeof option.menu === 'function')
|
||||||
option.menu = asFullList
|
option.menu = asFullList
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { defaultConfig } from '../config/index.mjs'
|
import { defaultConfig } from '../config/index.mjs'
|
||||||
import { round, formatMm, randomLoadingMessage } from '@freesewing/utils'
|
import { round, formatMm, randomLoadingMessage } from '@freesewing/utils'
|
||||||
import { formatDesignOptionValue, menuCoreSettingsStructure } from './index.mjs'
|
import { formatDesignOptionValue, menuCoreSettingsStructure, fractionToDecimal } from './index.mjs'
|
||||||
import { menuUiPreferencesStructure } from './ui-preferences.mjs'
|
import { menuUiPreferencesStructure } from './ui-preferences.mjs'
|
||||||
import { i18n } from '@freesewing/collection'
|
import { i18n } from '@freesewing/collection'
|
||||||
import { i18n as pluginI18n } from '@freesewing/core-plugins'
|
import { i18n as pluginI18n } from '@freesewing/core-plugins'
|
||||||
|
@ -721,15 +721,15 @@ export function cloudImageUrl({ id = 'default-avatar', variant = 'public' }) {
|
||||||
/*
|
/*
|
||||||
* Return something default so that people will actually change it
|
* Return something default so that people will actually change it
|
||||||
*/
|
*/
|
||||||
if (!id || id === 'default-avatar') return config.cloudImageDflt
|
if (!id || id === 'default-avatar') return defaultConfig.cloudImageDflt
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the variant is invalid, set it to the smallest thumbnail so
|
* If the variant is invalid, set it to the smallest thumbnail so
|
||||||
* people don't load enourmous images by accident
|
* people don't load enourmous images by accident
|
||||||
*/
|
*/
|
||||||
if (!config.cloudImageVariants.includes(variant)) variant = 'sq100'
|
if (!defaultConfig.cloudImageVariants.includes(variant)) variant = 'sq100'
|
||||||
|
|
||||||
return `${config.cloudImageUrl}${id}/${variant}`
|
return `${defaultConfig.cloudImageUrl}${id}/${variant}`
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This method does nothing. It is used to disable certain methods
|
* This method does nothing. It is used to disable certain methods
|
||||||
|
|
|
@ -3,7 +3,7 @@ import fileSaver from 'file-saver'
|
||||||
import { themePlugin } from '@freesewing/plugin-theme'
|
import { themePlugin } from '@freesewing/plugin-theme'
|
||||||
import { pluginI18n } from '@freesewing/plugin-i18n'
|
import { pluginI18n } from '@freesewing/plugin-i18n'
|
||||||
import { tilerPlugin } from './plugin-tiler.mjs'
|
import { tilerPlugin } from './plugin-tiler.mjs'
|
||||||
import { capitalize, escapeSvgText, formatMm, get } from '@freesewing/utils'
|
import { capitalize, escapeSvgText, get } from '@freesewing/utils'
|
||||||
import mustache from 'mustache'
|
import mustache from 'mustache'
|
||||||
import he from 'he'
|
import he from 'he'
|
||||||
import yaml from 'js-yaml'
|
import yaml from 'js-yaml'
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { linkClasses } from '@freesewing/utils'
|
|
||||||
import {
|
import {
|
||||||
CoverPageIcon,
|
CoverPageIcon,
|
||||||
PageMarginIcon,
|
PageMarginIcon,
|
||||||
|
@ -9,13 +8,6 @@ import {
|
||||||
ScaleIcon,
|
ScaleIcon,
|
||||||
} from '@freesewing/react/components/Icon'
|
} from '@freesewing/react/components/Icon'
|
||||||
|
|
||||||
const UiDocsLink = ({ item }) => (
|
|
||||||
<a href={`/docs/about/site/draft/#${item.toLowerCase()}`} className={`${linkClasses} tw:px-2`}>
|
|
||||||
Learn more
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
|
|
||||||
const sizes = ['a4', 'a3', 'a2', 'a1', 'a0', 'letter', 'legal', 'tabloid']
|
|
||||||
const defaultPrintSettings = (units) => ({
|
const defaultPrintSettings = (units) => ({
|
||||||
size: units === 'imperial' ? 'letter' : 'a4',
|
size: units === 'imperial' ? 'letter' : 'a4',
|
||||||
orientation: 'portrait',
|
orientation: 'portrait',
|
||||||
|
|
|
@ -27,7 +27,6 @@ export const IconWrapper = ({
|
||||||
fill = false,
|
fill = false,
|
||||||
fillOpacity = 1,
|
fillOpacity = 1,
|
||||||
dasharray = null,
|
dasharray = null,
|
||||||
wrapped = true,
|
|
||||||
}) => (
|
}) => (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
import { collection } from '@freesewing/collection'
|
import { collection } from '@freesewing/collection'
|
||||||
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
||||||
// Context
|
// Context
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useCallback, useContext } from 'react'
|
import React, { useState, useCallback, useContext } from 'react'
|
||||||
|
@ -19,7 +18,6 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { TrashIcon, ResetIcon, UploadIcon, HelpIcon } from '@freesewing/react/components/Icon'
|
import { TrashIcon, ResetIcon, UploadIcon, HelpIcon } from '@freesewing/react/components/Icon'
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
|
||||||
import { isDegreeMeasurement } from '@freesewing/config'
|
import { isDegreeMeasurement } from '@freesewing/config'
|
||||||
import { Tabs, Tab } from '@freesewing/react/components/Tab'
|
import { Tabs, Tab } from '@freesewing/react/components/Tab'
|
||||||
import Markdown from 'react-markdown'
|
import Markdown from 'react-markdown'
|
||||||
|
@ -68,31 +66,50 @@ const HelpLink = ({ help, Link = false }) => {
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const Fieldset = ({
|
export const Fieldset = ({
|
||||||
Link=false,
|
Link = false,
|
||||||
box=false,
|
box = false,
|
||||||
children,
|
children,
|
||||||
label=false,
|
label = false,
|
||||||
labelBL=false,
|
labelBL = false,
|
||||||
labelBR=false,
|
labelBR = false,
|
||||||
labelTR=false,
|
labelTR = false,
|
||||||
legend=false,
|
legend = false,
|
||||||
forId='',
|
forId = '',
|
||||||
help=false,
|
help = false,
|
||||||
}) => (
|
}) => (
|
||||||
<fieldset className={`tw:daisy-fieldset tw:w-full tw:mt-2 ${box ? 'tw:bg-base-200 tw:border-base-300 tw:rounded-box tw:border tw:p-4' : ''}`}>
|
<fieldset
|
||||||
|
className={`tw:daisy-fieldset tw:w-full tw:mt-2 ${box ? 'tw:bg-base-200 tw:border-base-300 tw:rounded-box tw:border tw:p-4' : ''}`}
|
||||||
|
>
|
||||||
{legend ? (
|
{legend ? (
|
||||||
<legend className="tw:daisy-fieldset-legend tw:px-2 tw:pb-1">
|
<legend className="tw:daisy-fieldset-legend tw:px-2 tw:pb-1">
|
||||||
{legend}<HelpLink {...{ help, Link }} />
|
{legend}
|
||||||
|
<HelpLink {...{ help, Link }} />
|
||||||
</legend>
|
</legend>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="tw:flex tw:flex-row tw:justify-between tw:px-2">
|
<div className="tw:flex tw:flex-row tw:justify-between tw:px-2">
|
||||||
{label ? <label className="tw:daisy-label" htmlFor={forId}>{label}</label> : null }
|
{label ? (
|
||||||
{labelTR ? <label className="tw:daisy-label" htmlFor={forId}>{labelTR}</label> : null }
|
<label className="tw:daisy-label" htmlFor={forId}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
) : null}
|
||||||
|
{labelTR ? (
|
||||||
|
<label className="tw:daisy-label" htmlFor={forId}>
|
||||||
|
{labelTR}
|
||||||
|
</label>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
<div className="tw:flex tw:flex-row tw:justify-between tw:px-2">
|
<div className="tw:flex tw:flex-row tw:justify-between tw:px-2">
|
||||||
{labelBL ? <label className="tw:daisy-label" htmlFor={forId}>{labelBL}</label> : null }
|
{labelBL ? (
|
||||||
{labelBR ? <label className="tw:daisy-label" htmlFor={forId}>{labelBR}</label> : null }
|
<label className="tw:daisy-label" htmlFor={forId}>
|
||||||
|
{labelBL}
|
||||||
|
</label>
|
||||||
|
) : null}
|
||||||
|
{labelBR ? (
|
||||||
|
<label className="tw:daisy-label" htmlFor={forId}>
|
||||||
|
{labelBR}
|
||||||
|
</label>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
)
|
)
|
||||||
|
@ -153,7 +170,7 @@ export const ButtonFrame = ({ active, children, dense, noBg, onClick }) => (
|
||||||
export const NumberInput = ({
|
export const NumberInput = ({
|
||||||
box = false,
|
box = false,
|
||||||
current,
|
current,
|
||||||
help=false,
|
help = false,
|
||||||
inputMode = 'decimal',
|
inputMode = 'decimal',
|
||||||
label = false,
|
label = false,
|
||||||
labelBL = false,
|
labelBL = false,
|
||||||
|
@ -212,7 +229,7 @@ export const NumberInput = ({
|
||||||
export const StringInput = ({
|
export const StringInput = ({
|
||||||
box = false,
|
box = false,
|
||||||
current,
|
current,
|
||||||
help=false,
|
help = false,
|
||||||
label = false,
|
label = false,
|
||||||
labelBL = false,
|
labelBL = false,
|
||||||
labelBR = false,
|
labelBR = false,
|
||||||
|
@ -265,7 +282,7 @@ export const StringInput = ({
|
||||||
export const MfaInput = ({
|
export const MfaInput = ({
|
||||||
box = false,
|
box = false,
|
||||||
current,
|
current,
|
||||||
help=false,
|
help = false,
|
||||||
label = false,
|
label = false,
|
||||||
labelBL = false,
|
labelBL = false,
|
||||||
labelBR = false,
|
labelBR = false,
|
||||||
|
@ -273,12 +290,26 @@ export const MfaInput = ({
|
||||||
id = 'mfa',
|
id = 'mfa',
|
||||||
inputMode = 'numeric',
|
inputMode = 'numeric',
|
||||||
legend = false,
|
legend = false,
|
||||||
placeholder="MFA Code",
|
placeholder = 'MFA Code',
|
||||||
update,
|
update,
|
||||||
valid = (val) => val.length > 4,
|
valid = (val) => val.length > 4,
|
||||||
}) => (
|
}) => (
|
||||||
<NumberInput
|
<NumberInput
|
||||||
{...{ box, current, help, label, labelBL, labelBR, labelTR, id, inputMode, legend, placeholder, update, valid }}
|
{...{
|
||||||
|
box,
|
||||||
|
current,
|
||||||
|
help,
|
||||||
|
label,
|
||||||
|
labelBL,
|
||||||
|
labelBR,
|
||||||
|
labelTR,
|
||||||
|
id,
|
||||||
|
inputMode,
|
||||||
|
legend,
|
||||||
|
placeholder,
|
||||||
|
update,
|
||||||
|
valid,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -304,7 +335,7 @@ export const MfaInput = ({
|
||||||
export const PasswordInput = ({
|
export const PasswordInput = ({
|
||||||
box = false,
|
box = false,
|
||||||
current,
|
current,
|
||||||
help=false,
|
help = false,
|
||||||
label = false,
|
label = false,
|
||||||
labelBL = false,
|
labelBL = false,
|
||||||
labelTR = false,
|
labelTR = false,
|
||||||
|
@ -712,7 +743,7 @@ export const MarkdownInput = ({
|
||||||
labelTR = false,
|
labelTR = false,
|
||||||
legend = false,
|
legend = false,
|
||||||
update,
|
update,
|
||||||
placeholder='',
|
placeholder = '',
|
||||||
}) => (
|
}) => (
|
||||||
<Fieldset {...{ box, help, label, labelTR, labelBL, labelBR, legend }} forId={id}>
|
<Fieldset {...{ box, help, label, labelTR, labelBL, labelBR, legend }} forId={id}>
|
||||||
<Tabs tabs={['edit', 'preview']}>
|
<Tabs tabs={['edit', 'preview']}>
|
||||||
|
@ -743,7 +774,6 @@ export const MarkdownInput = ({
|
||||||
* @component
|
* @component
|
||||||
* @param {object} props - All component props
|
* @param {object} props - All component props
|
||||||
* @param {boolean} [props.box = false] - Set this to true to render a boxed fieldset
|
* @param {boolean} [props.box = false] - Set this to true to render a boxed fieldset
|
||||||
* @param {number} props.current - The current value, to manage the state of this input
|
|
||||||
* @param {string|function} [props.props.help = false] - An optional URL/method to link/show help or docs
|
* @param {string|function} [props.props.help = false] - An optional URL/method to link/show help or docs
|
||||||
* @param {string} [props.id = ''] - Id of the HTML element to link the fieldset labels
|
* @param {string} [props.id = ''] - Id of the HTML element to link the fieldset labels
|
||||||
* @param {boolean} [props.imperial = false] - Set this to true to render imperial units
|
* @param {boolean} [props.imperial = false] - Set this to true to render imperial units
|
||||||
|
@ -758,7 +788,6 @@ export const MarkdownInput = ({
|
||||||
*/
|
*/
|
||||||
export const MeasurementInput = ({
|
export const MeasurementInput = ({
|
||||||
box = false,
|
box = false,
|
||||||
current,
|
|
||||||
help = false,
|
help = false,
|
||||||
id = '',
|
id = '',
|
||||||
imperial = false,
|
imperial = false,
|
||||||
|
@ -768,7 +797,7 @@ export const MeasurementInput = ({
|
||||||
m,
|
m,
|
||||||
update,
|
update,
|
||||||
original,
|
original,
|
||||||
placeholder='',
|
placeholder = '',
|
||||||
}) => {
|
}) => {
|
||||||
const isDegree = isDegreeMeasurement(m)
|
const isDegree = isDegreeMeasurement(m)
|
||||||
const units = imperial ? 'imperial' : 'metric'
|
const units = imperial ? 'imperial' : 'metric'
|
||||||
|
@ -876,7 +905,6 @@ export const MeasurementInput = ({
|
||||||
* @param {string} [props.legend = false] - The fieldset legend
|
* @param {string} [props.legend = false] - The fieldset legend
|
||||||
* @param {function} props.update - The onChange handler
|
* @param {function} props.update - The onChange handler
|
||||||
* @param {number} props.original - The original value, which allows a reset
|
* @param {number} props.original - The original value, which allows a reset
|
||||||
* @param {function} [props.valid = () => true] - A function that should return whether the value is valid or not
|
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const FileInput = ({
|
export const FileInput = ({
|
||||||
|
@ -892,7 +920,6 @@ export const FileInput = ({
|
||||||
legend = false,
|
legend = false,
|
||||||
update,
|
update,
|
||||||
original,
|
original,
|
||||||
valid = () => true,
|
|
||||||
}) => {
|
}) => {
|
||||||
/*
|
/*
|
||||||
* Ondrop handler
|
* Ondrop handler
|
||||||
|
@ -974,8 +1001,6 @@ export const FileInput = ({
|
||||||
* @param {array} [props.list = [true, false] - An array of values to choose between
|
* @param {array} [props.list = [true, false] - An array of values to choose between
|
||||||
* @param {function} props.update - The onChange handler
|
* @param {function} props.update - The onChange handler
|
||||||
* @param {any} [props.on = true] - The value that should show the toggle in the 'on' state
|
* @param {any} [props.on = true] - The value that should show the toggle in the 'on' state
|
||||||
* @param {number} props.original - The original value, which allows a reset
|
|
||||||
* @param {function} [props.valid = () => true] - A function that should return whether the value is valid or not
|
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const ToggleInput = ({
|
export const ToggleInput = ({
|
||||||
|
@ -993,8 +1018,6 @@ export const ToggleInput = ({
|
||||||
list = [true, false],
|
list = [true, false],
|
||||||
update,
|
update,
|
||||||
on = true,
|
on = true,
|
||||||
original,
|
|
||||||
valid = () => true,
|
|
||||||
}) => (
|
}) => (
|
||||||
<Fieldset {...{ box, help, labelTR, labelBL, labelBR, legend }} forId={id}>
|
<Fieldset {...{ box, help, labelTR, labelBL, labelBR, legend }} forId={id}>
|
||||||
<label className="tw:daisy-label">
|
<label className="tw:daisy-label">
|
||||||
|
@ -1009,8 +1032,7 @@ export const ToggleInput = ({
|
||||||
/>
|
/>
|
||||||
{label
|
{label
|
||||||
? `${label} (${current === on ? labels[0] : labels[1]})`
|
? `${label} (${current === on ? labels[0] : labels[1]})`
|
||||||
: `${current === on ? labels[0] : labels[1]}`
|
: `${current === on ? labels[0] : labels[1]}`}
|
||||||
}
|
|
||||||
</label>
|
</label>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const KeyVal = ({
|
||||||
href = false,
|
href = false,
|
||||||
onClick = false,
|
onClick = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [copied, setCopied] = useState(false)
|
const setCopied = useState(false)[1]
|
||||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||||
|
|
||||||
let colorClasses1 = primaryClasses1
|
let colorClasses1 = primaryClasses1
|
||||||
|
@ -83,7 +83,6 @@ export const KeyVal = ({
|
||||||
const LinkKeyVal = ({
|
const LinkKeyVal = ({
|
||||||
k,
|
k,
|
||||||
val,
|
val,
|
||||||
color = 'primary',
|
|
||||||
small = false,
|
small = false,
|
||||||
href = false,
|
href = false,
|
||||||
colorClasses1,
|
colorClasses1,
|
||||||
|
@ -134,21 +133,6 @@ const successClasses2 = `tw:text-success tw:border-success`
|
||||||
const errorClasses1 = `tw:text-error-content tw:bg-error tw:border-error`
|
const errorClasses1 = `tw:text-error-content tw:bg-error tw:border-error`
|
||||||
const errorClasses2 = `tw:text-error tw:border-error`
|
const errorClasses2 = `tw:text-error tw:border-error`
|
||||||
|
|
||||||
const PrimarySpans = ({ small, k, val }) => (
|
|
||||||
<>
|
|
||||||
<span
|
|
||||||
className={`${sharedClasses} ${small ? 'tw:rounded-l' : 'tw:rounded-l-lg'} ${primaryClasses} ${small ? 'tw:text-xs' : ''}`}
|
|
||||||
>
|
|
||||||
{k}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`${sharedClasses} ${small ? 'tw:rounded-r' : 'tw:rounded-r-lg'} ${primaryClasses} ${small ? 'tw:text-xs' : ''}`}
|
|
||||||
>
|
|
||||||
{val}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCopied = (setCopied, setLoadingStatus, label) => {
|
const handleCopied = (setCopied, setLoadingStatus, label) => {
|
||||||
setCopied(true)
|
setCopied(true)
|
||||||
setLoadingStatus([
|
setLoadingStatus([
|
||||||
|
|
|
@ -10,11 +10,10 @@ import { Link as DefaultLink } from '@freesewing/react/components/Link'
|
||||||
* @param {function} [props.Link = false] - An optional framework specific Link component
|
* @param {function} [props.Link = false] - An optional framework specific Link component
|
||||||
* @param {JSX.Element} [props.children = []] - The component children to render inside the layout
|
* @param {JSX.Element} [props.children = []] - The component children to render inside the layout
|
||||||
* @param {array} [props.crumbs = []] - Data for the breadcrumbs, see Breadcrumbs
|
* @param {array} [props.crumbs = []] - Data for the breadcrumbs, see Breadcrumbs
|
||||||
* @param {string} props.description - The page description
|
|
||||||
* @param {string} props.title - The page title
|
* @param {string} props.title - The page title
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const Layout = ({ children = [], crumbs = [], description, Link = false, title }) => {
|
export const Layout = ({ children = [], crumbs = [], Link = false, title }) => {
|
||||||
if (!Link) Link = DefaultLink
|
if (!Link) Link = DefaultLink
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -48,7 +48,7 @@ export const AlbertFront = ({ className, stroke = 1 }) => (
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const AlbertBack = ({ className, stroke = 1 }) => (
|
export const AlbertBack = ({ className, stroke = 1 }) => (
|
||||||
<LineDrawingWrapper viewBox="74 0 74 119" {...props}>
|
<LineDrawingWrapper viewBox="74 0 74 119" {...{ className, stroke }}>
|
||||||
<Back stroke={stroke * strokeScale} />
|
<Back stroke={stroke * strokeScale} />
|
||||||
</LineDrawingWrapper>
|
</LineDrawingWrapper>
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,7 +17,7 @@ const strokeScale = 0.7
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const Bent = ({ className, stroke = 1 }) => (
|
export const Bent = ({ className, stroke = 1 }) => (
|
||||||
<LineDrawingWrapper viewBox="0 -70 210 210" {...{ stroke }}>
|
<LineDrawingWrapper viewBox="0 -70 210 210" {...{ className, stroke }}>
|
||||||
<Front stroke={strokeScale * stroke} />
|
<Front stroke={strokeScale * stroke} />
|
||||||
<Back stroke={strokeScale * stroke} />
|
<Back stroke={strokeScale * stroke} />
|
||||||
</LineDrawingWrapper>
|
</LineDrawingWrapper>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { LineDrawingWrapper, thin, dashed } from './shared.mjs'
|
import { LineDrawingWrapper } from './shared.mjs'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This strokeScale factor is used to normalize the stroke across
|
* This strokeScale factor is used to normalize the stroke across
|
||||||
|
@ -56,7 +56,7 @@ export const JaneBack = ({ className, stroke = 1 }) => (
|
||||||
/*
|
/*
|
||||||
* SVG elements for the front
|
* SVG elements for the front
|
||||||
*/
|
*/
|
||||||
const Front = ({ stroke }) => (
|
const Front = () => (
|
||||||
<>
|
<>
|
||||||
<path
|
<path
|
||||||
key="outline"
|
key="outline"
|
||||||
|
@ -69,7 +69,7 @@ const Front = ({ stroke }) => (
|
||||||
/*
|
/*
|
||||||
* SVG elements for the back
|
* SVG elements for the back
|
||||||
*/
|
*/
|
||||||
const Back = ({ stroke }) => (
|
const Back = () => (
|
||||||
<>
|
<>
|
||||||
<path
|
<path
|
||||||
key="outline"
|
key="outline"
|
||||||
|
|
|
@ -52,22 +52,20 @@ const BaseLink = Link
|
||||||
* @param {object} props - All component props
|
* @param {object} props - All component props
|
||||||
* @param {JSX.Element} props.children - The component children, will be rendered inside the link
|
* @param {JSX.Element} props.children - The component children, will be rendered inside the link
|
||||||
* @param {string} props.href - The URL to link to
|
* @param {string} props.href - The URL to link to
|
||||||
* @param {string} [props.target = undefined] - An optional link title
|
|
||||||
* @param {string} [props.title = false] - An optional link title
|
* @param {string} [props.title = false] - An optional link title
|
||||||
* @param {React.FC} [Link = undefined] - An optional framework-specific Link component
|
* @param {React.FC} [Link = undefined] - An optional framework-specific Link component
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const SuccessLink = ({
|
export const SuccessLink = ({ children, href, title = false, Link }) =>
|
||||||
children,
|
Link ? (
|
||||||
href,
|
<Link href={href} className={linkClasses} title={title ? title : ''}>
|
||||||
target,
|
<span className="tw:text-success-content tw:hover:text-success-content">{children}</span>
|
||||||
title = false,
|
</Link>
|
||||||
Link,
|
) : (
|
||||||
}) => (
|
<a href={href} className={linkClasses} title={title ? title : ''}>
|
||||||
<a href={href} className={linkClasses} title={title ? title : ''}>
|
<span className="tw:text-success-content tw:hover:text-success-content">{children}</span>
|
||||||
<span className="tw:text-success-content tw:hover:text-success-content">{children}</span>
|
</a>
|
||||||
</a>
|
)
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A link styled as a card
|
* A link styled as a card
|
||||||
|
|
|
@ -10,24 +10,17 @@ import { logoPath } from '@freesewing/config'
|
||||||
* @param {string} [props.stroke = undefined] - Set this explicitly to use a different stroke color
|
* @param {string} [props.stroke = undefined] - Set this explicitly to use a different stroke color
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const FreeSewingLogo = ({ className = 'tw:w-20 tw:h-20', stroke }) => {
|
export const FreeSewingLogo = ({ className = 'tw:w-20 tw:h-20', stroke }) => (
|
||||||
const svgProps = {}
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1 0 25 25" className={className}>
|
||||||
const strokes = { dark: '#000', light: 'var(--p)' }
|
<defs>
|
||||||
|
<path id="react-logo" d={logoPath} />
|
||||||
return (
|
</defs>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1 0 25 25" className={className}>
|
<use
|
||||||
<defs>
|
xlinkHref="#react-logo"
|
||||||
<path id="react-logo" d={logoPath} />
|
fill="none"
|
||||||
</defs>
|
strokeWidth="0.5"
|
||||||
<use
|
style={{ stroke: stroke ? stroke : 'var(--color-base-100)' }}
|
||||||
xlinkHref="#react-logo"
|
/>
|
||||||
fill="none"
|
<use xlinkHref="#react-logo" fill="currentColor" stroke="none" />
|
||||||
strokeWidth="0.5"
|
</svg>
|
||||||
style={{ stroke: stroke ? stroke : 'var(--color-base-100)'}}
|
)
|
||||||
/>
|
|
||||||
<use xlinkHref="#react-logo" fill="currentColor" stroke="none" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,12 @@ import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import React, { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { NoIcon, OkIcon, SaveIcon, RightIcon, WarningIcon } from '@freesewing/react/components/Icon'
|
import { OkIcon, WarningIcon } from '@freesewing/react/components/Icon'
|
||||||
import { EmailInput } from '@freesewing/react/components/Input'
|
import { EmailInput } from '@freesewing/react/components/Input'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
|
||||||
import { MiniTip } from '@freesewing/react/components/Mini'
|
import { MiniTip } from '@freesewing/react/components/Mini'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,7 +29,6 @@ export const NewsletterSignup = ({ Link = false, noP = false, noTitle = false, n
|
||||||
if (!Link) Link = WebLink
|
if (!Link) Link = WebLink
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
const { account, setAccount } = useAccount()
|
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||||
|
|
||||||
|
@ -44,7 +40,7 @@ export const NewsletterSignup = ({ Link = false, noP = false, noTitle = false, n
|
||||||
// Helper method to handle subscription
|
// Helper method to handle subscription
|
||||||
const subscribe = async () => {
|
const subscribe = async () => {
|
||||||
setLoadingStatus([true, 'Contacting backend'])
|
setLoadingStatus([true, 'Contacting backend'])
|
||||||
const [status, body] = unsubscribe
|
const [status] = unsubscribe
|
||||||
? await backend.newsletterStartUnsubscribe(email)
|
? await backend.newsletterStartUnsubscribe(email)
|
||||||
: await backend.newsletterSubscribe(email)
|
: await backend.newsletterSubscribe(email)
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
|
@ -146,7 +142,7 @@ export const NewsletterUnsubscribe = ({ Link = false }) => {
|
||||||
|
|
||||||
// Helper method to handle subscription
|
// Helper method to handle subscription
|
||||||
const unsubscribe = async () => {
|
const unsubscribe = async () => {
|
||||||
const [status, body] = await backend.newsletterUnsubscribe(ehash)
|
const [status] = await backend.newsletterUnsubscribe(ehash)
|
||||||
if (status === 204 || status === 404) setGone(true)
|
if (status === 204 || status === 404) setGone(true)
|
||||||
else setError(true)
|
else setError(true)
|
||||||
}
|
}
|
||||||
|
@ -197,6 +193,4 @@ export const NewsletterUnsubscribe = ({ Link = false }) => {
|
||||||
</MiniTip>
|
</MiniTip>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
return <p>Unsubscribe here {ehash}</p>
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sometimes, you just want a component that does nothing
|
* Sometimes, you just want a component that does nothing
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { linkClasses } from '@freesewing/utils'
|
import { linkClasses } from '@freesewing/utils'
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component to ask people to support FreeSewing financially
|
* A component to ask people to support FreeSewing financially
|
||||||
|
@ -211,7 +210,12 @@ export const Subscribe = ({
|
||||||
</button>
|
</button>
|
||||||
<p className="tw:text-center tw:text-sm tw:text-neutral-content tw:mt-2 tw:opacity-80">
|
<p className="tw:text-center tw:text-sm tw:text-neutral-content tw:mt-2 tw:opacity-80">
|
||||||
Don't have a PayPal account?
|
Don't have a PayPal account?
|
||||||
<a href="https://ko-fi.com/freesewing" target="_BLANK" className={linkClasses}>
|
<a
|
||||||
|
href="https://ko-fi.com/freesewing"
|
||||||
|
target="_BLANK"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={linkClasses}
|
||||||
|
>
|
||||||
<b className="tw:text-neutral-content tw:pl-2">Ko-fi.com/FreeSewing</b>
|
<b className="tw:text-neutral-content tw:pl-2">Ko-fi.com/FreeSewing</b>
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import sanitize from 'html-react-parser'
|
import sanitize from 'html-react-parser'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// Dependencies
|
|
||||||
import { cloudflareImageUrl } from '@freesewing/utils'
|
|
||||||
// Components
|
// Components
|
||||||
import React, { forwardRef } from 'react'
|
import React, { forwardRef } from 'react'
|
||||||
import { Svg as DefaultSvg } from './svg.mjs'
|
import { Svg as DefaultSvg } from './svg.mjs'
|
||||||
|
@ -14,7 +12,6 @@ import { Grid as DefaultGrid } from './grid.mjs'
|
||||||
import { Text as DefaultText, TextOnPath as DefaultTextOnPath } from './text.mjs'
|
import { Text as DefaultText, TextOnPath as DefaultTextOnPath } from './text.mjs'
|
||||||
import { Circle as DefaultCircle } from './circle.mjs'
|
import { Circle as DefaultCircle } from './circle.mjs'
|
||||||
import { getId, getProps, withinPartBounds, translateStrings } from './utils.mjs'
|
import { getId, getProps, withinPartBounds, translateStrings } from './utils.mjs'
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Pattern components that you can override
|
* Default Pattern components that you can override
|
||||||
|
@ -101,6 +98,7 @@ const Pattern = forwardRef((props, ref) => {
|
||||||
</Svg>
|
</Svg>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
Pattern.displayName = 'Pattern'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
// utils
|
// utils
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import React, { forwardRef } from 'react'
|
import React, { forwardRef } from 'react'
|
||||||
import { getId, getProps } from './utils.mjs'
|
import { getId, getProps } from './utils.mjs'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { getId, getProps } from './utils.mjs'
|
import { getId, getProps } from './utils.mjs'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { withinPartBounds } from './utils.mjs'
|
import { withinPartBounds } from './utils.mjs'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { getProps } from './utils.mjs'
|
import { getProps } from './utils.mjs'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef } from 'react'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { translateStrings } from './utils.mjs'
|
import { translateStrings } from './utils.mjs'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A method to generated an ID for an object part of a FreeSewing pattern
|
* A method to generated an ID for an object part of a FreeSewing pattern
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,44 +7,44 @@ import { CloseIcon } from '@freesewing/react/components/Icon'
|
||||||
*/
|
*/
|
||||||
const types = {
|
const types = {
|
||||||
comment: {
|
comment: {
|
||||||
bg: 'tw:bg-success/5',
|
bg: 'tw:bg-success/5',
|
||||||
border: 'tw:border-success',
|
border: 'tw:border-success',
|
||||||
text: 'tw:text-success',
|
text: 'tw:text-success',
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
bg: 'tw:bg-error/5',
|
bg: 'tw:bg-error/5',
|
||||||
border: 'tw:border-error',
|
border: 'tw:border-error',
|
||||||
text: 'tw:text-error',
|
text: 'tw:text-error',
|
||||||
},
|
},
|
||||||
fixme: {
|
fixme: {
|
||||||
bg: 'tw:bg-neutral/5',
|
bg: 'tw:bg-neutral/5',
|
||||||
border: 'tw:border-neutral',
|
border: 'tw:border-neutral',
|
||||||
text: 'tw:text-error',
|
text: 'tw:text-error',
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
bg: 'tw:bg-secondary/5',
|
bg: 'tw:bg-secondary/5',
|
||||||
border: 'tw:border-secondary',
|
border: 'tw:border-secondary',
|
||||||
text: 'tw:text-secondary',
|
text: 'tw:text-secondary',
|
||||||
},
|
},
|
||||||
note: {
|
note: {
|
||||||
bg: 'tw:bg-primary/5',
|
bg: 'tw:bg-primary/5',
|
||||||
border: 'tw:border-primary',
|
border: 'tw:border-primary',
|
||||||
text: 'tw:text-primary',
|
text: 'tw:text-primary',
|
||||||
},
|
},
|
||||||
related: {
|
related: {
|
||||||
bg: 'tw:bg-info/5',
|
bg: 'tw:bg-info/5',
|
||||||
border: 'tw:border-info',
|
border: 'tw:border-info',
|
||||||
text: 'tw:text-info',
|
text: 'tw:text-info',
|
||||||
},
|
},
|
||||||
tip: {
|
tip: {
|
||||||
bg: 'tw:bg-accent/5',
|
bg: 'tw:bg-accent/5',
|
||||||
border: 'tw:border-accent',
|
border: 'tw:border-accent',
|
||||||
text: 'tw:text-accent',
|
text: 'tw:text-accent',
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
bg: 'tw:bg-warning/5',
|
bg: 'tw:bg-warning/5',
|
||||||
border: 'tw:border-warning',
|
border: 'tw:border-warning',
|
||||||
text: 'tw:text-warning',
|
text: 'tw:text-warning',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ export const Popout = ({
|
||||||
compact = false,
|
compact = false,
|
||||||
dense = false,
|
dense = false,
|
||||||
hideable = false,
|
hideable = false,
|
||||||
type = "note",
|
type = 'note',
|
||||||
title = false,
|
title = false,
|
||||||
}) => {
|
}) => {
|
||||||
// Make this hideable/dismissable
|
// Make this hideable/dismissable
|
||||||
|
@ -76,15 +76,17 @@ export const Popout = ({
|
||||||
|
|
||||||
if (hide) return null
|
if (hide) return null
|
||||||
|
|
||||||
return compact
|
return compact ? (
|
||||||
? <CompactPopout {...{ by, compact, dense, hideable, type, title, setHide }}>{children}</CompactPopout>
|
<CompactPopout {...{ by, compact, dense, hideable, type, title, setHide }}>
|
||||||
: <RegularPopout {...{ by, hideable, type, title, setHide }}>{children}</RegularPopout>
|
{children}
|
||||||
|
</CompactPopout>
|
||||||
|
) : (
|
||||||
|
<RegularPopout {...{ by, hideable, type, title, setHide }}>{children}</RegularPopout>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegularPopout = ({ by, children, compact, hideable, type, title, setHide }) => (
|
const RegularPopout = ({ by, children, hideable, type, title, setHide }) => (
|
||||||
<div
|
<div className={`tw:relative tw:my-8 tw:-ml-4 tw:-mr-4 tw:sm:ml-0 tw:sm:mr-0 ${types[type].bg}`}>
|
||||||
className={`tw:relative tw:my-8 tw:-ml-4 tw:-mr-4 tw:sm:ml-0 tw:sm:mr-0 ${types[type].bg}`}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
tw:border-y-4 tw:border-x-0 tw:sm:border-0 tw:sm:border-l-4 tw:px-6 tw:sm:px-8 tw:py-4 tw:sm:py-2
|
tw:border-y-4 tw:border-x-0 tw:sm:border-0 tw:sm:border-l-4 tw:px-6 tw:sm:px-8 tw:py-4 tw:sm:py-2
|
||||||
|
@ -98,7 +100,6 @@ const RegularPopout = ({ by, children, compact, hideable, type, title, setHide }
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const CompactPopout = ({ by, children, compact, dense, hideable, type, title, setHide }) => (
|
const CompactPopout = ({ by, children, compact, dense, hideable, type, title, setHide }) => (
|
||||||
|
@ -122,20 +123,27 @@ const CompactPopout = ({ by, children, compact, dense, hideable, type, title, se
|
||||||
)
|
)
|
||||||
|
|
||||||
const PopoutTitle = ({ by, compact, hideable, setHide, title, type }) => (
|
const PopoutTitle = ({ by, compact, hideable, setHide, title, type }) => (
|
||||||
<div
|
<div className={`tw:font-bold tw:flex tw:flex-row tw:gap-1 tw:items-end tw:justify-between`}>
|
||||||
className={`tw:font-bold tw:flex tw:flex-row tw:gap-1 tw:items-end tw:justify-between`}
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<span className={`tw:font-bold tw:uppercase ${types[type].text}`}>
|
<span className={`tw:font-bold tw:uppercase ${types[type].text}`}>
|
||||||
{title ? title : types[type].title ? types[type].title : type.toUpperCase()}
|
{title ? title : types[type].title ? types[type].title : type.toUpperCase()}
|
||||||
{compact ? <span className="tw:px-2">|</span> : null}
|
{compact ? <span className="tw:px-2">|</span> : null}
|
||||||
</span>
|
</span>
|
||||||
{(type === 'comment' && by) && <span className={`tw:font-normal tw:text-base tw:pr-2 ${types[type].text}`}> by <b>{by}</b></span>}
|
{type === 'comment' && by && (
|
||||||
|
<span className={`tw:font-normal tw:text-base tw:pr-2 ${types[type].text}`}>
|
||||||
|
{' '}
|
||||||
|
by <b>{by}</b>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{hideable && (
|
{hideable && (
|
||||||
<button onClick={() => setHide(true)} className={`${types[type].text} tw:hover:text-neutral tw:hover:cursor-pointer`} title="Close">
|
<button
|
||||||
|
onClick={() => setHide(true)}
|
||||||
|
className={`${types[type].text} tw:hover:text-neutral tw:hover:cursor-pointer`}
|
||||||
|
title="Close"
|
||||||
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Dependencies
|
// Dependencies
|
||||||
import { linkClasses, cloudflareImageUrl, getSearchParam } from '@freesewing/utils'
|
import { cloudflareImageUrl, getSearchParam } from '@freesewing/utils'
|
||||||
// Context
|
// Context
|
||||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
// Hooks
|
// Hooks
|
||||||
|
@ -9,7 +9,6 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
// Components
|
// Components
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { NoIcon, OkIcon, SaveIcon, RightIcon, WarningIcon } from '@freesewing/react/components/Icon'
|
|
||||||
import { MiniWarning } from '@freesewing/react/components/Mini'
|
import { MiniWarning } from '@freesewing/react/components/Mini'
|
||||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||||
import Markdown from 'react-markdown'
|
import Markdown from 'react-markdown'
|
||||||
|
@ -40,12 +39,7 @@ export const OwnProfile = (props) => {
|
||||||
* @param {number} [props.uid = false] - The user ID for which to show the profile
|
* @param {number} [props.uid = false] - The user ID for which to show the profile
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const UserProfile = ({
|
export const UserProfile = ({ Link = false, setTitle = false, uid = false, fromUrl = false }) => {
|
||||||
Link = false,
|
|
||||||
setTitle = false,
|
|
||||||
uid = false,
|
|
||||||
fromUrl = false,
|
|
||||||
}) => {
|
|
||||||
if (!uid && !fromUrl)
|
if (!uid && !fromUrl)
|
||||||
return (
|
return (
|
||||||
<MiniWarning>
|
<MiniWarning>
|
||||||
|
@ -69,7 +63,7 @@ export const UserProfile = ({
|
||||||
const urlId = getSearchParam(fromUrl)
|
const urlId = getSearchParam(fromUrl)
|
||||||
if (urlId && urlId !== ruid) setRuid(urlId)
|
if (urlId && urlId !== ruid) setRuid(urlId)
|
||||||
}
|
}
|
||||||
if (ruid) loadProfileData(ruid, backend, setData)
|
if (ruid) loadProfileData(ruid, backend, setData, setTitle)
|
||||||
}, [uid, fromUrl, ruid])
|
}, [uid, fromUrl, ruid])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -121,7 +115,10 @@ export const Avatar = ({ ihash }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadProfileData(uid, backend, setData) {
|
async function loadProfileData(uid, backend, setData, setTitle = false) {
|
||||||
const [status, body] = await backend.getUserProfile(uid)
|
const [status, body] = await backend.getUserProfile(uid)
|
||||||
if (status === 200 && body.result === 'success' && body.profile) setData(body.profile)
|
if (status === 200 && body.result === 'success' && body.profile) {
|
||||||
|
setData(body.profile)
|
||||||
|
if (typeof setTitle === 'function' && body.profile.username) setTitle(body.profile.username)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Link as DefaultLink } from '@freesewing/react/components/Link'
|
||||||
import { LockIcon, PlusIcon } from '@freesewing/react/components/Icon'
|
import { LockIcon, PlusIcon } from '@freesewing/react/components/Icon'
|
||||||
import { Spinner } from '@freesewing/react/components/Spinner'
|
import { Spinner } from '@freesewing/react/components/Spinner'
|
||||||
import { Popout } from '@freesewing/react/components/Popout'
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
import { H1, H2, H3 } from '@freesewing/react/components/Heading'
|
import { H3 } from '@freesewing/react/components/Heading'
|
||||||
import { Consent } from '@freesewing/react/components/Account'
|
import { Consent } from '@freesewing/react/components/Account'
|
||||||
|
|
||||||
const Wrap = ({ children }) => (
|
const Wrap = ({ children }) => (
|
||||||
|
@ -104,7 +104,7 @@ const AccountStatusUnknown = ({ banner }) => (
|
||||||
</Wrap>
|
</Wrap>
|
||||||
)
|
)
|
||||||
|
|
||||||
const RoleLacking = ({ t, requiredRole, role, banner }) => (
|
const RoleLacking = ({ requiredRole, role, banner }) => (
|
||||||
<Wrap>
|
<Wrap>
|
||||||
{banner}
|
{banner}
|
||||||
<H3>You lack the required role to access this content</H3>
|
<H3>You lack the required role to access this content</H3>
|
||||||
|
@ -174,7 +174,7 @@ const ConsentLacking = ({ banner, refresh }) => {
|
||||||
* @param {JSX.Element} props.children - The component children, will be rendered if props.js is not set
|
* @param {JSX.Element} props.children - The component children, will be rendered if props.js is not set
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const RoleBlock = ({ children, role = "admin", Link = false }) => {
|
export const RoleBlock = ({ children, role = 'admin', Link = false }) => {
|
||||||
if (!Link) Link = DefaultLink
|
if (!Link) Link = DefaultLink
|
||||||
const requiredRole = role
|
const requiredRole = role
|
||||||
|
|
||||||
|
@ -275,7 +275,6 @@ export const UserVisitorContent = ({ userContent = null, visitorContent = null }
|
||||||
|
|
||||||
const [ready, setReady] = useState(false)
|
const [ready, setReady] = useState(false)
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
const [refreshCount, setRefreshCount] = useState(0)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Avoid hydration errors
|
* Avoid hydration errors
|
||||||
|
@ -300,14 +299,15 @@ export const UserVisitorContent = ({ userContent = null, visitorContent = null }
|
||||||
if (!account.bestBefore || account.bestBefore < Date.now()) verifyUser()
|
if (!account.bestBefore || account.bestBefore < Date.now()) verifyUser()
|
||||||
}
|
}
|
||||||
setReady(true)
|
setReady(true)
|
||||||
}, [refreshCount])
|
}, [])
|
||||||
|
|
||||||
const refresh = () => {
|
|
||||||
setRefreshCount(refreshCount + 1)
|
|
||||||
setError(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ready) return <Spinner />
|
if (!ready) return <Spinner />
|
||||||
|
if (error)
|
||||||
|
return (
|
||||||
|
<Popout type="error" title="Something went wrong" compact>
|
||||||
|
This is unexpected. You may want to report this.
|
||||||
|
</Popout>
|
||||||
|
)
|
||||||
|
|
||||||
return token && account.username ? userContent : visitorContent
|
return token && account.username ? userContent : visitorContent
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
// Utils
|
// Utils
|
||||||
import {
|
import { horFlexClasses, horFlexClassesNoSm, getSearchParam, navigate } from '@freesewing/utils'
|
||||||
linkClasses,
|
|
||||||
horFlexClasses,
|
|
||||||
horFlexClassesNoSm,
|
|
||||||
capitalize,
|
|
||||||
getSearchParam,
|
|
||||||
} from '@freesewing/utils'
|
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
// Hooks
|
// Hooks
|
||||||
|
@ -21,13 +15,11 @@ import {
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
WarningIcon,
|
WarningIcon,
|
||||||
GoogleIcon,
|
|
||||||
GitHubIcon,
|
|
||||||
FreeSewingIcon,
|
FreeSewingIcon,
|
||||||
UserIcon,
|
UserIcon,
|
||||||
} from '@freesewing/react/components/Icon'
|
} from '@freesewing/react/components/Icon'
|
||||||
import { MfaInput, StringInput, PasswordInput } from '@freesewing/react/components/Input'
|
import { MfaInput, StringInput, PasswordInput } from '@freesewing/react/components/Input'
|
||||||
import { H1, H2, H3, H4 } from '@freesewing/react/components/Heading'
|
import { H1 } from '@freesewing/react/components/Heading'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
@ -44,7 +36,7 @@ import { H1, H2, H3, H4 } from '@freesewing/react/components/Heading'
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const SignIn = ({ onSuccess = false, silent = false }) => {
|
export const SignIn = ({ onSuccess = false, silent = false }) => {
|
||||||
const { account, setAccount, setToken, seenUser, setSeenUser } = useAccount()
|
const { setAccount, setToken, seenUser, setSeenUser } = useAccount()
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||||
|
|
||||||
|
@ -149,15 +141,6 @@ export const SignIn = ({ onSuccess = false, silent = false }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initOauth = async (provider) => {
|
|
||||||
setLoadingStatus([true, 'Contacting the FreeSewing backend'])
|
|
||||||
const [status, body] = await backend.oauthInit(provider.toLowerCase())
|
|
||||||
if (status === 200 && body.result === 'success') {
|
|
||||||
setLoadingStatus([true, `Contacting ${capitalize(provider)}`])
|
|
||||||
window.location.href = body.authUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const btnClasses = `tw:daisy-btn tw:capitalize tw:w-full tw:mt-4 ${
|
const btnClasses = `tw:daisy-btn tw:capitalize tw:w-full tw:mt-4 ${
|
||||||
signInFailed ? 'tw:daisy-btn-warning' : 'tw:daisy-btn-primary'
|
signInFailed ? 'tw:daisy-btn-warning' : 'tw:daisy-btn-primary'
|
||||||
} tw:transition-colors tw:ease-in-out tw:duration-300 ${horFlexClassesNoSm}`
|
} tw:transition-colors tw:ease-in-out tw:duration-300 ${horFlexClassesNoSm}`
|
||||||
|
@ -412,7 +395,12 @@ export const SignInConfirmation = ({ onSuccess = false }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short-circuit errors
|
// Short-circuit errors
|
||||||
if (error === 'noId') return <Popout type="error" title="Invalid Sign In URL">You seem to have arrived on this page in a way that is not supported</Popout>
|
if (error === 'noId')
|
||||||
|
return (
|
||||||
|
<Popout type="error" title="Invalid Sign In URL">
|
||||||
|
You seem to have arrived on this page in a way that is not supported
|
||||||
|
</Popout>
|
||||||
|
)
|
||||||
if (error && mfa)
|
if (error && mfa)
|
||||||
return error === 'signInFailed' ? (
|
return error === 'signInFailed' ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { validateEmail, validateTld, getSearchParam } from '@freesewing/utils'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
|
@ -12,16 +11,7 @@ import { ModalContext } from '@freesewing/react/context/Modal'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Link } from '@freesewing/react/components/Link'
|
import { Link } from '@freesewing/react/components/Link'
|
||||||
import {
|
import { LeftIcon, HelpIcon, KeyIcon, EmailIcon } from '@freesewing/react/components/Icon'
|
||||||
LeftIcon,
|
|
||||||
RightIcon,
|
|
||||||
HelpIcon,
|
|
||||||
GoogleIcon,
|
|
||||||
GitHubIcon,
|
|
||||||
KeyIcon,
|
|
||||||
EmailIcon,
|
|
||||||
DownIcon,
|
|
||||||
} from '@freesewing/react/components/Icon'
|
|
||||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||||
import { EmailInput } from '@freesewing/react/components/Input'
|
import { EmailInput } from '@freesewing/react/components/Input'
|
||||||
import { IconButton } from '@freesewing/react/components/Button'
|
import { IconButton } from '@freesewing/react/components/Button'
|
||||||
|
@ -42,7 +32,6 @@ export const SignUp = ({ embed = false }) => {
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
const [emailValid, setEmailValid] = useState(false)
|
const [emailValid, setEmailValid] = useState(false)
|
||||||
const [result, setResult] = useState(false)
|
const [result, setResult] = useState(false)
|
||||||
const [showAll, setShowAll] = useState(false)
|
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
const backend = useBackend()
|
const backend = useBackend()
|
||||||
|
@ -93,14 +82,6 @@ export const SignUp = ({ embed = false }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initOauth = async (provider) => {
|
|
||||||
setLoadingStatus([true, 'Contacting the backend'])
|
|
||||||
const [status, body] = await backend.oauthInit(provider.toLowerCase())
|
|
||||||
if (status === 200 && body.result === 'success') {
|
|
||||||
setLoadingStatus([true, `Contacting ${provider}`])
|
|
||||||
window.location.href = body.authUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const Heading = embed
|
const Heading = embed
|
||||||
? ({ children }) => <h2 className="tw:text-inherit">{children}</h2>
|
? ({ children }) => <h2 className="tw:text-inherit">{children}</h2>
|
||||||
: ({ children }) => <h1 className="tw:text-inherit">{children}</h1>
|
: ({ children }) => <h1 className="tw:text-inherit">{children}</h1>
|
||||||
|
@ -200,10 +181,9 @@ export const SignUp = ({ embed = false }) => {
|
||||||
*
|
*
|
||||||
* @component
|
* @component
|
||||||
* @param {object} props - All component props
|
* @param {object} props - All component props
|
||||||
* @param {function} [props.onSuccess = false] - A method to run when the sign in is successful
|
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const SignUpConfirmation = ({ onSuccess = false }) => {
|
export const SignUpConfirmation = () => {
|
||||||
// State
|
// State
|
||||||
const [id, setId] = useState()
|
const [id, setId] = useState()
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
|
@ -219,7 +199,12 @@ export const SignUpConfirmation = ({ onSuccess = false }) => {
|
||||||
}, [id, check])
|
}, [id, check])
|
||||||
|
|
||||||
// Short-circuit errors
|
// Short-circuit errors
|
||||||
if (error === 'noId') return <Popout type="error" title="Invalid Sign Up URL">You seem to have arrived on this page in a way that is not supported</Popout>
|
if (error === 'noId')
|
||||||
|
return (
|
||||||
|
<Popout type="error" title="Invalid Sign Up URL">
|
||||||
|
You seem to have arrived on this page in a way that is not supported
|
||||||
|
</Popout>
|
||||||
|
)
|
||||||
// If we do not (yet) have the data, show a loader
|
// If we do not (yet) have the data, show a loader
|
||||||
if (!id || !check)
|
if (!id || !check)
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -7,11 +7,7 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
import { Spinner } from '@freesewing/react/components/Spinner'
|
import { Spinner } from '@freesewing/react/components/Spinner'
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { ChartWrapper } from '@freesewing/react/components/Echart'
|
import { ChartWrapper } from '@freesewing/react/components/Echart'
|
||||||
|
import { Popout } from '@freesewing/react/components/Popout'
|
||||||
const meta = {
|
|
||||||
title: 'FreeSewing by numbers',
|
|
||||||
description: 'Some high-level numbers about Freesewing',
|
|
||||||
}
|
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
@ -104,6 +100,13 @@ export const Stats = ({ Link = false }) => {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return (
|
||||||
|
<Popout type="error" title="Something went wrong" compact>
|
||||||
|
This is unexpected. You may want to report this.
|
||||||
|
</Popout>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="tw:max-w-7xl tw:mx-auto tw:my-12 tw:px-4">
|
<div className="tw:max-w-7xl tw:mx-auto tw:my-12 tw:px-4">
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const Tabs = ({ tabs = '', active = 0, children, withModal = false }) =>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tw:border tw:border-base-300 tw:rounded-lg tw:pt-2">
|
<div className="tw:border tw:border-base-300 tw:rounded-lg tw:pt-2">
|
||||||
<div role="tablist" className="tw:daisy-tabs tw:daisy-tabs-border" role="tablist">
|
<div className="tw:daisy-tabs tw:daisy-tabs-border" role="tablist">
|
||||||
{tablist.map((title, tabId) => {
|
{tablist.map((title, tabId) => {
|
||||||
const btnClasses = `tw:text-lg tw:font-bold tw:capitalize tw:daisy-tab tw:h-auto tw:grow tw:py-1 ${
|
const btnClasses = `tw:text-lg tw:font-bold tw:capitalize tw:daisy-tab tw:h-auto tw:grow tw:py-1 ${
|
||||||
activeTab === tabId ? 'tw:daisy-tab-active' : ''
|
activeTab === tabId ? 'tw:daisy-tab-active' : ''
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import React from 'react'
|
||||||
import { shortUuid } from '@freesewing/utils'
|
import { shortUuid } from '@freesewing/utils'
|
||||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||||
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
||||||
|
@ -14,9 +14,7 @@ import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
||||||
* @param {string} [props.label = false] - An optional label to pass to the CopyToClipboardButton
|
* @param {string} [props.label = false] - An optional label to pass to the CopyToClipboardButton
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
*/
|
*/
|
||||||
export const Uuid = ({ uuid, href = false, label = "UUID", Link = false }) => {
|
export const Uuid = ({ uuid, href = false, label = 'UUID', Link = false }) => {
|
||||||
const [full, setFull] = useState()
|
|
||||||
const short = shortUuid(uuid)
|
|
||||||
if (!Link) Link = WebLink
|
if (!Link) Link = WebLink
|
||||||
|
|
||||||
if (href === false)
|
if (href === false)
|
||||||
|
|
|
@ -81,3 +81,5 @@ export const Xray = forwardRef((props, ref) => {
|
||||||
</Svg>
|
</Svg>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Xray.displayName = 'Xray'
|
||||||
|
|
|
@ -96,9 +96,8 @@ export const PathXray = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const PathXrayInfo = ({ path, pathName, stackName, part }) => {
|
const PathXrayInfo = ({ path, pathName, stackName }) => {
|
||||||
const [rounded, setRounded] = useState(true)
|
const [rounded, setRounded] = useState(true)
|
||||||
const log = (val) => console.log(val)
|
|
||||||
const rounder = rounded ? round : (val) => val
|
const rounder = rounded ? round : (val) => val
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const PointXray = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const PointXrayInfo = ({ point, pointName, stackName, part }) => {
|
const PointXrayInfo = ({ point, pointName, stackName }) => {
|
||||||
const [rounded, setRounded] = useState(true)
|
const [rounded, setRounded] = useState(true)
|
||||||
const rounder = rounded ? round : (val) => val
|
const rounder = rounded ? round : (val) => val
|
||||||
|
|
||||||
|
|
30
packages/react/eslint.config.mjs
Normal file
30
packages/react/eslint.config.mjs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import pluginReact from 'eslint-plugin-react'
|
||||||
|
import { defineConfig } from 'eslint/config'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,mjs,cjs,jsx}'],
|
||||||
|
plugins: { js },
|
||||||
|
extends: ['js/recommended'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,mjs,cjs,jsx}'],
|
||||||
|
languageOptions: { globals: { ...globals.browser, ...globals.node } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plugins: pluginReact.configs.flat.recommended.plugins,
|
||||||
|
languageOptions: pluginReact.configs.flat.recommended.languageOptions,
|
||||||
|
rules: {
|
||||||
|
...pluginReact.configs.flat.recommended.rules,
|
||||||
|
// Maybe one day someone wants to do this, but we use jsdoc for now
|
||||||
|
'react/prop-types': 0,
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
|
@ -1,8 +1,7 @@
|
||||||
// Context
|
// Context
|
||||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import React, { useState, useContext } from 'react'
|
import { useState, useContext } from 'react'
|
||||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import React from 'react'
|
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { atomWithHash } from 'jotai-location'
|
import { atomWithHash } from 'jotai-location'
|
||||||
|
|
||||||
const filterAtom = atomWithHash('filter', { })
|
const filterAtom = atomWithHash('filter', {})
|
||||||
|
|
||||||
export const useFilter = () => useAtom(filterAtom)
|
export const useFilter = () => useAtom(filterAtom)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
export const useSelection = (items) => {
|
export const useSelection = (items) => {
|
||||||
const [selection, setSelection] = useState({})
|
const [selection, setSelection] = useState({})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import set from 'lodash/set.js'
|
import set from 'lodash/set.js'
|
||||||
import unset from 'lodash/unset.js'
|
import unset from 'lodash/unset.js'
|
||||||
|
|
||||||
|
|
|
@ -68,9 +68,11 @@ async function withoutBody(method = 'GET', url, headers = {}, raw = false, log =
|
||||||
try {
|
try {
|
||||||
body = raw ? await response.text() : await response.json()
|
body = raw ? await response.text() : await response.json()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
try {
|
try {
|
||||||
body = await response.text()
|
body = await response.text()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
body = false
|
body = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,9 +127,11 @@ async function withBody(method = 'POST', url, data, headers, raw = false, log =
|
||||||
try {
|
try {
|
||||||
body = raw ? await response.text() : await response.json()
|
body = raw ? await response.text() : await response.json()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
try {
|
try {
|
||||||
body = await response.text()
|
body = await response.text()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
body = false
|
body = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
const html = (
|
|
||||||
<>
|
|
||||||
<button className="tw:btn-ghost">
|
|
||||||
<span className="tw:px-1 tw:text-sm tw:font-medium tw:whitespace-nowrap tw:border-2 tw:border-solid tw:rounded-l-lg tw:text-primary-content tw:bg-primary tw:border-primary ">
|
|
||||||
Code
|
|
||||||
</span>
|
|
||||||
<span className="tw:px-1 tw:text-sm tw:font-medium tw:whitespace-nowrap tw:border-2 tw:border-solid tw:rounded-r-lg tw:text-primary tw:bg-base-100 tw:border-primary ">
|
|
||||||
Joost De Cock
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)
|
|
Loading…
Add table
Add a link
Reference in a new issue