Bugfixes - Editor images, git-gateway auth, editor scrolling, numeric selects, object/list previews (#50)

* v1.0.0-alpha26
This commit is contained in:
Daniel Lautzenheiser 2022-10-27 12:24:30 -04:00 committed by GitHub
parent 1de3d52d57
commit 8c8a59093d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 794 additions and 942 deletions

24
.gitignore vendored
View File

@ -1,24 +0,0 @@
dist/
bin/
node_modules/
npm-debug.log
.DS_Store
.tern-project
yarn-error.log
.vscode/
.idea/
manifest.yml
.imdone/
website/data/contributors.json
cypress/videos
cypress/screenshots
__diff_output__
coverage/
.cache
*.log
.env
.temp/
*.tgz
old-website
website/public/sw.js
website/public/workbox*.js

56
core/.gitignore vendored Normal file
View File

@ -0,0 +1,56 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
dist/
bin/
.tern-project
.vscode/
.idea/
manifest.yml
.imdone/
data/contributors.json
cypress/videos
cypress/screenshots
__diff_output__
.cache
*.log
.env
.temp/
*.tgz
public/sw.js
public/workbox*.js

View File

@ -90,8 +90,6 @@ collections:
- name: widgets
label: Widgets
delete: false
editor:
preview: false
files:
- name: boolean
label: Boolean
@ -620,6 +618,16 @@ collections:
value: 2
- label: Three
value: 3
- label: Select mixed string and numeric
name: select_mixed_string_numeric
widget: select
options:
- label: One
value: "One"
- label: Two
value: 2
- label: Three
value: 3
- label: Hidden
name: hidden
widget: hidden

View File

@ -1,6 +1,6 @@
{
"name": "@staticcms/core",
"version": "1.0.0-alpha16",
"version": "1.0.0-alpha26",
"license": "MIT",
"description": "Static CMS core application.",
"repository": "https://github.com/StaticJsCMS/static-cms",
@ -20,7 +20,6 @@
"format:prettier": "prettier \"{{src,scripts,website}/**/,}*.{js,jsx,ts,tsx,css}\"",
"format": "run-s \"lint:js --fix --quiet\" \"format:prettier --write\"",
"lint-quiet": "run-p -c --aggregate-output \"lint:* --quiet\"",
"lint:css": "stylelint --ignore-path .gitignore \"{src/**/*.{css,js,jsx,ts,tsx},website/**/*.css}\"",
"lint:format": "prettier \"src/**/*.{js,jsx,ts,tsx,css}\" --list-different",
"lint:js": "eslint --color --ignore-path .gitignore \"src/**/*.{js,jsx,ts,tsx}\"",
"lint": "run-p -c --aggregate-output \"lint:*\"",
@ -216,10 +215,6 @@
"simple-git": "3.14.1",
"source-map-loader": "4.0.0",
"style-loader": "3.3.1",
"stylelint": "14.12.1",
"stylelint-config-standard-scss": "3.0.0",
"stylelint-config-styled-components": "0.1.1",
"stylelint-processor-styled-components": "1.10.0",
"to-string-loader": "1.2.0",
"typescript": "4.8.4",
"webpack": "5.74.0",

View File

@ -64,8 +64,11 @@ export function authenticateUser() {
dispatch(doneAuthenticating());
}
})
.catch((error: Error) => {
dispatch(authError(error));
.catch((error: unknown) => {
console.error(error);
if (error instanceof Error) {
dispatch(authError(error));
}
dispatch(logoutUser());
});
};

View File

@ -386,7 +386,7 @@ export async function handleLocalBackend(originalConfig: Config) {
});
}
export function loadConfig(manualConfig: Config | undefined, onLoad: () => unknown) {
export function loadConfig(manualConfig: Config | undefined, onLoad: (config: Config) => unknown) {
if (window.CMS_CONFIG) {
return configLoaded(window.CMS_CONFIG);
}
@ -405,7 +405,7 @@ export function loadConfig(manualConfig: Config | undefined, onLoad: () => unkno
dispatch(configLoaded(config));
if (typeof onLoad === 'function') {
onLoad();
onLoad(config);
}
} catch (error: unknown) {
console.error(error);

View File

@ -76,9 +76,14 @@ const emptyAsset = createAssetProxy({
}),
});
export function getAsset(collection: Collection, entry: Entry, path: string, field?: Field) {
export function getAsset(
collection: Collection | null | undefined,
entry: Entry | null | undefined,
path: string,
field?: Field,
) {
return (dispatch: ThunkDispatch<RootState, {}, AnyAction>, getState: () => RootState) => {
if (!path) {
if (!collection || !entry || !path) {
return emptyAsset;
}

View File

@ -1,25 +1,9 @@
import Button from '@mui/material/Button';
import { styled } from '@mui/material/styles';
import TextField from '@mui/material/TextField';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import AuthenticationPage from '../../components/UI/AuthenticationPage';
import { colors } from '../../components/UI/styles';
import type { ChangeEvent, FormEvent } from 'react';
import type { AuthenticationPageProps, TranslatedProps, User } from '../../interface';
const StyledAuthForm = styled('form')`
width: 350px;
display: flex;
flex-direction: column;
gap: 16px;
`;
const ErrorMessage = styled('div')`
color: ${colors.errorText};
`;
function useNetlifyIdentifyEvent(eventName: 'login', callback: (login: User) => void): void;
function useNetlifyIdentifyEvent(eventName: 'logout', callback: () => void): void;
function useNetlifyIdentifyEvent(eventName: 'error', callback: (err: Error) => void): void;
@ -39,15 +23,12 @@ export interface GitGatewayAuthenticationPageProps
}
const GitGatewayAuthenticationPage = ({
inProgress = false,
config,
onLogin,
handleAuth,
t,
}: GitGatewayAuthenticationPageProps) => {
const [loggingIn, setLoggingIn] = useState(false);
const [loggedIn, setLoggedIn] = useState(false);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState<{
identity?: string;
server?: string;
@ -57,16 +38,28 @@ const GitGatewayAuthenticationPage = ({
useEffect(() => {
if (!loggedIn && window.netlifyIdentity && window.netlifyIdentity.currentUser()) {
onLogin(window.netlifyIdentity.currentUser());
window.netlifyIdentity.close();
setLoggingIn(true);
setTimeout(() => {
if (!window.netlifyIdentity) {
setLoggingIn(false);
return;
}
onLogin(window.netlifyIdentity.currentUser());
setLoggedIn(true);
window.netlifyIdentity.close();
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleIdentityLogin = useCallback(
(user: User) => {
onLogin(user);
window.netlifyIdentity?.close();
setLoggingIn(true);
setTimeout(() => {
onLogin(user);
setLoggedIn(true);
window.netlifyIdentity?.close();
});
},
[onLogin],
);
@ -94,130 +87,45 @@ const GitGatewayAuthenticationPage = ({
const handleIdentity = useCallback(() => {
const user = window.netlifyIdentity?.currentUser();
if (user) {
onLogin(user);
setLoggingIn(true);
setTimeout(() => {
onLogin(user);
setLoggedIn(true);
});
} else {
window.netlifyIdentity?.open();
}
}, [onLogin]);
const handleEmailChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value);
}, []);
const pageContent = useMemo(() => {
if (!window.netlifyIdentity) {
return t('auth.errors.netlifyIdentityNotFound');
}
const handlePasswordChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
setPassword(event.target.value);
}, []);
const handleLogin = useCallback(
async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const validationErrors: typeof errors = {};
if (!email) {
validationErrors.email = t('auth.errors.email');
}
if (!password) {
validationErrors.password = t('auth.errors.password');
}
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
let response: User | string;
try {
response = await handleAuth(email, password);
} catch (e: unknown) {
if (e instanceof Error) {
response = e.message;
} else {
response = 'Unknown authentication error';
}
}
if (typeof response === 'string') {
setErrors({ server: response });
setLoggedIn(false);
return;
}
onLogin(response);
},
[email, handleAuth, onLogin, password, t],
);
if (window.netlifyIdentity) {
if (errors.identity) {
return (
<AuthenticationPage
logoUrl={config.logo_url}
siteUrl={config.site_url}
onLogin={handleIdentity}
pageContent={
<a
href="https://docs.netlify.com/visitor-access/git-gateway/#setup-and-settings"
target="_blank"
rel="noopener noreferrer"
>
{errors.identity}
</a>
}
t={t}
/>
);
} else {
return (
<AuthenticationPage
logoUrl={config.logo_url}
siteUrl={config.site_url}
onLogin={handleIdentity}
buttonContent={t('auth.loginWithNetlifyIdentity')}
t={t}
/>
<a
href="https://docs.netlify.com/visitor-access/git-gateway/#setup-and-settings"
target="_blank"
rel="noopener noreferrer"
>
{errors.identity}
</a>
);
}
}
return null;
}, [errors.identity, t]);
return (
<AuthenticationPage
key="git-gateway-auth"
logoUrl={config.logo_url}
siteUrl={config.site_url}
pageContent={
<StyledAuthForm onSubmit={handleLogin}>
{!errors.server ? null : <ErrorMessage>{String(errors.server)}</ErrorMessage>}
<TextField
type="text"
name="email"
label="Email"
value={email}
onChange={handleEmailChange}
fullWidth
variant="outlined"
error={Boolean(errors.email)}
helperText={errors.email ?? undefined}
/>
<TextField
type="password"
name="password"
label="Password"
value={password}
onChange={handlePasswordChange}
fullWidth
variant="outlined"
error={Boolean(errors.password)}
helperText={errors.password ?? undefined}
/>
<Button
variant="contained"
type="submit"
disabled={inProgress}
sx={{ width: 120, alignSelf: 'center' }}
>
{inProgress ? t('auth.loggingIn') : t('auth.login')}
</Button>
</StyledAuthForm>
}
onLogin={handleIdentity}
buttonContent={t('auth.loginWithNetlifyIdentity')}
pageContent={pageContent}
loginDisabled={loggingIn}
t={t}
/>
);

View File

@ -1,10 +1,9 @@
import React, { useCallback } from 'react';
import GoTrue from 'gotrue-js';
import ini from 'ini';
import jwtDecode from 'jwt-decode';
import get from 'lodash/get';
import intersection from 'lodash/intersection';
import pick from 'lodash/pick';
import React, { useCallback } from 'react';
import {
AccessTokenError,
@ -25,22 +24,22 @@ import GitHubAPI from './GitHubAPI';
import GitLabAPI from './GitLabAPI';
import { getClient } from './netlify-lfs-client';
import type { ApiRequest, Cursor } from '../../lib/util';
import type {
AuthenticationPageProps,
BackendClass,
BackendEntry,
Config,
Credentials,
DisplayURL,
DisplayURLObject,
BackendEntry,
BackendClass,
ImplementationFile,
PersistOptions,
User,
TranslatedProps,
AuthenticationPageProps,
User,
} from '../../interface';
import type { Client } from './netlify-lfs-client';
import type { ApiRequest, Cursor } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
import type { Client } from './netlify-lfs-client';
const STATUS_PAGE = 'https://www.netlifystatus.com';
const GIT_GATEWAY_STATUS_ENDPOINT = `${STATUS_PAGE}/api/v2/components.json`;
@ -224,33 +223,18 @@ export default class GitGateway implements BackendClass {
return this.authClient;
}
await initPromise;
if (window.netlifyIdentity) {
this.authClient = {
logout: () => window.netlifyIdentity?.logout(),
currentUser: () => window.netlifyIdentity?.currentUser(),
clearStore: () => {
const store = window.netlifyIdentity?.store;
if (store) {
store.user = null;
store.modal.page = 'login';
store.saving = false;
}
},
};
} else {
const goTrue = new GoTrue({ APIUrl: this.apiUrl });
this.authClient = {
logout: () => {
const user = goTrue.currentUser();
if (user) {
return user.logout();
}
},
currentUser: () => goTrue.currentUser(),
login: goTrue.login.bind(goTrue),
clearStore: () => undefined,
};
}
this.authClient = {
logout: () => window.netlifyIdentity?.logout(),
currentUser: () => window.netlifyIdentity?.currentUser(),
clearStore: () => {
const store = window.netlifyIdentity?.store;
if (store) {
store.user = null;
store.modal.page = 'login';
store.saving = false;
}
},
};
}
requestFunction = (req: ApiRequest) =>

View File

@ -228,7 +228,7 @@ export default class TestBackend implements BackendClass {
async getMediaFile(path: string) {
const asset = getFile(path, window.repoFiles).content as AssetProxy;
const url = asset.toString();
const url = asset?.toString() ?? '';
const name = basename(path);
const blob = await fetch(url).then(res => res.blob());
const fileObj = new File([blob], name);

View File

@ -86,8 +86,10 @@ function bootstrap(opts?: { config?: Config; autoInitialize?: boolean }) {
}
store.dispatch(
loadConfig(config, function onLoad() {
store.dispatch(authenticateUser() as unknown as AnyAction);
loadConfig(config, function onLoad(config) {
if (config.backend.name !== 'git-gateway') {
store.dispatch(authenticateUser() as unknown as AnyAction);
}
}) as AnyAction,
);

View File

@ -28,8 +28,6 @@ import { selectIsLoadingAsset } from '../../../reducers/medias';
import type { ComponentType } from 'react';
import type { ConnectedProps } from 'react-redux';
import type {
Collection,
Entry,
Field,
FieldsErrors,
GetAssetFunction,
@ -178,12 +176,12 @@ const EditorControl = ({
const errors = useMemo(() => fieldsErrors[path] ?? [], [fieldsErrors, path]);
const hasErrors = (submitted || dirty) && Boolean(errors.length);
const handleGetAsset = useCallback(
(collection: Collection, entry: Entry): GetAssetFunction =>
(path: string, field?: Field) => {
return getAsset(collection, entry, path, field);
},
[getAsset],
const handleGetAsset: GetAssetFunction = useMemo(
() => (path: string, field?: Field) => {
return getAsset(collection, entry, path, field);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[collection],
);
useEffect(() => {
@ -211,6 +209,7 @@ const EditorControl = ({
<ControlContainer className={className} $isHidden={isHidden}>
<>
{React.createElement(widget.control, {
key: `field_${path}`,
clearFieldErrors,
clearSearch,
collection,
@ -219,7 +218,7 @@ const EditorControl = ({
field,
fieldsErrors,
submitted,
getAsset: handleGetAsset(collection, entry),
getAsset: handleGetAsset,
isDisabled: isDisabled ?? false,
isFetching,
isFieldDuplicate,
@ -241,9 +240,13 @@ const EditorControl = ({
i18n,
hasErrors,
})}
{fieldHint && <ControlHint $error={hasErrors}>{fieldHint}</ControlHint>}
{fieldHint ? (
<ControlHint key="hint" $error={hasErrors}>
{fieldHint}
</ControlHint>
) : null}
{hasErrors ? (
<ControlErrorsList>
<ControlErrorsList key="errors">
{errors.map(error => {
return (
error.message &&

View File

@ -193,11 +193,11 @@ const EditorControlPane = ({
) : null}
{fields
.filter(f => f.widget !== 'hidden')
.map((field, i) => {
.map(field => {
const isTranslatable = isFieldTranslatable(field, locale, i18n?.defaultLocale);
const isDuplicate = isFieldDuplicate(field, locale, i18n?.defaultLocale);
const isHidden = isFieldHidden(field, locale, i18n?.defaultLocale);
const key = i18n ? `${locale}_${i}` : i;
const key = i18n ? `field-${locale}_${field.name}` : `field-${field.name}`;
return (
<EditorControl

View File

@ -3,12 +3,12 @@ import React, { useMemo } from 'react';
import type { ReactNode } from 'react';
import type { TemplatePreviewComponent, TemplatePreviewProps } from '../../../interface';
interface PreviewContentProps {
interface EditorPreviewContentProps {
previewComponent?: TemplatePreviewComponent;
previewProps: TemplatePreviewProps;
}
const PreviewContent = ({ previewComponent, previewProps }: PreviewContentProps) => {
const EditorPreviewContent = ({ previewComponent, previewProps }: EditorPreviewContentProps) => {
return useMemo(() => {
let children: ReactNode;
if (!previewComponent) {
@ -23,4 +23,4 @@ const PreviewContent = ({ previewComponent, previewProps }: PreviewContentProps)
}, [previewComponent, previewProps]);
};
export default PreviewContent;
export default EditorPreviewContent;

View File

@ -8,11 +8,11 @@ import { ScrollSyncPane } from 'react-scroll-sync';
import { getAsset as getAssetAction } from '../../../actions/media';
import { lengths } from '../../../components/UI/styles';
import { INFERABLE_FIELDS } from '../../../constants/fieldInference';
import { getPreviewStyles, getPreviewTemplate, resolveWidget } from '../../../lib/registry';
import { selectInferedField, selectTemplateName } from '../../../lib/util/collection.util';
import { selectTemplateName, useInferedFields } from '../../../lib/util/collection.util';
import { selectField } from '../../../lib/util/field.util';
import { selectIsLoadingAsset } from '../../../reducers/medias';
import { getTypedFieldForValue } from '../../../widgets/list/typedListHelpers';
import { ErrorBoundary } from '../../UI';
import EditorPreview from './EditorPreview';
import EditorPreviewContent from './EditorPreviewContent';
@ -27,6 +27,8 @@ import type {
EntryData,
Field,
GetAssetFunction,
ListField,
RenderedField,
TemplatePreviewProps,
TranslatedProps,
ValueOrNestedValue,
@ -42,6 +44,20 @@ const PreviewPaneFrame = styled(Frame)`
overflow: auto;
`;
const FrameGlobalStyles = `
body {
margin: 0;
}
img {
max-width: 100%;
}
.frame-content {
padding: 16px;
}
`;
const PreviewPaneWrapper = styled('div')`
width: 100%;
height: 100%;
@ -49,6 +65,7 @@ const PreviewPaneWrapper = styled('div')`
background: #fff;
border-radius: ${lengths.borderRadius};
overflow: auto;
padding: 16px;
`;
const StyledPreviewContent = styled('div')`
@ -57,8 +74,7 @@ const StyledPreviewContent = styled('div')`
right: 0;
position: absolute;
height: calc(100vh - 64px);
overflow-y: auto;
padding: 16px;
overflow: hidden;
`;
/**
@ -85,10 +101,7 @@ function getWidgetFor(
}
const value = values?.[field.name];
let fieldWithWidgets: Omit<Field, 'fields' | 'field'> & {
fields?: ReactNode[];
field?: ReactNode;
} = Object.entries(field).reduce((acc, [key, fieldValue]) => {
let fieldWithWidgets: RenderedField = Object.entries(field).reduce((acc, [key, fieldValue]) => {
if (!['fields', 'fields'].includes(key)) {
acc[key] = fieldValue;
}
@ -108,6 +121,18 @@ function getWidgetFor(
value as EntryData | EntryData[],
),
};
} else if ('types' in field && field.types) {
fieldWithWidgets = {
...fieldWithWidgets,
fields: getTypedNestedWidgets(
collection,
field,
entry,
inferedFields,
getAsset,
value as EntryData[],
),
};
}
const labelledWidgets = ['string', 'text', 'number'];
@ -135,7 +160,9 @@ function getWidgetFor(
</div>
);
}
return renderedValue ? getWidget(field, renderedValue, entry, getAsset) : null;
return renderedValue
? getWidget(fieldWithWidgets, collection, renderedValue, entry, getAsset)
: null;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -153,7 +180,8 @@ function isReactFragment(value: any): value is ReactFragment {
}
function getWidget(
field: Field,
field: RenderedField,
collection: Collection,
value: ValueOrNestedValue | ReactNode,
entry: Entry,
getAsset: GetAssetFunction,
@ -175,6 +203,7 @@ function getWidget(
key={key}
field={field}
getAsset={getAsset}
collection={collection}
value={
value &&
!widget.allowMapValue &&
@ -218,6 +247,37 @@ function widgetsForNestedFields(
.filter(widget => Boolean(widget)) as JSX.Element[];
}
/**
* Retrieves widgets for nested fields (children of object/list fields)
*/
function getTypedNestedWidgets(
collection: Collection,
field: ListField,
entry: Entry,
inferedFields: Record<string, InferredField>,
getAsset: GetAssetFunction,
values: EntryData[],
) {
return values
.flatMap((value, index) => {
const itemType = getTypedFieldForValue(field, value ?? {}, index);
if (!itemType) {
return null;
}
return widgetsForNestedFields(
collection,
itemType.fields,
entry,
inferedFields,
getAsset,
itemType.fields,
value,
);
})
.filter(Boolean);
}
/**
* Retrieves widgets for nested fields (children of object/list fields)
*/
@ -260,29 +320,21 @@ function getNestedWidgets(
const PreviewPane = (props: TranslatedProps<EditorPreviewPaneProps>) => {
const { entry, collection, config, fields, previewInFrame, getAsset, t } = props;
const inferedFields = useMemo(() => {
const titleField = selectInferedField(collection, 'title');
const shortTitleField = selectInferedField(collection, 'shortTitle');
const authorField = selectInferedField(collection, 'author');
const iFields: Record<string, InferredField> = {};
if (titleField) {
iFields[titleField] = INFERABLE_FIELDS.title;
}
if (shortTitleField) {
iFields[shortTitleField] = INFERABLE_FIELDS.shortTitle;
}
if (authorField) {
iFields[authorField] = INFERABLE_FIELDS.author;
}
return iFields;
}, [collection]);
const inferedFields = useInferedFields(collection);
const handleGetAsset = useCallback(
(path: string, field?: Field) => {
return getAsset(collection, entry, path, field);
},
[collection, entry, getAsset],
// eslint-disable-next-line react-hooks/exhaustive-deps
[collection],
);
const widgetFor = useCallback(
(name: string) => {
return getWidgetFor(collection, name, fields, entry, inferedFields, handleGetAsset);
},
[collection, entry, fields, handleGetAsset, inferedFields],
);
/**
@ -305,7 +357,7 @@ const PreviewPane = (props: TranslatedProps<EditorPreviewPaneProps>) => {
if (Array.isArray(value)) {
return value.map(val => {
const widgets = nestedFields.reduce((acc, field, index) => {
acc[field.name] = <div key={index}>{getWidget(field, val, entry, handleGetAsset)}</div>;
acc[field.name] = <div key={index}>{widgetFor(field.name)}</div>;
return acc;
}, {} as Record<string, ReactNode>);
return { data: val, widgets };
@ -315,29 +367,24 @@ const PreviewPane = (props: TranslatedProps<EditorPreviewPaneProps>) => {
return {
data: value,
widgets: nestedFields.reduce((acc, field, index) => {
acc[field.name] = <div key={index}>{getWidget(field, value, entry, handleGetAsset)}</div>;
acc[field.name] = <div key={index}>{widgetFor(field.name)}</div>;
return acc;
}, {} as Record<string, ReactNode>),
};
},
[entry, fields, handleGetAsset],
);
const widgetFor = useCallback(
(name: string) => {
return getWidgetFor(collection, name, fields, entry, inferedFields, handleGetAsset);
},
[collection, entry, fields, handleGetAsset, inferedFields],
[entry.data, fields, widgetFor],
);
const previewStyles = useMemo(
() =>
getPreviewStyles().map((style, i) => {
() => [
...getPreviewStyles().map((style, i) => {
if (style.raw) {
return <style key={i}>{style.value}</style>;
}
return <link key={i} href={style.value} type="text/css" rel="stylesheet" />;
}),
<style key="global">{FrameGlobalStyles}</style>,
],
[],
);

View File

@ -3,7 +3,7 @@ import React from 'react';
import type { WidgetPreviewComponent, WidgetPreviewProps } from '../../../interface';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
interface PreviewHOCProps extends WidgetPreviewProps {
interface PreviewHOCProps extends Omit<WidgetPreviewProps, 'widgetFor'> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
previewComponent: WidgetPreviewComponent;
}

View File

@ -172,7 +172,9 @@ const EditorToolbar = ({
if (canCreate) {
items.push(
<MenuItem key="duplicate" onClick={handleDuplicate}>{t('editor.editorToolbar.duplicate')}</MenuItem>,
<MenuItem key="duplicate" onClick={handleDuplicate}>
{t('editor.editorToolbar.duplicate')}
</MenuItem>,
);
}

View File

@ -7,11 +7,7 @@ import {
ProxyBackend,
TestBackend,
} from './backends';
import {
registerBackend,
registerLocale,
registerWidget,
} from './lib/registry';
import { registerBackend, registerLocale, registerWidget } from './lib/registry';
import { locales } from './locales';
import {
BooleanWidget,

View File

@ -1,5 +1,8 @@
import type { EditorPlugin, EditorType, WidgetRule } from '@toast-ui/editor/types/editor';
import type { ToolbarItemOptions } from '@toast-ui/editor/types/ui';
import type {
EditorPlugin as MarkdownPlugin,
EditorType as MarkdownEditorType,
} from '@toast-ui/editor/types/editor';
import type { ToolbarItemOptions as MarkdownToolbarItemOptions } from '@toast-ui/editor/types/ui';
import type { PropertiesSchema } from 'ajv/dist/types/json-schema';
import type { ComponentType, ReactNode } from 'react';
import type { t, TranslateProps as ReactPolyglotTranslateProps } from 'react-polyglot';
@ -259,8 +262,9 @@ export interface WidgetControlProps<T, F extends Field = Field> {
}
export interface WidgetPreviewProps<T = unknown, F extends Field = Field> {
collection: Collection;
entry: Entry;
field: F;
field: RenderedField<F>;
getAsset: GetAssetFunction;
resolveWidget: <W = unknown, WF extends Field = Field>(name: string) => Widget<W, WF>;
value: T | undefined | null;
@ -525,6 +529,10 @@ export type AuthScope = 'repo' | 'public_repo';
export type SlugEncoding = 'unicode' | 'ascii';
export type RenderedField<T extends Field = Field> = Omit<T, 'fields'> & {
fields?: ReactNode[];
};
export interface BaseField {
name: string;
label?: string;
@ -898,25 +906,25 @@ export interface PreviewStyle {
raw: boolean;
}
export interface WidgetRulesFactoryProps {
export interface MarkdownPluginFactoryProps {
getAsset: GetAssetFunction;
field: MarkdownField;
mode: 'editor' | 'preview';
}
export type WidgetRulesFactory = (props: WidgetRulesFactoryProps) => WidgetRule[];
export type MarkdownPluginFactory = (props: MarkdownPluginFactoryProps) => MarkdownPlugin;
export interface ToolbarItemsFactoryProps {
imageToolbarButton: ToolbarItemOptions;
export interface MarkdownToolbarItemsFactoryProps {
imageToolbarButton: MarkdownToolbarItemOptions;
}
export type ToolbarItemsFactory = (
props: ToolbarItemsFactoryProps,
) => (string | ToolbarItemOptions)[][];
export type MarkdownToolbarItemsFactory = (
props: MarkdownToolbarItemsFactoryProps,
) => (string | MarkdownToolbarItemOptions)[][];
export interface MarkdownEditorOptions {
widgetRules?: WidgetRulesFactory;
initialEditType?: EditorType;
initialEditType?: MarkdownEditorType;
height?: string;
toolbarItems?: ToolbarItemsFactory;
plugins?: EditorPlugin[];
toolbarItems?: MarkdownToolbarItemsFactory;
plugins?: MarkdownPluginFactory[];
}

View File

@ -133,7 +133,11 @@ export function registerWidget<T = unknown>(
name: string | WidgetParam<T> | WidgetParam[],
control?: string | Widget<T>['control'],
preview?: Widget<T>['preview'],
{ schema, validator, getValidValue }: WidgetOptions = {},
{
schema,
validator = () => false,
getValidValue = (value: unknown) => value,
}: WidgetOptions = {},
): void {
if (Array.isArray(name)) {
name.forEach(widget => {

View File

@ -1,4 +1,5 @@
import get from 'lodash/get';
import { useMemo } from 'react';
import { FILES, FOLDER } from '../../constants/collectionTypes';
import { COMMIT_AUTHOR, COMMIT_DATE } from '../../constants/commitProps';
@ -15,6 +16,7 @@ import { selectField } from './field.util';
import { selectMediaFolder } from './media.util';
import type { Backend } from '../../backend';
import type { InferredField } from '../../constants/fieldInference';
import type {
Collection,
CollectionFile,
@ -415,3 +417,23 @@ export function selectInferedField(collection: Collection, fieldName: string) {
return null;
}
export function useInferedFields(collection: Collection) {
return useMemo(() => {
const titleField = selectInferedField(collection, 'title');
const shortTitleField = selectInferedField(collection, 'shortTitle');
const authorField = selectInferedField(collection, 'author');
const iFields: Record<string, InferredField> = {};
if (titleField) {
iFields[titleField] = INFERABLE_FIELDS.title;
}
if (shortTitleField) {
iFields[shortTitleField] = INFERABLE_FIELDS.shortTitle;
}
if (authorField) {
iFields[authorField] = INFERABLE_FIELDS.author;
}
return iFields;
}, [collection]);
}

View File

@ -27,3 +27,38 @@ export function getFieldLabel(field: Field, t: t) {
field.required === false ? ` (${t('editor.editorControl.field.optional')})` : ''
}`}`;
}
function findField(field: Field | undefined, path: string[]): Field | null {
if (!field) {
return null;
}
if (path.length === 0) {
return field;
}
if (!('fields' in field && field.fields)) {
return null;
}
const name = path.slice(0, 1)[0];
const rest = path.slice(1);
return findField(
field.fields.find(f => f.name === name),
rest,
);
}
export function getField(field: Field | Field[], path: string): Field | null {
return findField(
Array.isArray(field)
? {
widget: 'object',
name: 'root',
fields: field,
}
: field,
path.split('.'),
);
}

View File

@ -3,7 +3,7 @@ import isNumber from 'lodash/isNumber';
export function validateMinMax(
t: (key: string, options: unknown) => string,
fieldLabel: string,
value?: string | string[] | undefined | null,
value?: string | number | (string | number)[] | undefined | null,
min?: number,
max?: number,
) {
@ -19,11 +19,17 @@ export function validateMinMax(
};
}
if ([min, max, value?.length].every(isNumber) && (value!.length < min! || value!.length > max!)) {
if (typeof value === 'string' || typeof value === 'number') {
return false;
}
const length = value?.length ?? 0;
if ([min, max, length].every(isNumber) && (length < min! || length > max!)) {
return minMaxError(min === max ? 'rangeCountExact' : 'rangeCount');
} else if (isNumber(min) && min > 0 && value?.length && value.length < min) {
} else if (isNumber(min) && min > 0 && length < min) {
return minMaxError('rangeMin');
} else if (isNumber(max) && value?.length && value.length > max) {
} else if (isNumber(max) && length > max) {
return minMaxError('rangeMax');
}
}

View File

@ -14,6 +14,7 @@ const en: LocalePhrasesRoot = {
password: 'Please enter your password.',
authTitle: 'Error logging in',
authBody: '%{details}',
netlifyIdentityNotFound: 'Netlify Identity plugin not found',
identitySettings:
'Unable to access identity settings. When using git-gateway backend make sure to enable Identity service and Git Gateway.',
},

View File

@ -14,7 +14,7 @@ export default class AssetProxy {
field?: Field;
constructor({ url, file, path, field }: AssetProxyArgs) {
this.url = url ? url : window.URL.createObjectURL(file as Blob);
this.url = url ? url : file ? window.URL.createObjectURL(file as Blob) : '';
this.fileObj = file;
this.path = path;
this.field = field;

View File

@ -39,9 +39,7 @@ const StyledCodeControlContent = styled(
${
$collapsed
? `
visibility: hidden;
height: 0;
width: 0;
display: none;
`
: ''
}

View File

@ -36,9 +36,7 @@ const StyledColorControlContent = styled(
${
$collapsed
? `
visibility: hidden;
height: 0;
width: 0;
display: none;
`
: `
padding: 16px;

View File

@ -1,20 +1,34 @@
import React from 'react';
import { styled } from '@mui/material/styles';
import React, { useEffect, useState } from 'react';
import WidgetPreviewContainer from '../../components/UI/WidgetPreviewContainer';
import type { FileOrImageField, WidgetPreviewProps, GetAssetFunction } from '../../interface';
import type { FileOrImageField, GetAssetFunction, WidgetPreviewProps } from '../../interface';
interface FileLinkProps {
href: string;
path: string;
value: string;
getAsset: GetAssetFunction;
field: FileOrImageField;
}
const FileLink = styled(({ href, path }: FileLinkProps) => (
<a href={href} rel="noopener noreferrer" target="_blank">
{path}
</a>
))`
const FileLink = ({ value, getAsset, field }: FileLinkProps) => {
const [assetSource, setAssetSource] = useState('');
useEffect(() => {
if (!value || Array.isArray(value)) {
return;
}
setAssetSource(getAsset(value, field)?.toString() ?? '');
}, [field, getAsset, value]);
return (
<a href={assetSource} rel="noopener noreferrer" target="_blank">
{value}
</a>
);
};
const StyledFileLink = styled(FileLink)`
display: block;
`;
@ -28,7 +42,7 @@ function FileLinkList({ values, getAsset, field }: FileLinkListProps) {
return (
<div>
{values.map(value => (
<FileLink key={value} path={value} href={getAsset(value, field).toString()} />
<StyledFileLink key={value} value={value} getAsset={getAsset} field={field} />
))}
</div>
);
@ -47,7 +61,7 @@ function FileContent({
return <FileLinkList values={value} getAsset={getAsset} field={field} />;
}
return <FileLink key={value} path={value} href={getAsset(value, field).toString()} />;
return <StyledFileLink key={value} value={value} getAsset={getAsset} field={field} />;
}
function FilePreview(props: WidgetPreviewProps<string | string[], FileOrImageField>) {

View File

@ -3,8 +3,8 @@ import PhotoIcon from '@mui/icons-material/Photo';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import { styled } from '@mui/material/styles';
import { arrayMoveImmutable as arrayMove } from 'array-move';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { arrayMoveImmutable } from 'array-move';
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
import uuid from 'uuid/v4';
@ -12,6 +12,7 @@ import ObjectWidgetTopBar from '../../components/UI/ObjectWidgetTopBar';
import Outline from '../../components/UI/Outline';
import { borders, effects, lengths, shadows } from '../../components/UI/styles';
import { basename, transientOptions } from '../../lib/util';
import { isEmpty } from '../../lib/util/string.util';
import type { MouseEvent, MouseEventHandler } from 'react';
import type { FileOrImageField, GetAssetFunction, WidgetControlProps } from '../../interface';
@ -40,9 +41,7 @@ const StyledFileControlContent = styled(
${
$collapsed
? `
visibility: hidden;
height: 0;
width: 0;
display: none;
`
: `
padding: 16px;
@ -135,10 +134,15 @@ interface SortableImageProps {
const SortableImage = SortableElement<SortableImageProps>(
({ itemValue, getAsset, field, onRemove, onReplace }: SortableImageProps) => {
const [assetSource, setAssetSource] = useState('');
useEffect(() => {
setAssetSource(getAsset(itemValue, field)?.toString() ?? '');
}, [field, getAsset, itemValue]);
return (
<div>
<ImageWrapper key="image-wrapper" $sortable>
<Image key="image" src={getAsset(itemValue, field)?.toString() ?? ''} />
<Image key="image" src={assetSource} />
</ImageWrapper>
<SortableImageButtons
key="image-buttons"
@ -215,35 +219,41 @@ export function getValidValue(value: string | string[] | null | undefined) {
return value;
}
function sizeOfValue(value: string | string[] | null | undefined) {
if (Array.isArray(value)) {
return value.length;
}
return value ? 1 : 0;
}
interface WithImageOptions {
forImage?: boolean;
}
export default function withFileControl({ forImage = false }: WithImageOptions = {}) {
const FileControl = ({
path,
value,
field,
onChange,
openMediaLibrary,
clearMediaControl,
removeInsertedMedia,
removeMediaControl,
getAsset,
mediaPaths,
hasErrors,
t,
}: WidgetControlProps<string | string[], FileOrImageField>) => {
const FileControl = memo((props: WidgetControlProps<string | string[], FileOrImageField>) => {
const {
value,
field,
onChange,
openMediaLibrary,
clearMediaControl,
removeInsertedMedia,
removeMediaControl,
getAsset,
mediaPaths,
hasErrors,
t,
} = props;
const controlID = useMemo(() => uuid(), []);
const [collapsed, setCollapsed] = useState(false);
const [internalValue, setInternalValue] = useState(value ?? '');
const handleOnChange = useCallback(
(newValue: string | string[]) => {
if (newValue !== internalValue) {
setInternalValue(newValue);
setTimeout(() => {
onChange(newValue);
});
}
},
[internalValue, onChange],
);
const handleCollapseToggle = useCallback(() => {
setCollapsed(!collapsed);
@ -251,12 +261,12 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
useEffect(() => {
const mediaPath = mediaPaths[controlID];
if (mediaPath && mediaPath !== value) {
onChange(mediaPath);
} else if (mediaPath && mediaPath === value) {
if (mediaPath && mediaPath !== internalValue) {
handleOnChange(mediaPath);
} else if (mediaPath && mediaPath === internalValue) {
removeInsertedMedia(controlID);
}
}, [controlID, field, mediaPaths, onChange, removeInsertedMedia, path, value]);
}, [controlID, handleOnChange, internalValue, mediaPaths, removeInsertedMedia]);
useEffect(() => {
return () => {
@ -291,7 +301,7 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
controlID,
forImage,
privateUpload: field.private,
value: value ?? '',
value: internalValue,
allowMultiple:
'allow_multiple' in mediaLibraryFieldOptions
? mediaLibraryFieldOptions.allow_multiple ?? true
@ -300,7 +310,7 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
field,
});
},
[config, controlID, field, mediaLibraryFieldOptions, openMediaLibrary, value],
[config, controlID, field, mediaLibraryFieldOptions, openMediaLibrary, internalValue],
);
const handleUrl = useCallback(
@ -309,28 +319,29 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
const url = window.prompt(t(`editor.editorWidgets.${subject}.promptUrl`));
return onChange(url);
handleOnChange(url ?? '');
},
[onChange, t],
[handleOnChange, t],
);
const handleRemove = useCallback(
(e: MouseEvent) => {
e.preventDefault();
clearMediaControl(controlID);
return onChange('');
handleOnChange('');
},
[controlID, onChange, clearMediaControl],
[clearMediaControl, controlID, handleOnChange],
);
const onRemoveOne = useCallback(
(index: number) => () => {
if (Array.isArray(value)) {
value.splice(index, 1);
return onChange(sizeOfValue(value) > 0 ? [...value] : null);
if (Array.isArray(internalValue)) {
const newValue = [...internalValue];
newValue.splice(index, 1);
handleOnChange(newValue);
}
},
[onChange, value],
[handleOnChange, internalValue],
);
const onReplaceOne = useCallback(
@ -339,45 +350,86 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
controlID,
forImage,
privateUpload: field.private,
value: value ?? '',
value: internalValue,
replaceIndex: index,
allowMultiple: false,
config,
field,
});
},
[config, controlID, field, openMediaLibrary, value],
[config, controlID, field, openMediaLibrary, internalValue],
);
const onSortEnd = useCallback(
({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
if (Array.isArray(value)) {
const newValue = arrayMove(value, oldIndex, newIndex);
return onChange(newValue);
if (Array.isArray(internalValue)) {
const newValue = arrayMoveImmutable(internalValue, oldIndex, newIndex);
handleOnChange(newValue);
}
},
[onChange, value],
[handleOnChange, internalValue],
);
const renderFileLink = useCallback((value: string | undefined | null) => {
const renderFileLink = useCallback((link: string | undefined | null) => {
const size = MAX_DISPLAY_LENGTH;
if (!value || value.length <= size) {
return value;
if (!link || link.length <= size) {
return link;
}
const text = `${value.slice(0, size / 2)}\u2026${value.slice(-(size / 2) + 1)}`;
const text = `${link.slice(0, size / 2)}\u2026${link.slice(-(size / 2) + 1)}`;
return (
<FileLink key={`file-link-${text}`} href={value} rel="noopener" target="_blank">
<FileLink key={`file-link-${text}`} href={link} rel="noopener" target="_blank">
{text}
</FileLink>
);
}, []);
const renderFileLinks = useCallback(() => {
if (isMultiple(value)) {
const [assetSource, setAssetSource] = useState('');
useEffect(() => {
if (Array.isArray(internalValue)) {
return;
}
const newValue = getAsset(internalValue, field)?.toString() ?? '';
if (newValue !== internalValue) {
setAssetSource(newValue);
}
}, [field, getAsset, internalValue]);
const renderedImagesLinks = useMemo(() => {
if (forImage) {
if (!internalValue) {
return null;
}
if (isMultiple(internalValue)) {
return (
<SortableMultiImageWrapper
key="mulitple-image-wrapper"
items={internalValue}
onSortEnd={onSortEnd}
onRemoveOne={onRemoveOne}
onReplaceOne={onReplaceOne}
distance={4}
getAsset={getAsset}
field={field}
axis="xy"
lockToContainerEdges={true}
></SortableMultiImageWrapper>
);
}
return (
<ImageWrapper key="single-image-wrapper">
<Image key="single-image" src={assetSource} />
</ImageWrapper>
);
}
if (isMultiple(internalValue)) {
return (
<FileLinks key="mulitple-file-links">
<FileLinkList key="file-links-list">
{value.map(val => (
{internalValue.map(val => (
<li key={val}>{renderFileLink(val)}</li>
))}
</FileLinkList>
@ -385,43 +437,22 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
);
}
return <FileLinks key="single-file-links">{renderFileLink(value)}</FileLinks>;
}, [renderFileLink, value]);
const renderImages = useCallback(() => {
if (!value) {
return null;
}
if (isMultiple(value)) {
return (
<SortableMultiImageWrapper
key="mulitple-image-wrapper"
items={value}
onSortEnd={onSortEnd}
onRemoveOne={onRemoveOne}
onReplaceOne={onReplaceOne}
distance={4}
getAsset={getAsset}
field={field}
axis="xy"
lockToContainerEdges={true}
></SortableMultiImageWrapper>
);
}
const src = getAsset(value, field)?.toString() ?? '';
return (
<ImageWrapper key="single-image-wrapper">
<Image key="single-image" src={src || ''} />
</ImageWrapper>
);
}, [field, getAsset, onRemoveOne, onReplaceOne, onSortEnd, value]);
return <FileLinks key="single-file-links">{renderFileLink(internalValue)}</FileLinks>;
}, [
assetSource,
field,
getAsset,
internalValue,
onRemoveOne,
onReplaceOne,
onSortEnd,
renderFileLink,
]);
const content = useMemo(() => {
const subject = forImage ? 'image' : 'file';
if (!value) {
if (Array.isArray(internalValue) ? internalValue.length === 0 : isEmpty(internalValue)) {
return (
<StyledButtonWrapper>
<Button color="primary" variant="outlined" key="upload" onClick={handleChange}>
@ -443,7 +474,7 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
return (
<StyledSelection key="selection">
{forImage ? renderImages() : renderFileLinks()}
{renderedImagesLinks}
<StyledButtonWrapper key="controls">
<Button color="primary" variant="outlined" key="add-replace" onClick={handleChange}>
{t(
@ -467,32 +498,34 @@ export default function withFileControl({ forImage = false }: WithImageOptions =
</StyledSelection>
);
}, [
internalValue,
renderedImagesLinks,
handleChange,
t,
allowsMultiple,
chooseUrl,
handleChange,
handleRemove,
handleUrl,
renderFileLinks,
renderImages,
t,
value,
handleRemove,
]);
return (
<StyledFileControlWrapper key="file-control-wrapper">
<ObjectWidgetTopBar
key="file-control-top-bar"
collapsed={collapsed}
onCollapseToggle={handleCollapseToggle}
heading={field.label ?? field.name}
hasError={hasErrors}
t={t}
/>
<StyledFileControlContent $collapsed={collapsed}>{content}</StyledFileControlContent>
<Outline hasError={hasErrors} />
</StyledFileControlWrapper>
return useMemo(
() => (
<StyledFileControlWrapper key="file-control-wrapper">
<ObjectWidgetTopBar
key="file-control-top-bar"
collapsed={collapsed}
onCollapseToggle={handleCollapseToggle}
heading={field.label ?? field.name}
hasError={hasErrors}
t={t}
/>
<StyledFileControlContent $collapsed={collapsed}>{content}</StyledFileControlContent>
<Outline hasError={hasErrors} />
</StyledFileControlWrapper>
),
[collapsed, content, field.label, field.name, handleCollapseToggle, hasErrors, t],
);
};
});
FileControl.displayName = 'FileControl';

View File

@ -1,9 +1,9 @@
import React from 'react';
import { styled } from '@mui/material/styles';
import React, { useEffect, useState } from 'react';
import WidgetPreviewContainer from '../../components/UI/WidgetPreviewContainer';
import type { FileOrImageField, WidgetPreviewProps, GetAssetFunction } from '../../interface';
import type { FileOrImageField, GetAssetFunction, WidgetPreviewProps } from '../../interface';
interface StyledImageProps {
src: string;
@ -17,14 +17,19 @@ const StyledImage = styled(({ src }: StyledImageProps) => (
height: auto;
`;
interface StyledImageAsset {
interface ImageAssetProps {
getAsset: GetAssetFunction;
value: string;
field: FileOrImageField;
}
function StyledImageAsset({ getAsset, value, field }: StyledImageAsset) {
return <StyledImage src={getAsset(value, field).toString()} />;
function ImageAsset({ getAsset, value, field }: ImageAssetProps) {
const [assetSource, setAssetSource] = useState('');
useEffect(() => {
setAssetSource(getAsset(value, field)?.toString() ?? '');
}, [field, getAsset, value]);
return <StyledImage src={assetSource} />;
}
function ImagePreviewContent({
@ -40,13 +45,13 @@ function ImagePreviewContent({
return (
<>
{value.map(val => (
<StyledImageAsset key={val} value={val} getAsset={getAsset} field={field} />
<ImageAsset key={val} value={val} getAsset={getAsset} field={field} />
))}
</>
);
}
return <StyledImageAsset value={value} getAsset={getAsset} field={field} />;
return <ImageAsset value={value} getAsset={getAsset} field={field} />;
}
function ImagePreview(props: WidgetPreviewProps<string | string[], FileOrImageField>) {

View File

@ -42,9 +42,7 @@ const StyledSortableList = styled(
${
$collapsed
? `
visibility: hidden;
height: 0;
width: 0;
display: none;
`
: `
padding: 16px;

View File

@ -47,9 +47,7 @@ const StyledObjectFieldWrapper = styled(
${
$collapsed
? `
visibility: hidden;
height: 0;
width: 0;
display: none;
`
: ''
}

View File

@ -40,9 +40,7 @@ const StyledMapControlContent = styled(
${
$collapsed
? `
visibility: hidden;
height: 0;
width: 0;
display: none;
`
: ''
}

View File

@ -11,8 +11,8 @@ import { doesUrlFileExist } from '../../lib/util/fetch.util';
import { isNotNullish } from '../../lib/util/null.util';
import { isNotEmpty } from '../../lib/util/string.util';
import useEditorOptions from './hooks/useEditorOptions';
import usePlugins from './hooks/usePlugins';
import useToolbarItems from './hooks/useToolbarItems';
import useWidgetRules from './hooks/useWidgetRules';
import type { RefObject } from 'react';
import type { MarkdownField, MediaLibrary, WidgetControlProps } from '../../interface';
@ -151,38 +151,54 @@ const MarkdownControl = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [field, mediaPath]);
const { initialEditType, height, plugins, ...markdownEditorOptions } = useEditorOptions();
const widgetRules = useWidgetRules(markdownEditorOptions.widgetRules, { getAsset, field });
const { initialEditType, height, ...markdownEditorOptions } = useEditorOptions();
const plugins = usePlugins(markdownEditorOptions.plugins, { getAsset, field, mode: 'editor' });
const toolbarItems = useToolbarItems(markdownEditorOptions.toolbarItems, handleOpenMedialLibrary);
return (
<StyledEditorWrapper key="markdown-control-wrapper">
<FieldLabel
key="markdown-control-label"
isActive={hasFocus}
hasErrors={hasErrors}
onClick={handleLabelClick}
>
{label}
</FieldLabel>
<Editor
key="markdown-control-editor"
initialValue={internalValue}
previewStyle="vertical"
height={height}
initialEditType={initialEditType}
useCommandShortcut={true}
onChange={handleOnChange}
toolbarItems={toolbarItems}
ref={editorRef}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
autofocus={false}
widgetRules={widgetRules}
plugins={plugins}
/>
<Outline key="markdown-control-outline" hasLabel hasError={hasErrors} />
</StyledEditorWrapper>
return useMemo(
() => (
<StyledEditorWrapper key="markdown-control-wrapper">
<FieldLabel
key="markdown-control-label"
isActive={hasFocus}
hasErrors={hasErrors}
onClick={handleLabelClick}
>
{label}
</FieldLabel>
<Editor
key="markdown-control-editor"
initialValue={internalValue}
previewStyle="vertical"
height={height}
initialEditType={initialEditType}
useCommandShortcut={true}
onChange={handleOnChange}
toolbarItems={toolbarItems}
ref={editorRef}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
autofocus={false}
plugins={plugins}
/>
<Outline key="markdown-control-outline" hasLabel hasError={hasErrors} />
</StyledEditorWrapper>
),
[
editorRef,
handleLabelClick,
handleOnBlur,
handleOnChange,
handleOnFocus,
hasErrors,
hasFocus,
height,
initialEditType,
internalValue,
label,
plugins,
toolbarItems,
],
);
};

View File

@ -3,11 +3,14 @@ import React, { useEffect, useRef } from 'react';
import WidgetPreviewContainer from '../../components/UI/WidgetPreviewContainer';
import useEditorOptions from './hooks/useEditorOptions';
import usePlugins from './hooks/usePlugins';
import type { MarkdownField, WidgetPreviewProps } from '../../interface';
const MarkdownPreview = ({ value }: WidgetPreviewProps<string, MarkdownField>) => {
const { plugins } = useEditorOptions();
const MarkdownPreview = ({ value, getAsset, field }: WidgetPreviewProps<string, MarkdownField>) => {
const options = useEditorOptions();
const plugins = usePlugins(options.plugins, { getAsset, field, mode: 'preview' });
const viewer = useRef<Viewer | null>(null);
useEffect(() => {
@ -20,7 +23,12 @@ const MarkdownPreview = ({ value }: WidgetPreviewProps<string, MarkdownField>) =
return (
<WidgetPreviewContainer>
<Viewer ref={viewer} initialValue={value} plugins={plugins} />
<Viewer
ref={viewer}
initialValue={value}
customHTMLSanitizer={(content: string) => content}
plugins={plugins}
/>
</WidgetPreviewContainer>
);
};

View File

@ -1,35 +0,0 @@
import type { WidgetRulesFactory } from '../../../interface';
const imageFilePattern = /(!)?\[([^\]]*)\]\(([^)]+)\)/;
const defaultWidgetRules: WidgetRulesFactory = ({ getAsset, field }) => [
{
rule: imageFilePattern,
toDOM(text) {
const rule = imageFilePattern;
const matched = text.match(rule);
if (matched) {
if (matched?.length === 4) {
// Image
const img = document.createElement('img');
img.setAttribute('src', getAsset(matched[3] ?? '', field).url);
img.setAttribute('style', 'width: 100%;');
img.innerHTML = matched[2] ?? '';
return img;
} else {
// File
const a = document.createElement('a');
a.setAttribute('target', '_blank');
a.setAttribute('href', matched[2] ?? '');
a.innerHTML = matched[1] ?? '';
return a;
}
}
return document.createElement('div');
},
},
];
export default defaultWidgetRules;

View File

@ -0,0 +1,22 @@
import { useMemo } from 'react';
import imagePlugin from '../plugins/imagePlugin';
import type { MarkdownEditorOptions, MarkdownPluginFactoryProps } from '../../../interface';
const usePlugins = (
editorPlugins: MarkdownEditorOptions['plugins'] = [],
{ getAsset, field, mode }: MarkdownPluginFactoryProps,
) => {
return useMemo(() => {
const plugins = [imagePlugin({ getAsset, field, mode })];
if (plugins) {
plugins.push(...editorPlugins.map(editorPlugin => editorPlugin({ getAsset, field, mode })));
}
return plugins;
}, [editorPlugins, field, getAsset, mode]);
};
export default usePlugins;

View File

@ -1,20 +0,0 @@
import { useMemo } from 'react';
import defaultWidgetRules from '../config/widgetRules';
import type { WidgetRulesFactory, WidgetRulesFactoryProps } from '../../../interface';
const useWidgetRules = (
widgetRules: WidgetRulesFactory | undefined,
{ getAsset, field }: WidgetRulesFactoryProps,
) => {
return useMemo(() => {
const rules = defaultWidgetRules({ getAsset, field });
if (widgetRules) {
rules.push(...widgetRules({ getAsset, field }));
}
return rules;
}, [field, getAsset, widgetRules]);
};
export default useWidgetRules;

View File

@ -0,0 +1,43 @@
import type { CustomHTMLRenderer } from '@toast-ui/editor';
import type { LinkMdNode, MdNode } from '@toast-ui/editor/types/toastmark';
import type { MarkdownPluginFactory, MarkdownPluginFactoryProps } from '../../../interface';
function isLinkNode(node: MdNode): node is LinkMdNode {
return 'destination' in node;
}
const toHTMLRenderers: (props: MarkdownPluginFactoryProps) => CustomHTMLRenderer = ({
getAsset,
field,
}) => ({
image: (node: MdNode, { entering, skipChildren }) => {
if (entering && isLinkNode(node)) {
skipChildren();
return {
type: 'openTag',
tagName: 'img',
outerNewLine: true,
attributes: {
src: node.destination,
onerror: `this.onerror=null; this.src='${
node.destination
? getAsset(node.destination, field)?.toString() ?? node.destination
: ''
}'`,
},
selfClose: true,
};
}
return [];
},
});
const imagePlugin: MarkdownPluginFactory = props => {
return () => ({
toHTMLRenderers: toHTMLRenderers(props),
});
};
export default imagePlugin;

View File

@ -31,9 +31,7 @@ const StyledFieldsBox = styled(
${
$collapsed
? `
visibility: hidden;
height: 0;
width: 0;
display: none;
`
: `
padding: 16px;

View File

@ -5,9 +5,9 @@ import WidgetPreviewContainer from '../../components/UI/WidgetPreviewContainer';
import type { WidgetPreviewProps, ObjectField, ListField, ObjectValue } from '../../interface';
function ObjectPreview({
value,
field,
}: WidgetPreviewProps<ObjectValue | ObjectValue[], ObjectField | ListField>) {
return <WidgetPreviewContainer>{JSON.stringify(value, null, 2)}</WidgetPreviewContainer>;
return <WidgetPreviewContainer>{field.fields ?? null}</WidgetPreviewContainer>;
}
export default ObjectPreview;

View File

@ -7,17 +7,19 @@ import OutlinedInput from '@mui/material/OutlinedInput';
import Select from '@mui/material/Select';
import React, { useCallback, useMemo, useState } from 'react';
import { isNullish } from '../../lib/util/null.util';
import type { SelectChangeEvent } from '@mui/material/Select';
import type { SelectField, WidgetControlProps } from '../../interface';
interface Option {
label: string;
value: string;
value: string | number;
}
function convertToOption(raw: string | Option | undefined): Option | undefined {
if (typeof raw === 'string') {
return { label: raw, value: raw };
function convertToOption(raw: string | number | Option | undefined): Option | undefined {
if (typeof raw === 'string' || typeof raw === 'number') {
return { label: `${raw}`, value: raw };
}
return raw;
@ -29,15 +31,43 @@ const SelectControl = ({
value,
hasErrors,
onChange,
}: WidgetControlProps<string | string[], SelectField>) => {
}: WidgetControlProps<string | number | (string | number)[], SelectField>) => {
const [internalValue, setInternalValue] = useState(value);
const fieldOptions: (string | Option)[] = useMemo(() => field.options, [field.options]);
const isMultiple = useMemo(() => field.multiple ?? false, [field.multiple]);
const options = useMemo(
() => fieldOptions.map(convertToOption).filter(Boolean) as Option[],
[fieldOptions],
);
const optionsByValue = useMemo(
() =>
options.reduce((acc, option) => {
acc[`${option.value}`] = option;
return acc;
}, {} as Record<string, Option>),
[options],
);
const stringValueOptions = useMemo(
() =>
options.map(option => ({
label: option.label,
value: `${option.value}`,
})),
[options],
);
const handleChange = useCallback(
(event: SelectChangeEvent<string | string[]>) => {
const selectedOption = event.target.value;
const selectedValue = event.target.value;
const isMultiple = field.multiple ?? false;
const isEmpty =
isMultiple && Array.isArray(selectedOption) ? !selectedOption?.length : !selectedOption;
isMultiple && Array.isArray(selectedValue)
? !selectedValue?.length
: isNullish(selectedValue);
if (field.required && isEmpty && isMultiple) {
setInternalValue([]);
@ -45,53 +75,63 @@ const SelectControl = ({
} else if (isEmpty) {
setInternalValue('');
onChange('');
} else if (typeof selectedOption === 'string' || isMultiple) {
setInternalValue(selectedOption);
onChange(selectedOption);
} else if (typeof selectedValue === 'string') {
const selectedOption = optionsByValue[selectedValue];
const optionValue = selectedOption?.value ?? '';
setInternalValue(optionValue);
onChange(optionValue);
} else if (isMultiple) {
const optionValues = selectedValue.map(v => {
const selectedOption = optionsByValue[v];
return selectedOption?.value ?? '';
});
setInternalValue(optionValues);
onChange(optionValues);
}
},
[field, onChange],
[field.multiple, field.required, onChange, optionsByValue],
);
const fieldOptions: (string | Option)[] = field.options;
const isMultiple = field.multiple ?? false;
const stringValue = useMemo(() => {
if (!internalValue) {
return isMultiple ? [] : '';
}
const options = useMemo(
() => [...(fieldOptions.map(convertToOption) as Option[])].filter(Boolean),
[fieldOptions],
);
if (Array.isArray(internalValue)) {
return internalValue.map(v => `${v}`);
}
const optionsByValue = options.reduce((acc, option) => {
acc[option.value] = option;
return acc;
}, {} as Record<string, Option>);
return `${internalValue}`;
}, [isMultiple, internalValue]);
return (
<FormControl fullWidth error={hasErrors}>
<InputLabel id="demo-simple-select-label">{label}</InputLabel>
<Select
value={(internalValue ? internalValue : isMultiple ? [] : '') as string | string[]}
value={stringValue}
onChange={handleChange}
multiple={isMultiple}
label={label}
input={isMultiple ? <OutlinedInput id="select-multiple-chip" label={label} /> : undefined}
renderValue={selectValues =>
Array.isArray(selectValues) ? (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selectValues.map(selectValue => {
const label = optionsByValue[selectValue]?.label ?? selectValue;
return <Chip key={selectValue} label={label} />;
})}
</Box>
) : (
selectValues
)
}
renderValue={selectValues => {
if (Array.isArray(selectValues)) {
return (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selectValues.map(selectValue => {
const label = optionsByValue[selectValue]?.label ?? selectValue;
return <Chip key={selectValue} label={label} />;
})}
</Box>
);
}
return optionsByValue[selectValues]?.label ?? selectValues;
}}
>
<MenuItem key={`empty-option`} value="">
&nbsp;
</MenuItem>
{options.map(option => (
{stringValueOptions.map(option => (
<MenuItem key={`option-${option.value}`} value={option.value}>
{option.label}
</MenuItem>

View File

@ -5,27 +5,33 @@ import WidgetPreviewContainer from '../../components/UI/WidgetPreviewContainer';
import type { SelectField, WidgetPreviewProps } from '../../interface';
interface ListPreviewProps {
values: string[];
values: (string | number)[];
}
const ListPreview = ({ values }: ListPreviewProps) => {
return (
<ul>
{(values as string[]).map((value, idx) => (
{values.map((value, idx) => (
<li key={idx}>{value}</li>
))}
</ul>
);
};
const SelectPreview = ({ value }: WidgetPreviewProps<string | string[], SelectField>) => {
const SelectPreview = ({
value,
}: WidgetPreviewProps<string | number | (string | number)[], SelectField>) => {
if (!value) {
return <WidgetPreviewContainer />;
}
return (
<WidgetPreviewContainer>
{typeof value === 'string' ? value : <ListPreview values={value} />}
{typeof value === 'string' || typeof value === 'number' ? (
value
) : (
<ListPreview values={value} />
)}
</WidgetPreviewContainer>
);
};

View File

@ -5,7 +5,7 @@ import { validateMinMax } from '../../lib/widgets/validations';
import type { SelectField, WidgetParam } from '../../interface';
const SelectWidget = (): WidgetParam<string | string[], SelectField> => {
const SelectWidget = (): WidgetParam<string | number | (string | number)[], SelectField> => {
return {
name: 'select',
controlComponent,

View File

@ -313,7 +313,7 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.0.0", "@babel/parser@^7.18.10", "@babel/parser@^7.19.1", "@babel/parser@^7.19.3", "@babel/parser@^7.8.3":
"@babel/parser@^7.0.0", "@babel/parser@^7.18.10", "@babel/parser@^7.19.1", "@babel/parser@^7.19.3":
version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
@ -1047,7 +1047,7 @@
"@babel/parser" "^7.18.10"
"@babel/types" "^7.18.10"
"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.19.3", "@babel/traverse@^7.8.3":
"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.19.3":
version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.3.tgz#3a3c5348d4988ba60884e8494b0592b2f15a04b4"
integrity sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==
@ -1102,11 +1102,6 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@csstools/selector-specificity@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36"
integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==
"@date-io/core@^2.15.0", "@date-io/core@^2.16.0":
version "2.16.0"
resolved "https://registry.yarnpkg.com/@date-io/core/-/core-2.16.0.tgz#7871bfc1d9bca9aa35ad444a239505589d0f22f6"
@ -2243,11 +2238,6 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
"@types/minimist@^1.2.0":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
"@types/node-fetch@2.6.2":
version "2.6.2"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
@ -2727,7 +2717,7 @@ ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@8.11.0, ajv@^8.0.0, ajv@^8.0.1, ajv@^8.8.0:
ajv@8.11.0, ajv@^8.0.0, ajv@^8.8.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
@ -2892,21 +2882,11 @@ array.prototype.reduce@^1.0.4:
es-array-method-boxes-properly "^1.0.0"
is-string "^1.0.7"
arrify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==
asap@^2.0.0:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
astral-regex@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
async@^2.6.4:
version "2.6.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
@ -3198,11 +3178,6 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
balanced-match@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -3378,15 +3353,6 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
camelcase-keys@^6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0"
integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==
dependencies:
camelcase "^5.3.1"
map-obj "^4.0.0"
quick-lru "^4.0.1"
camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
@ -3580,11 +3546,6 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colord@^2.9.3:
version "2.9.3"
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
colorette@^2.0.10, colorette@^2.0.14:
version "2.0.19"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
@ -3743,7 +3704,7 @@ corser@^2.0.1:
resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87"
integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==
cosmiconfig@^7.0.0, cosmiconfig@^7.0.1:
cosmiconfig@^7.0.0:
version "7.0.1"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d"
integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==
@ -3789,11 +3750,6 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
css-functions-list@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.1.0.tgz#cf5b09f835ad91a00e5959bcfc627cd498e1321b"
integrity sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==
css-loader@3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645"
@ -3928,19 +3884,6 @@ decache@^4.5.1:
dependencies:
callsite "^1.0.0"
decamelize-keys@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
integrity sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==
dependencies:
decamelize "^1.1.0"
map-obj "^1.0.0"
decamelize@^1.1.0, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@ -4699,7 +4642,7 @@ fast-diff@^1.1.2:
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9:
fast-glob@^3.2.7, fast-glob@^3.2.9:
version "3.2.12"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
@ -4720,7 +4663,7 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.16:
fastest-levenshtein@^1.0.12:
version "1.0.16"
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
@ -5018,22 +4961,6 @@ glob@~8.0.1:
minimatch "^5.0.1"
once "^1.3.0"
global-modules@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
dependencies:
global-prefix "^3.0.0"
global-prefix@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
dependencies:
ini "^1.3.5"
kind-of "^6.0.2"
which "^1.3.1"
globals@^11.1.0, globals@^11.12.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@ -5075,11 +5002,6 @@ globby@^11.1.0:
merge2 "^1.4.1"
slash "^3.0.0"
globjoin@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==
gotrue-js@0.9.29:
version "0.9.29"
resolved "https://registry.yarnpkg.com/gotrue-js/-/gotrue-js-0.9.29.tgz#08e62184d4eaadcd87f95cb6e49e3c4a9742a052"
@ -5124,11 +5046,6 @@ handle-thing@^2.0.0:
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==
hard-rejection@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@ -5206,13 +5123,6 @@ hosted-git-info@^2.1.4:
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
hosted-git-info@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224"
integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==
dependencies:
lru-cache "^6.0.0"
hpack.js@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
@ -5240,11 +5150,6 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
html-tags@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==
http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
@ -5369,11 +5274,6 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
parent-module "^1.0.0"
resolve-from "^4.0.0"
import-lazy@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
import-local@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
@ -5415,11 +5315,6 @@ ini@2.0.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
ini@^1.3.5:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
internal-slot@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
@ -5490,7 +5385,7 @@ is-callable@^1.1.4, is-callable@^1.2.6:
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0:
is-core-module@^2.8.1, is-core-module@^2.9.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
@ -5568,11 +5463,6 @@ is-number@^7.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-plain-obj@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==
is-plain-obj@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
@ -6199,7 +6089,7 @@ jwt-decode@3.1.2:
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3:
kind-of@^6.0.0, kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
@ -6209,11 +6099,6 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
known-css-properties@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.25.0.tgz#6ebc4d4b412f602e5cfbeb4086bd544e34c0a776"
integrity sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==
ky-universal@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.3.0.tgz#3fcbb0dd03da39b5f05100d9362a630d5e1d402e"
@ -6351,11 +6236,6 @@ lodash.set@^4.3.2:
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
integrity sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==
lodash.truncate@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@ -6414,16 +6294,6 @@ makeerror@1.0.12:
dependencies:
tmpl "1.0.5"
map-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==
map-obj@^4.0.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a"
integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==
mapbox-to-css-font@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.1.tgz#41bf38faed36b7dab069828aa3654e4bd91a1eda"
@ -6434,11 +6304,6 @@ material-colors@^1.2.1:
resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==
mathml-tag-names@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
mdn-data@2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
@ -6476,24 +6341,6 @@ memorystream@^0.3.1:
resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2"
integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==
meow@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364"
integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==
dependencies:
"@types/minimist" "^1.2.0"
camelcase-keys "^6.2.2"
decamelize "^1.2.0"
decamelize-keys "^1.1.0"
hard-rejection "^2.1.0"
minimist-options "4.1.0"
normalize-package-data "^3.0.0"
read-pkg-up "^7.0.1"
redent "^3.0.0"
trim-newlines "^3.0.0"
type-fest "^0.18.0"
yargs-parser "^20.2.3"
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@ -6519,7 +6366,7 @@ micro-api-client@^3.2.1:
resolved "https://registry.yarnpkg.com/micro-api-client/-/micro-api-client-3.3.0.tgz#52dd567d322f10faffe63d19d4feeac4e4ffd215"
integrity sha512-y0y6CUB9RLVsy3kfgayU28746QrNMpSm9O/AYGNsBgOkJr/X/Jk0VLGoO8Ude7Bpa8adywzF+MzXNZRFRsNPhg==
micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
@ -6580,15 +6427,6 @@ minimatch@^5.0.1:
dependencies:
brace-expansion "^2.0.1"
minimist-options@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==
dependencies:
arrify "^1.0.1"
is-plain-obj "^1.1.0"
kind-of "^6.0.3"
minimist@>=1.2.2, minimist@^1.2.0, minimist@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
@ -6745,16 +6583,6 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
normalize-package-data@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e"
integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==
dependencies:
hosted-git-info "^4.0.1"
is-core-module "^2.5.0"
semver "^7.3.4"
validate-npm-package-license "^3.0.1"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@ -7182,11 +7010,6 @@ portfinder@^1.0.28:
debug "^3.2.7"
mkdirp "^0.5.6"
postcss-media-query-parser@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244"
integrity sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==
postcss-modules-extract-imports@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e"
@ -7220,22 +7043,12 @@ postcss-modules-values@^3.0.0:
icss-utils "^4.0.0"
postcss "^7.0.6"
postcss-resolve-nested-selector@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e"
integrity sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==
postcss-safe-parser@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1"
integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==
postcss-scss@4.0.5, postcss-scss@^4.0.2:
postcss-scss@4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.5.tgz#8ee33c1dda8d9d4753b565ec79014803dc6edabf"
integrity sha512-F7xpB6TrXyqUh3GKdyB4Gkp3QL3DDW1+uI+gxx/oJnUt/qXI4trj5OGlp9rOKdoABGULuqtqeG+3HEVQk4DjmA==
postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.6:
postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
version "6.0.10"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
@ -7243,12 +7056,12 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.10, postcss-selecto
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
postcss-value-parser@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@8.4.16, postcss@^8.4.16:
postcss@8.4.16:
version "8.4.16"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
@ -7257,7 +7070,7 @@ postcss@8.4.16, postcss@^8.4.16:
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
postcss@^7.0.14, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.39"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309"
integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
@ -7467,11 +7280,6 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
quick-lru@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
quickselect@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018"
@ -7811,14 +7619,6 @@ rechoir@^0.7.0:
dependencies:
resolve "^1.9.0"
redent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
dependencies:
indent-string "^4.0.0"
strip-indent "^3.0.0"
redux-thunk@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
@ -8297,15 +8097,6 @@ slash@^4.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
slice-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
dependencies:
ansi-styles "^4.0.0"
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
sockjs@^0.3.24:
version "0.3.24"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce"
@ -8619,110 +8410,6 @@ style-loader@3.3.1:
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
style-search@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902"
integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==
stylelint-config-recommended-scss@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz#193f483861c76a36ece24c52eb6baca4838f4a48"
integrity sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==
dependencies:
postcss-scss "^4.0.2"
stylelint-config-recommended "^6.0.0"
stylelint-scss "^4.0.0"
stylelint-config-recommended@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz#fd2523a322836005ad9bf473d3e5534719c09f9d"
integrity sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==
stylelint-config-standard-scss@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz#dafc4fa5538d0ed833bf0a7d391e075683ffd96c"
integrity sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw==
dependencies:
stylelint-config-recommended-scss "^5.0.2"
stylelint-config-standard "^24.0.0"
stylelint-config-standard@^24.0.0:
version "24.0.0"
resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz#6823f207ab997ae0b641f9a636d007cc44d77541"
integrity sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==
dependencies:
stylelint-config-recommended "^6.0.0"
stylelint-config-styled-components@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/stylelint-config-styled-components/-/stylelint-config-styled-components-0.1.1.tgz#b408388d7c687833ab4be4c4e6522d97d2827ede"
integrity sha512-z5Xz/9GmvxO6e/DLzBMwkB85zHxEEjN6K7Cj80Bi+o/9vR9eS3GX3E9VuMnX9WLFYulqbqLtTapGGY28JBiy9Q==
stylelint-processor-styled-components@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/stylelint-processor-styled-components/-/stylelint-processor-styled-components-1.10.0.tgz#8082fc68779476aac411d3afffac0bc833d77a29"
integrity sha512-g4HpN9rm0JD0LoHuIOcd/FIjTZCJ0ErQ+dC3VTxp+dSvnkV+MklKCCmCQEdz5K5WxF4vPuzfVgdbSDuPYGZhoA==
dependencies:
"@babel/parser" "^7.8.3"
"@babel/traverse" "^7.8.3"
micromatch "^4.0.2"
postcss "^7.0.26"
stylelint-scss@^4.0.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.3.0.tgz#638800faf823db11fff60d537c81051fe74c90fa"
integrity sha512-GvSaKCA3tipzZHoz+nNO7S02ZqOsdBzMiCx9poSmLlb3tdJlGddEX/8QzCOD8O7GQan9bjsvLMsO5xiw6IhhIQ==
dependencies:
lodash "^4.17.21"
postcss-media-query-parser "^0.2.3"
postcss-resolve-nested-selector "^0.1.1"
postcss-selector-parser "^6.0.6"
postcss-value-parser "^4.1.0"
stylelint@14.12.1:
version "14.12.1"
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.12.1.tgz#7fac1578662ca12330c32a61c8583be7fad4a530"
integrity sha512-ZEM4TuksChMBfuPadQsHUkbOoRySAT9QMfDvvYxdAchOJl0p+csTMBXOu6ORAAxKhwBmxqJiep8V88bXfNs3EQ==
dependencies:
"@csstools/selector-specificity" "^2.0.2"
balanced-match "^2.0.0"
colord "^2.9.3"
cosmiconfig "^7.0.1"
css-functions-list "^3.1.0"
debug "^4.3.4"
fast-glob "^3.2.12"
fastest-levenshtein "^1.0.16"
file-entry-cache "^6.0.1"
global-modules "^2.0.0"
globby "^11.1.0"
globjoin "^0.1.4"
html-tags "^3.2.0"
ignore "^5.2.0"
import-lazy "^4.0.0"
imurmurhash "^0.1.4"
is-plain-object "^5.0.0"
known-css-properties "^0.25.0"
mathml-tag-names "^2.1.3"
meow "^9.0.0"
micromatch "^4.0.5"
normalize-path "^3.0.0"
picocolors "^1.0.0"
postcss "^8.4.16"
postcss-media-query-parser "^0.2.3"
postcss-resolve-nested-selector "^0.1.1"
postcss-safe-parser "^6.0.0"
postcss-selector-parser "^6.0.10"
postcss-value-parser "^4.2.0"
resolve-from "^5.0.0"
string-width "^4.2.3"
strip-ansi "^6.0.1"
style-search "^0.1.0"
supports-hyperlinks "^2.3.0"
svg-tags "^1.0.0"
table "^6.8.0"
v8-compile-cache "^2.3.0"
write-file-atomic "^4.0.2"
stylis@4.0.13:
version "4.0.13"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
@ -8754,7 +8441,7 @@ supports-color@^8.0.0:
dependencies:
has-flag "^4.0.0"
supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.3.0:
supports-hyperlinks@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624"
integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==
@ -8767,11 +8454,6 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svg-tags@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==
svgo@^1.2.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
@ -8814,17 +8496,6 @@ sywac@^1.2.2:
resolved "https://registry.yarnpkg.com/sywac/-/sywac-1.3.0.tgz#324789bdb8bd7d0d66625c9144fce81ab5ba6f99"
integrity sha512-LDt2stNTp4bVPMgd70Jj9PWrSa4batl+bv+Ea5NLNGT7ufc4oQPtRfQ73wbddNV6RilaPqnEt6y1Wkm5FVTNEg==
table@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca"
integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==
dependencies:
ajv "^8.0.1"
lodash.truncate "^4.4.2"
slice-ansi "^4.0.0"
string-width "^4.2.3"
strip-ansi "^6.0.1"
tapable@^2.1.1, tapable@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
@ -8952,11 +8623,6 @@ traverse@0.6.6:
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
integrity sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw==
trim-newlines@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
truncate-utf8-bytes@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b"
@ -9013,11 +8679,6 @@ type-detect@4.0.8:
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
type-fest@^0.18.0:
version "0.18.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f"
integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==
type-fest@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
@ -9240,11 +8901,6 @@ uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
v8-to-istanbul@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4"
@ -9502,7 +9158,7 @@ which-boxed-primitive@^1.0.2:
is-string "^1.0.5"
is-symbol "^1.0.3"
which@^1.2.9, which@^1.3.1:
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@ -9547,7 +9203,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
write-file-atomic@^4.0.1, write-file-atomic@^4.0.2:
write-file-atomic@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
@ -9590,11 +9246,6 @@ yaml@1.10.2, yaml@^1.10.0:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yargs-parser@^20.2.3:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
yargs-parser@^21.0.0:
version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"

20
website/.gitignore vendored
View File

@ -34,3 +34,23 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
dist/
bin/
.tern-project
.vscode/
.idea/
manifest.yml
.imdone/
data/contributors.json
cypress/videos
cypress/screenshots
__diff_output__
.cache
*.log
.env
.temp/
*.tgz
public/sw.js
public/workbox*.js

View File

@ -18,12 +18,12 @@ If you are using a package manager like Yarn or NPM, use their standard procedur
If you are using the CMS through a CDN like Unpkg, then that depends on the version tag you are using. You can find the version tag in the `/admin/index.html` file of your site.
- (Recommended) If you use `^0.1.0`, the CMS does all updates except major versions automatically.
- It upgrades to `0.1.1`, `0.2.0`, `0.2.1`.
- It does not upgrade to `1.0.0` or higher.
- (Recommended) If you use `^1.0.0`, the CMS does all updates except major versions automatically.
- It upgrades to `1.0.1`, `1.1.0`, `1.1.1`.
- It does not upgrade to `2.0.0` or higher.
- It does not upgrade to beta versions.
- If you use `~0.1.0`, the CMS will do only patch updates automatically.
- It upgrades `0.1.1`, `0.1.2`.
- It does not upgrade to `0.2.0` or higher.
- If you use `~1.0.0`, the CMS will do only patch updates automatically.
- It upgrades `1.0.1`, `1.0.2`.
- It does not upgrade to `1.1.0` or higher.
- It does not upgrade beta versions.