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": {
|
||||
"@commitlint/cli": "^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",
|
||||
"all-contributors-cli": "^6.26.1",
|
||||
"axios": "^1.5.1",
|
||||
"chalk": "^4.1.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^8.23.1",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-jsonc": "^2.4.0",
|
||||
"eslint-plugin-markdown": "^5.0.0",
|
||||
"eslint-plugin-mongo": "^1.0.5",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-yaml": "^0.5.0",
|
||||
"execa": "^9.3.1",
|
||||
"globals": "^16.2.0",
|
||||
"husky": "^9.0.10",
|
||||
"js-yaml": "^4.0.0",
|
||||
"lerna": "^8.0.0",
|
||||
|
|
|
@ -1,49 +1,34 @@
|
|||
// Dependencies
|
||||
import { DateTime } from 'luxon'
|
||||
import orderBy from 'lodash/orderBy.js'
|
||||
import { capitalize, shortDate } from '@freesewing/utils'
|
||||
import { shortDate } from '@freesewing/utils'
|
||||
import { apikeyLevels } from '@freesewing/config'
|
||||
// Context
|
||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useEffect, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
||||
|
||||
// Components
|
||||
import { TableWrapper } from '@freesewing/react/components/Table'
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import {
|
||||
BoolNoIcon,
|
||||
BoolYesIcon,
|
||||
PlusIcon,
|
||||
RightIcon,
|
||||
TrashIcon,
|
||||
} from '@freesewing/react/components/Icon'
|
||||
import { PlusIcon, RightIcon, TrashIcon } from '@freesewing/react/components/Icon'
|
||||
import { Uuid } from '@freesewing/react/components/Uuid'
|
||||
import { Popout } from '@freesewing/react/components/Popout'
|
||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
import { NumberCircle } from '@freesewing/react/components/Number'
|
||||
import { StringInput, Fieldset, ListInput } from '@freesewing/react/components/Input'
|
||||
import { DisplayRow } from './shared.mjs'
|
||||
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
||||
import { TimeAgo, TimeToGo } from '@freesewing/react/components/Time'
|
||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||
|
||||
const t = (input) => {
|
||||
console.log('t called', input)
|
||||
return input
|
||||
}
|
||||
|
||||
const fields = {
|
||||
id: 'Key',
|
||||
name: 'Name',
|
||||
calls: 'Calls',
|
||||
level: 'Level',
|
||||
level: 'Level',
|
||||
createdAt: 'Created',
|
||||
expiresAt: 'Expires',
|
||||
}
|
||||
|
@ -242,13 +227,6 @@ const NewApikey = ({ onCreate = false }) => {
|
|||
} else setLoadingStatus([true, 'An error occured. Please report this', true, false])
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
setApikey(false)
|
||||
setGenerate(false)
|
||||
setName('')
|
||||
setLevel(1)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tw:w-full">
|
||||
<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 { ModalContext } from '@freesewing/react/context/Modal'
|
||||
// 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 { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
import { StringInput } from '@freesewing/react/components/Input'
|
||||
|
@ -33,7 +33,7 @@ const types = {
|
|||
export const Bookmarks = () => {
|
||||
// Hooks & Context
|
||||
const backend = useBackend()
|
||||
const { setModal, clearModal } = useContext(ModalContext)
|
||||
const { setModal } = useContext(ModalContext)
|
||||
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
|
||||
|
||||
// State
|
||||
|
@ -194,7 +194,7 @@ const NewBookmark = ({ onCreated = false }) => {
|
|||
// This method will create the bookmark
|
||||
const createBookmark = async () => {
|
||||
setLoadingStatus([true, 'Processing update'])
|
||||
const [status, body] = await backend.createBookmark({
|
||||
const [status] = await backend.createBookmark({
|
||||
title,
|
||||
url,
|
||||
type: 'custom',
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
// Dependencies
|
||||
import { welcomeSteps } from './shared.mjs'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
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 { IconButton } from '@freesewing/react/components/Button'
|
||||
import { WelcomeIcons } from './shared.mjs'
|
||||
|
|
|
@ -1,36 +1,15 @@
|
|||
// Dependencies
|
||||
import { welcomeSteps } from './shared.mjs'
|
||||
import { linkClasses, navigate } from '@freesewing/utils'
|
||||
|
||||
import { navigate } from '@freesewing/utils'
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
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'
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -79,7 +58,7 @@ export const Consent = ({ signUp = false, Link = false, title = false }) => {
|
|||
// Helper method to remove the account
|
||||
const removeAccount = async () => {
|
||||
setLoadingStatus([true, 'One moment please'])
|
||||
const [status, body] = await backend.removeAccount()
|
||||
const [status] = await backend.removeAccount()
|
||||
if (status === 200) {
|
||||
setLoadingStatus([true, 'All done, farewell', true, true])
|
||||
setToken(null)
|
||||
|
|
|
@ -1,39 +1,16 @@
|
|||
// Dependencies
|
||||
import { welcomeSteps } from './shared.mjs'
|
||||
import { controlDesc } from '@freesewing/config'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
import React from 'react'
|
||||
import { useControl } from '@freesewing/react/hooks/useControl'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { RightIcon, NoIcon, OkIcon, SaveIcon } from '@freesewing/react/components/Icon'
|
||||
import { RightIcon } from '@freesewing/react/components/Icon'
|
||||
import { ListInput } from '@freesewing/react/components/Input'
|
||||
import { ControlScore } from '@freesewing/react/components/Control'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
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
|
||||
*
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
// Dependencies
|
||||
import { welcomeSteps } from './shared.mjs'
|
||||
import { validateEmail, validateTld, getSearchParam } from '@freesewing/utils'
|
||||
|
||||
import { validateEmail, validateTld, getSearchParam, navigate } from '@freesewing/utils'
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext, useEffect } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { SaveIcon } from '@freesewing/react/components/Icon'
|
||||
|
@ -22,11 +18,10 @@ import { Spinner } from '@freesewing/react/components/Spinner'
|
|||
*
|
||||
* @component
|
||||
* @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
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const Email = ({ welcome = false, Link = false }) => {
|
||||
export const Email = ({ Link = false }) => {
|
||||
if (!Link) Link = WebLink
|
||||
|
||||
// Hooks
|
||||
|
@ -104,7 +99,7 @@ export const EmailChangeConfirmation = ({ onSuccess = false }) => {
|
|||
const [check, setCheck] = useState()
|
||||
|
||||
// Hooks
|
||||
const { setAccount, setToken } = useAccount()
|
||||
const { setAccount } = useAccount()
|
||||
const backend = useBackend()
|
||||
|
||||
// Context
|
||||
|
@ -134,7 +129,7 @@ export const EmailChangeConfirmation = ({ onSuccess = false }) => {
|
|||
})
|
||||
|
||||
// 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 (body.error) return setError(body.error)
|
||||
return setError(true)
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { DownloadIcon } from '@freesewing/react/components/Icon'
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
// Hooks
|
||||
import React, { useState } from 'react'
|
||||
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
|
||||
* @param {object} props - All component props
|
||||
* @param {React.Component} props.Link - A framework specific Link component for client-side routing
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const UserId = ({ Link = false }) => {
|
||||
if (!Link) Link = WebLink
|
||||
|
||||
export const UserId = () => {
|
||||
// Hooks
|
||||
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
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// 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 { Popout } from '@freesewing/react/components/Popout'
|
||||
import { Yaml } from '@freesewing/react/components/Yaml'
|
||||
|
@ -41,7 +39,7 @@ export const ImportSet = () => {
|
|||
if (set.measurements || set.measies) {
|
||||
const name = set.name || 'J. Doe'
|
||||
setLoadingStatus([true, `Importing ${name}`])
|
||||
const [status, body] = await backend.createSet({
|
||||
const [status] = await backend.createSet({
|
||||
name: set.name || 'J. Doe',
|
||||
units: set.units || 'metric',
|
||||
notes: set.notes || '',
|
||||
|
|
|
@ -93,8 +93,6 @@ const titles = {
|
|||
|
||||
const YesNo = ({ check }) => (check ? <BoolYesIcon /> : <BoolNoIcon />)
|
||||
|
||||
const t = (input) => input
|
||||
|
||||
/**
|
||||
* A component to manage the user's Instagram handle in their account data
|
||||
*
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
// Dependencies
|
||||
import { horFlexClasses } from '@freesewing/utils'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { NoIcon, LockIcon } from '@freesewing/react/components/Icon'
|
||||
import { PasswordInput } from '@freesewing/react/components/Input'
|
||||
import { Popout } from '@freesewing/react/components/Popout'
|
||||
import { NumberCircle } from '@freesewing/react/components/Number'
|
||||
import { CopyToClipboardButton } from '@freesewing/react/components/Button'
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
// Dependencies
|
||||
import { welcomeSteps } from './shared.mjs'
|
||||
import { linkClasses } from '@freesewing/utils'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
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 { Popout } from '@freesewing/react/components/Popout'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
// Dependencies
|
||||
import { welcomeSteps } from './shared.mjs'
|
||||
import { horFlexClasses } from '@freesewing/utils'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
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
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const Password = ({ welcome = false, Link = false }) => {
|
||||
export const Password = ({ Link = false }) => {
|
||||
if (!Link) Link = WebLink
|
||||
// Hooks
|
||||
const backend = useBackend()
|
||||
|
|
|
@ -1,25 +1,13 @@
|
|||
// Dependencies
|
||||
import orderBy from 'lodash/orderBy.js'
|
||||
import {
|
||||
cloudflareImageUrl,
|
||||
capitalize,
|
||||
shortDate,
|
||||
horFlexClasses,
|
||||
newPatternUrl,
|
||||
patternUrlFromState,
|
||||
} from '@freesewing/utils'
|
||||
import { cloudflareImageUrl, horFlexClasses, patternUrlFromState } from '@freesewing/utils'
|
||||
import { urls, control as controlConfig } from '@freesewing/config'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useEffect, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
||||
|
||||
// Components
|
||||
import Markdown from 'react-markdown'
|
||||
import {
|
||||
|
@ -28,7 +16,7 @@ import {
|
|||
PassiveImageInput,
|
||||
ListInput,
|
||||
} 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 {
|
||||
BoolNoIcon,
|
||||
BoolYesIcon,
|
||||
|
@ -42,7 +30,6 @@ import {
|
|||
ResetIcon,
|
||||
UploadIcon,
|
||||
} from '@freesewing/react/components/Icon'
|
||||
import { DisplayRow } from './shared.mjs'
|
||||
import { TimeAgo } from '@freesewing/react/components/Time'
|
||||
import { Popout } from '@freesewing/react/components/Popout'
|
||||
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
|
||||
*/
|
||||
const PatternHeader = ({
|
||||
pattern,
|
||||
Link,
|
||||
account,
|
||||
setModal,
|
||||
setEdit,
|
||||
togglePublic,
|
||||
save,
|
||||
clone,
|
||||
}) => (
|
||||
const PatternHeader = ({ pattern, Link, account, setModal, setEdit, togglePublic, clone }) => (
|
||||
<>
|
||||
<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">
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// Dependencies
|
||||
import orderBy from 'lodash/orderBy.js'
|
||||
import { capitalize, shortDate } from '@freesewing/utils'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useEffect, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
import { useSelection } from '@freesewing/react/hooks/useSelection'
|
||||
|
||||
// Components
|
||||
import { TableWrapper } from '@freesewing/react/components/Table'
|
||||
import { PatternCard } from '@freesewing/react/components/Account'
|
||||
|
@ -23,11 +19,6 @@ import {
|
|||
TrashIcon,
|
||||
} 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
|
||||
*
|
||||
|
@ -97,13 +88,15 @@ export const Patterns = ({ Link = false }) => {
|
|||
onClick={removeSelectedPatterns}
|
||||
disabled={count < 1}
|
||||
>
|
||||
<TrashIcon /> {count} {t('patterns')}
|
||||
<TrashIcon /> {count} Patterns
|
||||
</button>
|
||||
<Link
|
||||
className="tw:daisy-btn tw:daisy-btn-primary tw:capitalize tw:w-full tw:md:w-auto tw:hover:text-primary-content"
|
||||
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>
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { ReloadIcon } from '@freesewing/react/components/Icon'
|
||||
import { Popout } from '@freesewing/react/components/Popout'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,16 +3,12 @@ import { navigate } from '@freesewing/utils'
|
|||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
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 { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
|
||||
|
|
|
@ -6,14 +6,13 @@ import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
|||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { BackIcon, NoIcon } from '@freesewing/react/components/Icon'
|
||||
import { Popout } from '@freesewing/react/components/Popout'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
|
||||
|
@ -39,7 +38,7 @@ export const Restrict = ({ Link = false }) => {
|
|||
// Helper method to restrict the account
|
||||
const restrictAccount = async () => {
|
||||
setLoadingStatus([true, 'Talking to the backend'])
|
||||
const [status, body] = await backend.restrictAccount()
|
||||
const [status] = await backend.restrictAccount()
|
||||
if (status === 200) {
|
||||
setLoadingStatus([true, 'Done. Consider yourself restrcited.', true, true])
|
||||
signOut()
|
||||
|
|
|
@ -58,11 +58,6 @@ import { bundlePatternTranslations, draft, flattenFlags } from '../Editor/lib/in
|
|||
import { Bonny } from '@freesewing/bonny'
|
||||
import { MiniNote, MiniTip } from '../Mini/index.mjs'
|
||||
|
||||
const t = (input) => {
|
||||
console.log('t called', input)
|
||||
return input
|
||||
}
|
||||
|
||||
/**
|
||||
* Component to show an individual measurements set
|
||||
*
|
||||
|
@ -650,8 +645,8 @@ const SuggestCset = ({ mset, Link }) => {
|
|||
const [name, setName] = useState('')
|
||||
const [notes, setNotes] = useState('')
|
||||
const [submission, setSubmission] = useState(false)
|
||||
|
||||
console.log(mset)
|
||||
// Context
|
||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||
|
||||
// Hooks
|
||||
const backend = useBackend()
|
||||
|
@ -803,7 +798,7 @@ const RenderedCset = ({ mset, imperial }) => {
|
|||
<strong>{formatMm(pattern.parts[0].front.points.head.y * -1, imperial)}</strong> high.
|
||||
</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
|
||||
|
||||
return (
|
||||
|
@ -844,7 +839,7 @@ const RenderedCset = ({ mset, imperial }) => {
|
|||
</li>
|
||||
<li>
|
||||
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.
|
||||
</li>
|
||||
<li>
|
||||
|
@ -881,7 +876,7 @@ export const NewSet = () => {
|
|||
// Hooks
|
||||
const backend = useBackend()
|
||||
const { account } = useAccount()
|
||||
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
|
||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||
|
||||
// State
|
||||
const [name, setName] = useState('')
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// Dependencies
|
||||
import { measurements } from '@freesewing/config'
|
||||
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
||||
import { requiredMeasurements as designMeasurements } from '@freesewing/collection'
|
||||
import { cloudflareImageUrl, capitalize, hasRequiredMeasurements } from '@freesewing/utils'
|
||||
import { cloudflareImageUrl, hasRequiredMeasurements } from '@freesewing/utils'
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
// Dependencies
|
||||
import { welcomeSteps } from './shared.mjs'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { SaveIcon, RightIcon } from '@freesewing/react/components/Icon'
|
||||
import { RightIcon } from '@freesewing/react/components/Icon'
|
||||
import { ListInput } from '@freesewing/react/components/Input'
|
||||
import { NumberCircle } from '@freesewing/react/components/Number'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
|
|
|
@ -57,10 +57,6 @@ export const Username = ({ welcome = false, Link = false }) => {
|
|||
? '/welcome/' + welcomeSteps[account.control][5]
|
||||
: '/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 (
|
||||
<div className="tw:w-full">
|
||||
<StringInput
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import React from 'react'
|
||||
|
||||
import { AccountStatus } from './Status.mjs'
|
||||
import { Apikeys } from './Apikeys.mjs'
|
||||
import { Avatar } from './Avatar.mjs'
|
||||
|
|
|
@ -8,9 +8,6 @@ import {
|
|||
CompareIcon,
|
||||
DocsIcon,
|
||||
UserIcon,
|
||||
LeftIcon,
|
||||
OkIcon,
|
||||
NoIcon,
|
||||
ShowcaseIcon,
|
||||
} from '@freesewing/react/components/Icon'
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// Dependencies
|
||||
import { uiRoles as roles } from '@freesewing/config'
|
||||
import { userAvatarUrl } from '@freesewing/utils'
|
||||
// Hooks
|
||||
import React, { useState, useContext, useEffect } from 'react'
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
// Context
|
||||
|
@ -14,7 +13,6 @@ import { Spinner } from '@freesewing/react/components/Spinner'
|
|||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { SearchIcon } from '@freesewing/react/components/Icon'
|
||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||
import { Markdown } from '@freesewing/react/components/Markdown'
|
||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
import { AccountStatus, UserRole } from '@freesewing/react/components/Account'
|
||||
|
||||
|
@ -313,95 +311,3 @@ const ImpersonateButton = ({ userId }) => {
|
|||
</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 { copyToClipboard } from '@freesewing/utils'
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import { CopyIcon, OkIcon } from '@freesewing/react/components/Icon'
|
||||
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) => {
|
||||
copyToClipboard(content)
|
||||
setCopied(true)
|
||||
|
|
|
@ -4,32 +4,22 @@ import {
|
|||
collection,
|
||||
tags,
|
||||
techniques,
|
||||
designers,
|
||||
developers,
|
||||
examples,
|
||||
measurements,
|
||||
requiredMeasurements,
|
||||
optionalMeasurements,
|
||||
} from '@freesewing/collection'
|
||||
import { capitalize, linkClasses, mutateObject } from '@freesewing/utils'
|
||||
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
||||
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext, Fragment } from 'react'
|
||||
import React, { useState, Fragment } from 'react'
|
||||
import { useFilter } from '@freesewing/react/hooks/useFilter'
|
||||
|
||||
// Components
|
||||
import { Link as WebLink, AnchorLink } from '@freesewing/react/components/Link'
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import {
|
||||
CircleIcon,
|
||||
CisFemaleIcon,
|
||||
DocsIcon,
|
||||
FilterIcon,
|
||||
HeartIcon,
|
||||
NewPatternIcon,
|
||||
ResetIcon,
|
||||
ShowcaseIcon,
|
||||
|
@ -39,7 +29,6 @@ import {
|
|||
lineDrawingsBack,
|
||||
} from '@freesewing/react/components/LineDrawing'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||
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 }) => {
|
||||
if (!Link) Link = WebLink
|
||||
|
||||
|
@ -374,10 +331,6 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
|
|||
// State
|
||||
const [back, setBack] = useState(false)
|
||||
|
||||
// Context
|
||||
const { setModal, clearModal } = useContext(ModalContext)
|
||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||
|
||||
if (!design) return null
|
||||
|
||||
// Line drawings
|
||||
|
@ -391,13 +344,6 @@ export const DesignInfo = ({ Link = false, design = false, noDocsLink = false })
|
|||
: [about[design].design]
|
||||
const tags = about[design].tags || []
|
||||
const techniques = about[design].techniques || []
|
||||
const colors = {
|
||||
1: 'success',
|
||||
2: 'success',
|
||||
3: 'warning',
|
||||
4: 'warning',
|
||||
5: 'error',
|
||||
}
|
||||
|
||||
const makeButton = (
|
||||
<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.
|
||||
* 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
|
||||
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
|
||||
tw:content-start tw:text-left tw:list-none`,
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react'
|
||||
import mustache from 'mustache'
|
||||
import { flattenFlags, stripNamespace, bundlePatternTranslations } from '../lib/index.mjs'
|
||||
import { flattenFlags } from '../lib/index.mjs'
|
||||
import {
|
||||
ChatIcon,
|
||||
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)
|
||||
|
||||
if (Object.keys(flagList).length < 1) return null
|
||||
|
|
|
@ -24,7 +24,6 @@ import {
|
|||
ResetIcon,
|
||||
RightIcon,
|
||||
RocketIcon,
|
||||
RotateIcon,
|
||||
SaIcon,
|
||||
SaveAsIcon,
|
||||
SaveIcon,
|
||||
|
@ -133,7 +132,7 @@ export const HeaderMenuTestViewDesignMeasurements = (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)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -460,10 +459,8 @@ export const HeaderMenuUndoIcons = (props) => {
|
|||
}
|
||||
|
||||
export const HeaderMenuTestIcons = (props) => {
|
||||
const { update, state, Design } = props
|
||||
const { update } = props
|
||||
const Button = HeaderMenuButton
|
||||
const size = 'tw:w-5 tw:h-5'
|
||||
const undos = state._?.undos && state._.undos.length > 0 ? state._.undos : false
|
||||
|
||||
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">
|
||||
|
@ -660,11 +657,6 @@ export const HeaderMenuLayoutViewIcons = (props) => {
|
|||
}
|
||||
|
||||
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 blank = cols * rows - count
|
||||
|
||||
|
|
|
@ -35,11 +35,6 @@ export const LoadingStatus = ({ state, update }) => {
|
|||
|
||||
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 (
|
||||
<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">
|
||||
|
|
|
@ -85,7 +85,7 @@ export const MovablePattern = ({
|
|||
|
||||
const sortedRenderProps = { ...renderProps, stacks: sortedStacks }
|
||||
|
||||
const Stack = ({ stackName, stack, settings, components, t }) => (
|
||||
const Stack = ({ stackName, stack, settings, components }) => (
|
||||
<MovableStack
|
||||
{...{
|
||||
stackName,
|
||||
|
@ -397,7 +397,7 @@ function angle(pointA, pointB) {
|
|||
|
||||
const rectSize = 24
|
||||
|
||||
const Button = ({ onClickCb, transform, Icon, children, title = '' }) => {
|
||||
const Button = ({ onClickCb, transform, Icon, title = '' }) => {
|
||||
const _onClick = (event) => {
|
||||
event.stopPropagation()
|
||||
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 */
|
||||
export const Buttons = ({ transform, flip, rotate, resetPart, rotate90, iconSize }) => {
|
||||
return (
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
import React from 'react'
|
||||
import { useDesignTranslation } from '@freesewing/react/hooks/useDesignTranslation'
|
||||
import { ZoomContextProvider } from './ZoomablePattern.mjs'
|
||||
import {
|
||||
HeaderMenu,
|
||||
HeaderMenuDraftViewDesignOptions,
|
||||
HeaderMenuDraftViewCoreSettings,
|
||||
HeaderMenuDraftViewUiPreferences,
|
||||
HeaderMenuDraftViewFlags,
|
||||
} from './HeaderMenu.mjs'
|
||||
import { HeaderMenu } from './HeaderMenu.mjs'
|
||||
import { DesignOptionsMenu } from './menus/DesignOptionsMenu.mjs'
|
||||
import { CoreSettingsMenu } from './menus/CoreSettingsMenu.mjs'
|
||||
import { UiPreferencesMenu } from './menus/UiPreferencesMenu.mjs'
|
||||
import { Accordion } from './Accordion.mjs'
|
||||
|
||||
/**
|
||||
* A layout for views that include a drafted pattern
|
||||
|
@ -24,9 +16,7 @@ import { Accordion } from './Accordion.mjs'
|
|||
* @param {object] pattern - The drafted pattern
|
||||
*/
|
||||
export const PatternLayout = (props) => {
|
||||
const { menu = null, Design, pattern, update, config, state } = props
|
||||
const i18n = useDesignTranslation(Design.designConfig.data.id)
|
||||
const flags = props.pattern?.setStores?.[0]?.plugins?.['plugin-annotations']?.flags
|
||||
const { Design, pattern, update, config, state } = props
|
||||
|
||||
return (
|
||||
<ZoomContextProvider>
|
||||
|
|
|
@ -58,7 +58,7 @@ export const UserSetPicker = ({
|
|||
href={config.hrefNewSet}
|
||||
className="tw:daisy-btn tw:daisy-btn-accent tw:capitalize"
|
||||
target="_BLANK"
|
||||
rel="nofollow"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Create a new measurements set
|
||||
</a>
|
||||
|
|
|
@ -7,7 +7,7 @@ import { ZoomInIcon, ZoomOutIcon, RotateIcon } from '@freesewing/react/component
|
|||
* A pattern you can pan and zoom
|
||||
*/
|
||||
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)
|
||||
|
||||
return (
|
||||
|
|
|
@ -36,10 +36,8 @@ export const MenuItem = ({
|
|||
allowOverride = false,
|
||||
ux = 5,
|
||||
state,
|
||||
docs,
|
||||
config,
|
||||
Design,
|
||||
i18n,
|
||||
}) => {
|
||||
// Local state - whether the override input should be shown
|
||||
const [override, setOverride] = useState(false)
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
MenuScaleSettingValue,
|
||||
} from './Value.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
|
||||
|
|
|
@ -3,7 +3,7 @@ import { OptionsIcon, SettingsIcon, UiIcon } from '@freesewing/react/components/
|
|||
import { DesignOptionsMenu } from './DesignOptionsMenu.mjs'
|
||||
import { CoreSettingsMenu } from './CoreSettingsMenu.mjs'
|
||||
import { UiPreferencesMenu } from './UiPreferencesMenu.mjs'
|
||||
import { FlagsAccordionEntries } from '../Flag.mjs'
|
||||
import { FlagsAccordionEntries, FlagsAccordionTitle } from '../Flag.mjs'
|
||||
import { Accordion } from '../Accordion.mjs'
|
||||
|
||||
export const DraftMenu = ({ Design, pattern, state, update, i18n }) => {
|
||||
|
|
|
@ -16,12 +16,7 @@ import { mergeOptions } from '@freesewing/core'
|
|||
import { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||
|
||||
/** A boolean version of {@see MenuListInput} that sets up the necessary configuration */
|
||||
export const MenuBoolInput = (props) => {
|
||||
const { name, config } = props
|
||||
//const boolConfig = useBoolConfig(name, config)
|
||||
|
||||
return <MenuListInput {...props} />
|
||||
}
|
||||
export const MenuBoolInput = (props) => <MenuListInput {...props} />
|
||||
|
||||
/** A placeholder for an input to handle constant values */
|
||||
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
|
||||
* @param {String} options.name the name of the property this input changes
|
||||
|
@ -111,11 +71,9 @@ export const MenuListInput = ({
|
|||
current,
|
||||
updateHandler,
|
||||
compact = false,
|
||||
t,
|
||||
changed,
|
||||
design,
|
||||
isDesignOption = false,
|
||||
i18n,
|
||||
}) => {
|
||||
const handleChange = useSharedHandlers({
|
||||
dflt: config.dflt,
|
||||
|
@ -125,7 +83,7 @@ export const MenuListInput = ({
|
|||
})
|
||||
|
||||
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 })
|
||||
const sideBySide = config.sideBySide || about.length + title.length < 42
|
||||
|
||||
|
@ -286,7 +244,6 @@ export const MenuSliderInput = ({
|
|||
setReset,
|
||||
children,
|
||||
changed,
|
||||
i18n,
|
||||
state,
|
||||
Design,
|
||||
}) => {
|
||||
|
|
|
@ -92,7 +92,7 @@ const SampleOptionButton = ({ name, i18n, update }) => (
|
|||
</button>
|
||||
)
|
||||
|
||||
const SampleMeasurementButton = ({ name, i18n, update }) => (
|
||||
const SampleMeasurementButton = ({ name, update }) => (
|
||||
<button
|
||||
className={
|
||||
'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 = {
|
||||
aside: MenuListValue,
|
||||
ux: (props) => <span>{state.ui.ux}/5</span>,
|
||||
ux: () => <span>{state.ui.ux}/5</span>,
|
||||
rotate: MenuListValue,
|
||||
renderer: MenuListValue,
|
||||
}
|
||||
|
|
|
@ -160,11 +160,10 @@ export const MenuScaleSettingValue = ({ current, config, changed }) => (
|
|||
/**
|
||||
* Displays the value for core's only setting
|
||||
*
|
||||
* @param {object} config - The option config
|
||||
* @param {number} current - The current (count) value
|
||||
* @param {bool} changed - Whether or not the value is non-default
|
||||
*/
|
||||
export const MenuOnlySettingValue = ({ current, config }) => (
|
||||
export const MenuOnlySettingValue = ({ current }) => (
|
||||
<MenuHighlightValue changed={current !== undefined}>
|
||||
{current === undefined ? '-' : current.length}
|
||||
</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
|
||||
*
|
||||
* @param {Object} props - All the props
|
||||
* @param {Object} designs - Object holding all designs
|
||||
* @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">
|
||||
<h1>Choose a design from the FreeSewing collection</h1>
|
||||
<Collection
|
||||
|
|
|
@ -27,8 +27,8 @@ export const DraftErrorHandler = ({ failure, errors }) => {
|
|||
.
|
||||
</p>
|
||||
<p>
|
||||
If you believe your measurements are correct and/or if you'd like further assistance, you
|
||||
can ask for help <Link href="https://forum.freesewing.eu">on our forum</Link>,{' '}
|
||||
If you believe your measurements are correct and/or if you'd like further assistance,
|
||||
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://codeberg.org/freesewing/freesewing/issues">report an issue</Link>.
|
||||
</p>
|
||||
|
|
|
@ -23,7 +23,6 @@ import { yaml as yamlLang } from '@codemirror/lang-yaml'
|
|||
* @param {Object} props.update - Helper object for updating the editor state
|
||||
*/
|
||||
export const EditSettingsView = (props) => {
|
||||
const [settings, setSettings] = useState(props.state?.settings || {})
|
||||
const { state, config, update } = props
|
||||
|
||||
return (
|
||||
|
@ -96,7 +95,7 @@ export const PrimedSettingsEditor = (props) => {
|
|||
setSettings(newSettings)
|
||||
}
|
||||
} 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) => {
|
||||
const { config, state, update } = props
|
||||
const { settings = {} } = state // Guard against undefined settings
|
||||
const [link, setLink] = useState(false)
|
||||
const [format, setFormat] = useState(false)
|
||||
const setLink = useState(false)[1]
|
||||
const setFormat = useState(false)[1]
|
||||
|
||||
const { protocol, hostname, port } = window.location
|
||||
const { protocol, port } = window.location
|
||||
const site =
|
||||
(protocol === 'https:' && port === 443) || (protocol === 'http:' && port === 80)
|
||||
? `${window.location.protocol}//${window.location.hostname}`
|
||||
|
@ -102,6 +102,7 @@ export const ExportView = (props) => {
|
|||
<H3>ISO paper sizes</H3>
|
||||
{['a4', 'a3', 'a2', 'a1', 'a0'].map((format) => (
|
||||
<button
|
||||
key={format}
|
||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||
onClick={() => exportPattern({ ...exportProps, format })}
|
||||
>
|
||||
|
@ -114,6 +115,7 @@ export const ExportView = (props) => {
|
|||
<H3>Other paper sizes</H3>
|
||||
{['letter', 'legal', 'tabloid'].map((format) => (
|
||||
<button
|
||||
key={format}
|
||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||
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">
|
||||
{['svg', 'pdf'].map((format) => (
|
||||
<button
|
||||
key={format}
|
||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||
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">
|
||||
{['json', 'yaml'].map((format) => (
|
||||
<button
|
||||
key={format}
|
||||
className={`${horFlexClasses} tw:daisy-btn tw:daisy-btn-primary tw:uppercase`}
|
||||
onClick={() => exportPattern({ ...exportProps, format })}
|
||||
>
|
||||
|
|
|
@ -11,7 +11,7 @@ import { DraftErrorHandler } from './DraftErrorHandler.mjs'
|
|||
|
||||
export const LayoutView = (props) => {
|
||||
const { config, state, update, Design } = props
|
||||
const { ui, settings } = state
|
||||
const { settings } = state
|
||||
const defaultSettings = defaultPrintSettings(settings?.units)
|
||||
|
||||
// Settings for the tiler plugin
|
||||
|
|
|
@ -8,8 +8,8 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
|||
// Components
|
||||
import { RoleBlock } from '@freesewing/react/components/Role'
|
||||
import { Popout } from '@freesewing/react/components/Popout'
|
||||
import { StringInput } from '@freesewing/react/components/Input'
|
||||
import { SaveAsIcon } from '@freesewing/react/components/Icon'
|
||||
import { StringInput, MarkdownInput } from '@freesewing/react/components/Input'
|
||||
import { SaveAsIcon, SaveIcon } from '@freesewing/react/components/Icon'
|
||||
import { H1 } from '@freesewing/react/components/Heading'
|
||||
import { Link, SuccessLink } from '@freesewing/react/components/Link'
|
||||
import { HeaderMenu } from '../HeaderMenu.mjs'
|
||||
|
@ -67,7 +67,8 @@ export const SaveView = ({ config, state, update }) => {
|
|||
}
|
||||
|
||||
const savePattern = async () => {
|
||||
setLoadingStatus([true, 'Saving pattern...'])
|
||||
const loadingId = 'savePattern'
|
||||
update.startLoading(loadingId)
|
||||
const patternData = {
|
||||
design: state.design,
|
||||
settings,
|
||||
|
@ -77,8 +78,9 @@ export const SaveView = ({ config, state, update }) => {
|
|||
}
|
||||
const result = await backend.updatePattern(saveAs.pattern, patternData)
|
||||
if (result.success) {
|
||||
update.stopLoading(loadingId)
|
||||
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>
|
||||
)}
|
||||
<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}
|
||||
>
|
||||
<SaveIcon className="tw:h-8 tw:w-8" />
|
||||
Save Patter #{saveAs.pattern}
|
||||
Save Pattern #{saveAs.pattern}
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
|
@ -137,11 +139,7 @@ export const SaveView = ({ config, state, update }) => {
|
|||
}
|
||||
/>
|
||||
{withNotes ? (
|
||||
<Swizzled.components.MarkdownInput
|
||||
label="Pattern notes"
|
||||
current={notes}
|
||||
update={setNotes}
|
||||
/>
|
||||
<MarkdownInput label="Pattern notes" current={notes} update={setNotes} />
|
||||
) : null}
|
||||
<div className="tw:flex tw:flex-row tw:gap-2 tw:mt-8">
|
||||
<button
|
||||
|
|
|
@ -69,7 +69,6 @@ export const TestView = ({ Design, state, update, config }) => {
|
|||
patternLocale={state.locale || 'en'}
|
||||
rotate={state.ui.rotate}
|
||||
strings={strings}
|
||||
rotate={state.ui.rotate}
|
||||
/>
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Dependencies
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import * as echarts from 'echarts'
|
||||
import { timingPlugin } from '@freesewing/plugin-timing'
|
||||
// Components
|
||||
import { ChartWrapper } from '@freesewing/react/components/Echart'
|
||||
|
@ -36,18 +35,6 @@ const TimingHeader = ({ timing, parts }) => {
|
|||
) : 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) => {
|
||||
if (took < 25) return 'Very Fast'
|
||||
if (took < 50) return 'Fast'
|
||||
|
@ -58,7 +45,6 @@ const timeScore = (took) => {
|
|||
}
|
||||
|
||||
const option = (parts, data, took, setData) => ({
|
||||
//color: colors.map((color) => resolveColor(color)),
|
||||
title: {
|
||||
text: `Timing of most recent draft: ${timeScore(took)}`,
|
||||
left: 'center',
|
||||
|
@ -127,7 +113,7 @@ const option = (parts, data, took, setData) => ({
|
|||
},
|
||||
],
|
||||
yAxis: [{ type: 'value' }],
|
||||
series: parts.map((name, i) => ({
|
||||
series: parts.map((name) => ({
|
||||
name,
|
||||
type: 'line',
|
||||
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]
|
||||
|
||||
return (
|
||||
|
|
|
@ -37,6 +37,7 @@ export const useEditorState = (init = {}, setEphemeralState, config) => {
|
|||
if (typeof data.s === 'object') setState(data.s)
|
||||
else setState(init)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
setState(init)
|
||||
}
|
||||
}
|
||||
|
@ -45,31 +46,6 @@ export const useEditorState = (init = {}, setEphemeralState, config) => {
|
|||
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() {
|
||||
if (!window) return false
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ export function menuCoreSettingsStructure({
|
|||
title: 'Seam Allowance Size',
|
||||
about: (
|
||||
<>
|
||||
Controls the size of the pattern's seam allowance.
|
||||
Controls the size of the pattern's seam allowance.
|
||||
<CoreDocsLink item="sa" />
|
||||
</>
|
||||
),
|
||||
|
|
|
@ -9,6 +9,7 @@ const DesignDocsLink = ({ design, item }) => (
|
|||
href={`/docs/designs/${design}/options/#${item.toLowerCase()}`}
|
||||
className={`${linkClasses} tw:px-2`}
|
||||
target="_BLANK"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
|
@ -62,14 +63,14 @@ export function menuDesignOptionsStructure(design, options, settings, asFullList
|
|||
option.valueTitles = {}
|
||||
option.choiceTitles = {}
|
||||
option.choiceDescriptions = {}
|
||||
for (const entry of option.list) {
|
||||
option.list.forEach(() => {
|
||||
option.choiceTitles.false = eno[`${option.name}No`]?.t || option.name
|
||||
option.choiceDescriptions.false = eno[`${option.name}No`]?.d || 'No'
|
||||
option.valueTitles.false = 'No'
|
||||
option.choiceTitles.true = eno[`${option.name}Yes`]?.t || 'Yes'
|
||||
option.choiceDescriptions.true = eno[`${option.name}Yes`]?.d || 'No'
|
||||
option.valueTitles.true = 'Yes'
|
||||
}
|
||||
})
|
||||
}
|
||||
if (typeof option.menu === 'function')
|
||||
option.menu = asFullList
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import React from 'react'
|
||||
import { defaultConfig } from '../config/index.mjs'
|
||||
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 { i18n } from '@freesewing/collection'
|
||||
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
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
|
|
|
@ -3,7 +3,7 @@ import fileSaver from 'file-saver'
|
|||
import { themePlugin } from '@freesewing/plugin-theme'
|
||||
import { pluginI18n } from '@freesewing/plugin-i18n'
|
||||
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 he from 'he'
|
||||
import yaml from 'js-yaml'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react'
|
||||
import { linkClasses } from '@freesewing/utils'
|
||||
import {
|
||||
CoverPageIcon,
|
||||
PageMarginIcon,
|
||||
|
@ -9,13 +8,6 @@ import {
|
|||
ScaleIcon,
|
||||
} 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) => ({
|
||||
size: units === 'imperial' ? 'letter' : 'a4',
|
||||
orientation: 'portrait',
|
||||
|
|
|
@ -27,7 +27,6 @@ export const IconWrapper = ({
|
|||
fill = false,
|
||||
fillOpacity = 1,
|
||||
dasharray = null,
|
||||
wrapped = true,
|
||||
}) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
import { collection } from '@freesewing/collection'
|
||||
import { measurements as measurementsTranslations } from '@freesewing/i18n'
|
||||
// Context
|
||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
// Hooks
|
||||
import React, { useState, useCallback, useContext } from 'react'
|
||||
|
@ -19,7 +18,6 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
|||
// Components
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { TrashIcon, ResetIcon, UploadIcon, HelpIcon } from '@freesewing/react/components/Icon'
|
||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
import { isDegreeMeasurement } from '@freesewing/config'
|
||||
import { Tabs, Tab } from '@freesewing/react/components/Tab'
|
||||
import Markdown from 'react-markdown'
|
||||
|
@ -68,31 +66,50 @@ const HelpLink = ({ help, Link = false }) => {
|
|||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const Fieldset = ({
|
||||
Link=false,
|
||||
box=false,
|
||||
Link = false,
|
||||
box = false,
|
||||
children,
|
||||
label=false,
|
||||
labelBL=false,
|
||||
labelBR=false,
|
||||
labelTR=false,
|
||||
legend=false,
|
||||
forId='',
|
||||
help=false,
|
||||
label = false,
|
||||
labelBL = false,
|
||||
labelBR = false,
|
||||
labelTR = false,
|
||||
legend = false,
|
||||
forId = '',
|
||||
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 className="tw:daisy-fieldset-legend tw:px-2 tw:pb-1">
|
||||
{legend}<HelpLink {...{ help, Link }} />
|
||||
{legend}
|
||||
<HelpLink {...{ help, Link }} />
|
||||
</legend>
|
||||
) : null}
|
||||
<div className="tw:flex tw:flex-row tw:justify-between tw:px-2">
|
||||
{label ? <label className="tw:daisy-label" htmlFor={forId}>{label}</label> : null }
|
||||
{labelTR ? <label className="tw:daisy-label" htmlFor={forId}>{labelTR}</label> : null }
|
||||
{label ? (
|
||||
<label className="tw:daisy-label" htmlFor={forId}>
|
||||
{label}
|
||||
</label>
|
||||
) : null}
|
||||
{labelTR ? (
|
||||
<label className="tw:daisy-label" htmlFor={forId}>
|
||||
{labelTR}
|
||||
</label>
|
||||
) : null}
|
||||
</div>
|
||||
{children}
|
||||
<div className="tw:flex tw:flex-row tw:justify-between tw:px-2">
|
||||
{labelBL ? <label className="tw:daisy-label" htmlFor={forId}>{labelBL}</label> : null }
|
||||
{labelBR ? <label className="tw:daisy-label" htmlFor={forId}>{labelBR}</label> : null }
|
||||
{labelBL ? (
|
||||
<label className="tw:daisy-label" htmlFor={forId}>
|
||||
{labelBL}
|
||||
</label>
|
||||
) : null}
|
||||
{labelBR ? (
|
||||
<label className="tw:daisy-label" htmlFor={forId}>
|
||||
{labelBR}
|
||||
</label>
|
||||
) : null}
|
||||
</div>
|
||||
</fieldset>
|
||||
)
|
||||
|
@ -153,7 +170,7 @@ export const ButtonFrame = ({ active, children, dense, noBg, onClick }) => (
|
|||
export const NumberInput = ({
|
||||
box = false,
|
||||
current,
|
||||
help=false,
|
||||
help = false,
|
||||
inputMode = 'decimal',
|
||||
label = false,
|
||||
labelBL = false,
|
||||
|
@ -212,7 +229,7 @@ export const NumberInput = ({
|
|||
export const StringInput = ({
|
||||
box = false,
|
||||
current,
|
||||
help=false,
|
||||
help = false,
|
||||
label = false,
|
||||
labelBL = false,
|
||||
labelBR = false,
|
||||
|
@ -265,7 +282,7 @@ export const StringInput = ({
|
|||
export const MfaInput = ({
|
||||
box = false,
|
||||
current,
|
||||
help=false,
|
||||
help = false,
|
||||
label = false,
|
||||
labelBL = false,
|
||||
labelBR = false,
|
||||
|
@ -273,12 +290,26 @@ export const MfaInput = ({
|
|||
id = 'mfa',
|
||||
inputMode = 'numeric',
|
||||
legend = false,
|
||||
placeholder="MFA Code",
|
||||
placeholder = 'MFA Code',
|
||||
update,
|
||||
valid = (val) => val.length > 4,
|
||||
}) => (
|
||||
<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 = ({
|
||||
box = false,
|
||||
current,
|
||||
help=false,
|
||||
help = false,
|
||||
label = false,
|
||||
labelBL = false,
|
||||
labelTR = false,
|
||||
|
@ -712,7 +743,7 @@ export const MarkdownInput = ({
|
|||
labelTR = false,
|
||||
legend = false,
|
||||
update,
|
||||
placeholder='',
|
||||
placeholder = '',
|
||||
}) => (
|
||||
<Fieldset {...{ box, help, label, labelTR, labelBL, labelBR, legend }} forId={id}>
|
||||
<Tabs tabs={['edit', 'preview']}>
|
||||
|
@ -743,7 +774,6 @@ export const MarkdownInput = ({
|
|||
* @component
|
||||
* @param {object} props - All component props
|
||||
* @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} [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
|
||||
|
@ -758,7 +788,6 @@ export const MarkdownInput = ({
|
|||
*/
|
||||
export const MeasurementInput = ({
|
||||
box = false,
|
||||
current,
|
||||
help = false,
|
||||
id = '',
|
||||
imperial = false,
|
||||
|
@ -768,7 +797,7 @@ export const MeasurementInput = ({
|
|||
m,
|
||||
update,
|
||||
original,
|
||||
placeholder='',
|
||||
placeholder = '',
|
||||
}) => {
|
||||
const isDegree = isDegreeMeasurement(m)
|
||||
const units = imperial ? 'imperial' : 'metric'
|
||||
|
@ -876,7 +905,6 @@ export const MeasurementInput = ({
|
|||
* @param {string} [props.legend = false] - The fieldset legend
|
||||
* @param {function} props.update - The onChange handler
|
||||
* @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}
|
||||
*/
|
||||
export const FileInput = ({
|
||||
|
@ -892,7 +920,6 @@ export const FileInput = ({
|
|||
legend = false,
|
||||
update,
|
||||
original,
|
||||
valid = () => true,
|
||||
}) => {
|
||||
/*
|
||||
* Ondrop handler
|
||||
|
@ -974,8 +1001,6 @@ export const FileInput = ({
|
|||
* @param {array} [props.list = [true, false] - An array of values to choose between
|
||||
* @param {function} props.update - The onChange handler
|
||||
* @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}
|
||||
*/
|
||||
export const ToggleInput = ({
|
||||
|
@ -993,8 +1018,6 @@ export const ToggleInput = ({
|
|||
list = [true, false],
|
||||
update,
|
||||
on = true,
|
||||
original,
|
||||
valid = () => true,
|
||||
}) => (
|
||||
<Fieldset {...{ box, help, labelTR, labelBL, labelBR, legend }} forId={id}>
|
||||
<label className="tw:daisy-label">
|
||||
|
@ -1009,8 +1032,7 @@ export const ToggleInput = ({
|
|||
/>
|
||||
{label
|
||||
? `${label} (${current === on ? labels[0] : labels[1]})`
|
||||
: `${current === on ? labels[0] : labels[1]}`
|
||||
}
|
||||
: `${current === on ? labels[0] : labels[1]}`}
|
||||
</label>
|
||||
</Fieldset>
|
||||
)
|
||||
|
|
|
@ -23,7 +23,7 @@ export const KeyVal = ({
|
|||
href = false,
|
||||
onClick = false,
|
||||
}) => {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const setCopied = useState(false)[1]
|
||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||
|
||||
let colorClasses1 = primaryClasses1
|
||||
|
@ -83,7 +83,6 @@ export const KeyVal = ({
|
|||
const LinkKeyVal = ({
|
||||
k,
|
||||
val,
|
||||
color = 'primary',
|
||||
small = false,
|
||||
href = false,
|
||||
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 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) => {
|
||||
setCopied(true)
|
||||
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 {JSX.Element} [props.children = []] - The component children to render inside the layout
|
||||
* @param {array} [props.crumbs = []] - Data for the breadcrumbs, see Breadcrumbs
|
||||
* @param {string} props.description - The page description
|
||||
* @param {string} props.title - The page title
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const Layout = ({ children = [], crumbs = [], description, Link = false, title }) => {
|
||||
export const Layout = ({ children = [], crumbs = [], Link = false, title }) => {
|
||||
if (!Link) Link = DefaultLink
|
||||
|
||||
return (
|
||||
|
|
|
@ -48,7 +48,7 @@ export const AlbertFront = ({ className, stroke = 1 }) => (
|
|||
* @returns {JSX.Element}
|
||||
*/
|
||||
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} />
|
||||
</LineDrawingWrapper>
|
||||
)
|
||||
|
|
|
@ -17,7 +17,7 @@ const strokeScale = 0.7
|
|||
* @returns {JSX.Element}
|
||||
*/
|
||||
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} />
|
||||
<Back stroke={strokeScale * stroke} />
|
||||
</LineDrawingWrapper>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
|
@ -56,7 +56,7 @@ export const JaneBack = ({ className, stroke = 1 }) => (
|
|||
/*
|
||||
* SVG elements for the front
|
||||
*/
|
||||
const Front = ({ stroke }) => (
|
||||
const Front = () => (
|
||||
<>
|
||||
<path
|
||||
key="outline"
|
||||
|
@ -69,7 +69,7 @@ const Front = ({ stroke }) => (
|
|||
/*
|
||||
* SVG elements for the back
|
||||
*/
|
||||
const Back = ({ stroke }) => (
|
||||
const Back = () => (
|
||||
<>
|
||||
<path
|
||||
key="outline"
|
||||
|
|
|
@ -52,22 +52,20 @@ const BaseLink = Link
|
|||
* @param {object} props - All component props
|
||||
* @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.target = undefined] - An optional link title
|
||||
* @param {string} [props.title = false] - An optional link title
|
||||
* @param {React.FC} [Link = undefined] - An optional framework-specific Link component
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const SuccessLink = ({
|
||||
children,
|
||||
href,
|
||||
target,
|
||||
title = false,
|
||||
Link,
|
||||
}) => (
|
||||
export const SuccessLink = ({ children, href, title = false, Link }) =>
|
||||
Link ? (
|
||||
<Link href={href} className={linkClasses} title={title ? title : ''}>
|
||||
<span className="tw:text-success-content tw:hover:text-success-content">{children}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<a href={href} className={linkClasses} title={title ? title : ''}>
|
||||
<span className="tw:text-success-content tw:hover:text-success-content">{children}</span>
|
||||
</a>
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* A link styled as a card
|
||||
|
|
|
@ -10,11 +10,7 @@ import { logoPath } from '@freesewing/config'
|
|||
* @param {string} [props.stroke = undefined] - Set this explicitly to use a different stroke color
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const FreeSewingLogo = ({ className = 'tw:w-20 tw:h-20', stroke }) => {
|
||||
const svgProps = {}
|
||||
const strokes = { dark: '#000', light: 'var(--p)' }
|
||||
|
||||
return (
|
||||
export const FreeSewingLogo = ({ className = 'tw:w-20 tw:h-20', stroke }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1 0 25 25" className={className}>
|
||||
<defs>
|
||||
<path id="react-logo" d={logoPath} />
|
||||
|
@ -23,11 +19,8 @@ export const FreeSewingLogo = ({ className = 'tw:w-20 tw:h-20', stroke }) => {
|
|||
xlinkHref="#react-logo"
|
||||
fill="none"
|
||||
strokeWidth="0.5"
|
||||
style={{ stroke: stroke ? stroke : 'var(--color-base-100)'}}
|
||||
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
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Components
|
||||
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 { Popout } from '@freesewing/react/components/Popout'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
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
|
||||
|
||||
// Hooks
|
||||
const { account, setAccount } = useAccount()
|
||||
const backend = useBackend()
|
||||
const { setLoadingStatus } = useContext(LoadingStatusContext)
|
||||
|
||||
|
@ -44,7 +40,7 @@ export const NewsletterSignup = ({ Link = false, noP = false, noTitle = false, n
|
|||
// Helper method to handle subscription
|
||||
const subscribe = async () => {
|
||||
setLoadingStatus([true, 'Contacting backend'])
|
||||
const [status, body] = unsubscribe
|
||||
const [status] = unsubscribe
|
||||
? await backend.newsletterStartUnsubscribe(email)
|
||||
: await backend.newsletterSubscribe(email)
|
||||
if (status === 200) {
|
||||
|
@ -146,7 +142,7 @@ export const NewsletterUnsubscribe = ({ Link = false }) => {
|
|||
|
||||
// Helper method to handle subscription
|
||||
const unsubscribe = async () => {
|
||||
const [status, body] = await backend.newsletterUnsubscribe(ehash)
|
||||
const [status] = await backend.newsletterUnsubscribe(ehash)
|
||||
if (status === 204 || status === 404) setGone(true)
|
||||
else setError(true)
|
||||
}
|
||||
|
@ -197,6 +193,4 @@ export const NewsletterUnsubscribe = ({ Link = false }) => {
|
|||
</MiniTip>
|
||||
</>
|
||||
)
|
||||
|
||||
return <p>Unsubscribe here {ehash}</p>
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import React from 'react'
|
||||
|
||||
/**
|
||||
* Sometimes, you just want a component that does nothing
|
||||
*
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// Dependencies
|
||||
import { linkClasses } from '@freesewing/utils'
|
||||
// Hooks
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
/**
|
||||
* A component to ask people to support FreeSewing financially
|
||||
|
@ -211,7 +210,12 @@ export const Subscribe = ({
|
|||
</button>
|
||||
<p className="tw:text-center tw:text-sm tw:text-neutral-content tw:mt-2 tw:opacity-80">
|
||||
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>
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react'
|
||||
import sanitize from 'html-react-parser'
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// Dependencies
|
||||
import { cloudflareImageUrl } from '@freesewing/utils'
|
||||
// Components
|
||||
import React, { forwardRef } from 'react'
|
||||
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 { Circle as DefaultCircle } from './circle.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
|
||||
|
@ -101,6 +98,7 @@ const Pattern = forwardRef((props, ref) => {
|
|||
</Svg>
|
||||
)
|
||||
})
|
||||
Pattern.displayName = 'Pattern'
|
||||
|
||||
export {
|
||||
// utils
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
import React, { forwardRef } from 'react'
|
||||
import { getId, getProps } from './utils.mjs'
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react'
|
||||
import { getId, getProps } from './utils.mjs'
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react'
|
||||
import { withinPartBounds } from './utils.mjs'
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react'
|
||||
import { getProps } from './utils.mjs'
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react'
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react'
|
||||
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
|
||||
*
|
||||
|
|
|
@ -68,7 +68,7 @@ export const Popout = ({
|
|||
compact = false,
|
||||
dense = false,
|
||||
hideable = false,
|
||||
type = "note",
|
||||
type = 'note',
|
||||
title = false,
|
||||
}) => {
|
||||
// Make this hideable/dismissable
|
||||
|
@ -76,15 +76,17 @@ export const Popout = ({
|
|||
|
||||
if (hide) return null
|
||||
|
||||
return compact
|
||||
? <CompactPopout {...{ by, compact, dense, hideable, type, title, setHide }}>{children}</CompactPopout>
|
||||
: <RegularPopout {...{ by, hideable, type, title, setHide }}>{children}</RegularPopout>
|
||||
return compact ? (
|
||||
<CompactPopout {...{ by, compact, dense, hideable, type, title, setHide }}>
|
||||
{children}
|
||||
</CompactPopout>
|
||||
) : (
|
||||
<RegularPopout {...{ by, hideable, type, title, setHide }}>{children}</RegularPopout>
|
||||
)
|
||||
}
|
||||
|
||||
const RegularPopout = ({ by, children, compact, hideable, type, title, setHide }) => (
|
||||
<div
|
||||
className={`tw:relative tw:my-8 tw:-ml-4 tw:-mr-4 tw:sm:ml-0 tw:sm:mr-0 ${types[type].bg}`}
|
||||
>
|
||||
const RegularPopout = ({ by, children, hideable, type, title, setHide }) => (
|
||||
<div className={`tw:relative tw:my-8 tw:-ml-4 tw:-mr-4 tw:sm:ml-0 tw:sm:mr-0 ${types[type].bg}`}>
|
||||
<div
|
||||
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
|
||||
|
@ -98,7 +100,6 @@ const RegularPopout = ({ by, children, compact, hideable, type, title, setHide }
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
|
||||
const CompactPopout = ({ by, children, compact, dense, hideable, type, title, setHide }) => (
|
||||
|
@ -122,18 +123,25 @@ const CompactPopout = ({ by, children, compact, dense, hideable, type, title, se
|
|||
)
|
||||
|
||||
const PopoutTitle = ({ by, compact, hideable, setHide, title, type }) => (
|
||||
<div
|
||||
className={`tw:font-bold tw:flex tw:flex-row tw:gap-1 tw:items-end tw:justify-between`}
|
||||
>
|
||||
<div className={`tw:font-bold tw:flex tw:flex-row tw:gap-1 tw:items-end tw:justify-between`}>
|
||||
<div>
|
||||
<span className={`tw:font-bold tw:uppercase ${types[type].text}`}>
|
||||
{title ? title : types[type].title ? types[type].title : type.toUpperCase()}
|
||||
{compact ? <span className="tw:px-2">|</span> : null}
|
||||
</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>
|
||||
{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 />
|
||||
</button>
|
||||
)}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Dependencies
|
||||
import { linkClasses, cloudflareImageUrl, getSearchParam } from '@freesewing/utils'
|
||||
import { cloudflareImageUrl, getSearchParam } from '@freesewing/utils'
|
||||
// Context
|
||||
import { ModalContext } from '@freesewing/react/context/Modal'
|
||||
// Hooks
|
||||
|
@ -9,7 +9,6 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
|||
// Components
|
||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
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 { KeyVal } from '@freesewing/react/components/KeyVal'
|
||||
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
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const UserProfile = ({
|
||||
Link = false,
|
||||
setTitle = false,
|
||||
uid = false,
|
||||
fromUrl = false,
|
||||
}) => {
|
||||
export const UserProfile = ({ Link = false, setTitle = false, uid = false, fromUrl = false }) => {
|
||||
if (!uid && !fromUrl)
|
||||
return (
|
||||
<MiniWarning>
|
||||
|
@ -69,7 +63,7 @@ export const UserProfile = ({
|
|||
const urlId = getSearchParam(fromUrl)
|
||||
if (urlId && urlId !== ruid) setRuid(urlId)
|
||||
}
|
||||
if (ruid) loadProfileData(ruid, backend, setData)
|
||||
if (ruid) loadProfileData(ruid, backend, setData, setTitle)
|
||||
}, [uid, fromUrl, ruid])
|
||||
|
||||
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)
|
||||
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 { Spinner } from '@freesewing/react/components/Spinner'
|
||||
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'
|
||||
|
||||
const Wrap = ({ children }) => (
|
||||
|
@ -104,7 +104,7 @@ const AccountStatusUnknown = ({ banner }) => (
|
|||
</Wrap>
|
||||
)
|
||||
|
||||
const RoleLacking = ({ t, requiredRole, role, banner }) => (
|
||||
const RoleLacking = ({ requiredRole, role, banner }) => (
|
||||
<Wrap>
|
||||
{banner}
|
||||
<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
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const RoleBlock = ({ children, role = "admin", Link = false }) => {
|
||||
export const RoleBlock = ({ children, role = 'admin', Link = false }) => {
|
||||
if (!Link) Link = DefaultLink
|
||||
const requiredRole = role
|
||||
|
||||
|
@ -275,7 +275,6 @@ export const UserVisitorContent = ({ userContent = null, visitorContent = null }
|
|||
|
||||
const [ready, setReady] = useState(false)
|
||||
const [error, setError] = useState(false)
|
||||
const [refreshCount, setRefreshCount] = useState(0)
|
||||
|
||||
/*
|
||||
* Avoid hydration errors
|
||||
|
@ -300,14 +299,15 @@ export const UserVisitorContent = ({ userContent = null, visitorContent = null }
|
|||
if (!account.bestBefore || account.bestBefore < Date.now()) verifyUser()
|
||||
}
|
||||
setReady(true)
|
||||
}, [refreshCount])
|
||||
|
||||
const refresh = () => {
|
||||
setRefreshCount(refreshCount + 1)
|
||||
setError(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
// Utils
|
||||
import {
|
||||
linkClasses,
|
||||
horFlexClasses,
|
||||
horFlexClassesNoSm,
|
||||
capitalize,
|
||||
getSearchParam,
|
||||
} from '@freesewing/utils'
|
||||
import { horFlexClasses, horFlexClassesNoSm, getSearchParam, navigate } from '@freesewing/utils'
|
||||
// Context
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
// Hooks
|
||||
|
@ -21,13 +15,11 @@ import {
|
|||
KeyIcon,
|
||||
LockIcon,
|
||||
WarningIcon,
|
||||
GoogleIcon,
|
||||
GitHubIcon,
|
||||
FreeSewingIcon,
|
||||
UserIcon,
|
||||
} from '@freesewing/react/components/Icon'
|
||||
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}
|
||||
*/
|
||||
export const SignIn = ({ onSuccess = false, silent = false }) => {
|
||||
const { account, setAccount, setToken, seenUser, setSeenUser } = useAccount()
|
||||
const { setAccount, setToken, seenUser, setSeenUser } = useAccount()
|
||||
const backend = useBackend()
|
||||
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 ${
|
||||
signInFailed ? 'tw:daisy-btn-warning' : 'tw:daisy-btn-primary'
|
||||
} tw:transition-colors tw:ease-in-out tw:duration-300 ${horFlexClassesNoSm}`
|
||||
|
@ -412,7 +395,12 @@ export const SignInConfirmation = ({ onSuccess = false }) => {
|
|||
}
|
||||
|
||||
// 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)
|
||||
return error === 'signInFailed' ? (
|
||||
<>
|
||||
|
|
|
@ -3,7 +3,6 @@ import { validateEmail, validateTld, getSearchParam } from '@freesewing/utils'
|
|||
|
||||
// Hooks
|
||||
import React, { useState, useContext, useEffect } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
// Context
|
||||
|
@ -12,16 +11,7 @@ import { ModalContext } from '@freesewing/react/context/Modal'
|
|||
|
||||
// Components
|
||||
import { Link } from '@freesewing/react/components/Link'
|
||||
import {
|
||||
LeftIcon,
|
||||
RightIcon,
|
||||
HelpIcon,
|
||||
GoogleIcon,
|
||||
GitHubIcon,
|
||||
KeyIcon,
|
||||
EmailIcon,
|
||||
DownIcon,
|
||||
} from '@freesewing/react/components/Icon'
|
||||
import { LeftIcon, HelpIcon, KeyIcon, EmailIcon } from '@freesewing/react/components/Icon'
|
||||
import { ModalWrapper } from '@freesewing/react/components/Modal'
|
||||
import { EmailInput } from '@freesewing/react/components/Input'
|
||||
import { IconButton } from '@freesewing/react/components/Button'
|
||||
|
@ -42,7 +32,6 @@ export const SignUp = ({ embed = false }) => {
|
|||
const [email, setEmail] = useState('')
|
||||
const [emailValid, setEmailValid] = useState(false)
|
||||
const [result, setResult] = useState(false)
|
||||
const [showAll, setShowAll] = useState(false)
|
||||
|
||||
// Hooks
|
||||
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
|
||||
? ({ children }) => <h2 className="tw:text-inherit">{children}</h2>
|
||||
: ({ children }) => <h1 className="tw:text-inherit">{children}</h1>
|
||||
|
@ -200,10 +181,9 @@ export const SignUp = ({ embed = false }) => {
|
|||
*
|
||||
* @component
|
||||
* @param {object} props - All component props
|
||||
* @param {function} [props.onSuccess = false] - A method to run when the sign in is successful
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const SignUpConfirmation = ({ onSuccess = false }) => {
|
||||
export const SignUpConfirmation = () => {
|
||||
// State
|
||||
const [id, setId] = useState()
|
||||
const [error, setError] = useState(false)
|
||||
|
@ -219,7 +199,12 @@ export const SignUpConfirmation = ({ onSuccess = false }) => {
|
|||
}, [id, check])
|
||||
|
||||
// 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 (!id || !check)
|
||||
return (
|
||||
|
|
|
@ -7,11 +7,7 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
|
|||
import { Spinner } from '@freesewing/react/components/Spinner'
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
import { ChartWrapper } from '@freesewing/react/components/Echart'
|
||||
|
||||
const meta = {
|
||||
title: 'FreeSewing by numbers',
|
||||
description: 'Some high-level numbers about Freesewing',
|
||||
}
|
||||
import { Popout } from '@freesewing/react/components/Popout'
|
||||
|
||||
const option = {
|
||||
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 (
|
||||
<>
|
||||
<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 (
|
||||
<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) => {
|
||||
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' : ''
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
import { shortUuid } from '@freesewing/utils'
|
||||
import { Link as WebLink } from '@freesewing/react/components/Link'
|
||||
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
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export const Uuid = ({ uuid, href = false, label = "UUID", Link = false }) => {
|
||||
const [full, setFull] = useState()
|
||||
const short = shortUuid(uuid)
|
||||
export const Uuid = ({ uuid, href = false, label = 'UUID', Link = false }) => {
|
||||
if (!Link) Link = WebLink
|
||||
|
||||
if (href === false)
|
||||
|
|
|
@ -81,3 +81,5 @@ export const Xray = forwardRef((props, ref) => {
|
|||
</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 log = (val) => console.log(val)
|
||||
const rounder = rounded ? round : (val) => val
|
||||
|
||||
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 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
|
||||
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
|
||||
|
||||
// Hooks
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { useState, useContext } from 'react'
|
||||
import { useAccount } from '@freesewing/react/hooks/useAccount'
|
||||
import { useBackend } from '@freesewing/react/hooks/useBackend'
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React from 'react'
|
||||
import { useAtom } from 'jotai'
|
||||
import { atomWithHash } from 'jotai-location'
|
||||
|
||||
const filterAtom = atomWithHash('filter', { })
|
||||
const filterAtom = atomWithHash('filter', {})
|
||||
|
||||
export const useFilter = () => useAtom(filterAtom)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
export const useSelection = (items) => {
|
||||
const [selection, setSelection] = useState({})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import set from 'lodash/set.js'
|
||||
import unset from 'lodash/unset.js'
|
||||
|
||||
|
|
|
@ -68,9 +68,11 @@ async function withoutBody(method = 'GET', url, headers = {}, raw = false, log =
|
|||
try {
|
||||
body = raw ? await response.text() : await response.json()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
try {
|
||||
body = await response.text()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
body = false
|
||||
}
|
||||
}
|
||||
|
@ -125,9 +127,11 @@ async function withBody(method = 'POST', url, data, headers, raw = false, log =
|
|||
try {
|
||||
body = raw ? await response.text() : await response.json()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
try {
|
||||
body = await response.text()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
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