Feature/plate editor (#115)

* Add plate editor
This commit is contained in:
Daniel Lautzenheiser 2022-12-01 19:29:33 -05:00 committed by GitHub
parent f3c4337268
commit 147592a8b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
345 changed files with 12561 additions and 4346 deletions

View File

@ -55,6 +55,24 @@ module.exports = {
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@mui/*/*/*', '!@mui/material/test-utils/*'],
message: 'Do not import material imports as 3rd level imports',
allowTypeImports: true,
},
{
group: ['@mui/material', '!@mui/material/'],
message: 'Please import material imports as defaults or 2nd level imports',
allowTypeImports: true,
},
],
},
],
'import/prefer-default-export': 'error',
},
plugins: ['babel', '@emotion', 'cypress', 'unicorn', 'react-hooks'],
settings: {
@ -62,9 +80,7 @@ module.exports = {
version: 'detect',
},
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
typescript: {}, // this loads <rootdir>/tsconfig.json to eslint
},
'import/core-modules': ['src'],
},

View File

@ -68,9 +68,7 @@
content: '{}',
},
'image.json': {
content: `{
"required": "/assets/uploads/moby-dick.jpg"
}`,
content: `{}`,
},
'map.json': {
content: '{}',
@ -125,7 +123,71 @@
dateString +
'T00:00:00.000Z\n---\n# The post is number ' +
i +
'\n\nAnd this is yet another identical post body',
`\n\n![Static CMS](https://raw.githubusercontent.com/StaticJsCMS/static-cms/main/static-cms-logo.png)
# Awesome Editor!
It was _released as open source in 2022_ and is **_continually_** evolving to be the **best editor experience** available for static site generators.
## MDX
The output out this widget is \`mdx\`, a mixture of \`markdown\` and \`javascript components\`. See [MDX documentation](https://mdxjs.com/docs/).
\`\`\`yaml
name: body
label: Blog post content
widget: markdown
\`\`\`
\`\`\`js
name: 'body',
label: 'Blog post content',
widget: 'markdown',
\`\`\`
> See the table below for default options
> More API information can be found in the document
| Name | Type | Default | Description |
| ------------- | --------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| default | string | \`''\` | _Optional_. The default value for the field. Accepts markdown content |
| media_library | Media Library Options | \`{}\` | _Optional_. Media library settings to apply when a media library is opened by the current widget. See [Media Library Options](#media-library-options) |
| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo |
| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site |
### Media Library Options
| Name | Type | Default | Description |
| -------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| allow_multiple | boolean | \`true\` | _Optional_. When set to \`false\`, prevents multiple selection for any media library extension, but must be supported by the extension in use |
| config | string | \`{}\` | _Optional_. A configuration object that will be passed directly to the media library being used - available options are determined by the library |
| choose_url | string<br />\\| boolean | \`true\` | _Optional_. When set to \`false\`, the "Insert from URL" button will be hidden
## Features
* CommonMark + GFM Specifications
* Live \`Preview\`
* Auto Indent
* Syntax Highlight
1. Rich Editor
2. Preview
## Formatting
<font style={{ color: 'red', backgroundColor: 'black' }}>Colored Text</font>
<p align="center">Centered Text</p>
**Bold**, *Italic*, ***both***, <u>Underlined</u>
~~Strikethrough~~, <sub>subscript</sub>, <sup>superscript</sup>
## Support
> * Supports remark plugins
> * Supports wrappers
> 1. [x] React
> 2. [ ] More coming soon`,
};
}

View File

@ -41,30 +41,41 @@
"last 2 Safari versions"
],
"dependencies": {
"@codemirror/commands": "6.1.2",
"@codemirror/view": "6.6.0",
"@emotion/babel-preset-css-prop": "11.10.0",
"@emotion/css": "11.10.0",
"@emotion/react": "11.10.4",
"@emotion/styled": "11.10.4",
"@ltd/j-toml": "1.35.3",
"@mdx-js/mdx": "2.1.5",
"@mdx-js/react": "2.1.5",
"@mui/icons-material": "5.10.6",
"@mui/material": "5.10.10",
"@mui/system": "5.4.1",
"@mui/x-date-pickers": "5.0.9",
"@reduxjs/toolkit": "1.8.5",
"@toast-ui/react-editor": "3.2.2",
"@reduxjs/toolkit": "1.9.1",
"@styled-icons/fluentui-system-regular": "10.46.0",
"@styled-icons/remix-editor": "10.46.0",
"@udecode/plate": "18.9.0",
"@udecode/plate-juice": "18.9.0",
"@udecode/plate-serializer-md": "18.9.0",
"@uiw/codemirror-extensions-langs": "4.14.2",
"@uiw/react-codemirror": "4.14.2",
"ajv": "8.11.2",
"ajv-errors": "3.0.0",
"ajv-keywords": "5.1.0",
"array-move": "4.0.0",
"buffer": "6.0.3",
"clean-stack": "4.2.0",
"codemirror": "5.65.9",
"codemirror": "6.0.1",
"common-tags": "1.8.1",
"copy-text-to-clipboard": "3.0.1",
"create-react-class": "15.7.0",
"date-fns": "2.29.3",
"deepmerge": "4.2.2",
"diacritics": "1.3.0",
"escape-html": "1.0.3",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-babel": "5.3.1",
"fuzzy": "0.1.3",
@ -74,7 +85,7 @@
"graphql-tag": "2.12.6",
"gray-matter": "4.0.3",
"history": "4.10.1",
"immer": "9.0.15",
"immer": "9.0.16",
"ini": "2.0.0",
"is-hotkey": "0.2.0",
"js-base64": "3.7.2",
@ -88,15 +99,14 @@
"ol": "6.15.1",
"path-browserify": "1.0.1",
"react": "18.2.0",
"react-codemirror2": "7.2.1",
"react-color": "2.19.3",
"react-dnd": "14.0.5",
"react-dnd-html5-backend": "14.1.0",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
"react-dom": "18.2.0",
"react-frame-component": "5.2.3",
"react-is": "18.2.0",
"react-polyglot": "0.7.2",
"react-redux": "8.0.4",
"react-redux": "8.0.5",
"react-router-dom": "6.4.1",
"react-scroll-sync": "0.9.0",
"react-sortable-hoc": "2.0.0",
@ -105,17 +115,31 @@
"react-virtualized-auto-sizer": "1.0.7",
"react-waypoint": "10.3.0",
"react-window": "1.8.7",
"remark-gfm": "3.0.1",
"remark-html": "15.0.1",
"remark-mdx": "2.1.5",
"remark-parse": "10.0.1",
"sanitize-filename": "1.6.3",
"semaphore": "1.1.0",
"slate": "0.85.0",
"slate-history": "0.85.0",
"slate-hyperscript": "0.77.0",
"slate-react": "0.83.2",
"stream-browserify": "3.0.0",
"styled-components": "5.3.6",
"symbol-observable": "4.0.0",
"ts-loader": "9.4.1",
"unified": "10.1.2",
"unist-util-visit": "4.1.1",
"uploadcare-widget": "3.19.0",
"uploadcare-widget-tab-effects": "1.5.0",
"url": "0.11.0",
"url-join": "4.0.1",
"uuid": "3.4.0",
"validate-color": "2.2.1",
"vfile": "5.3.6",
"vfile-message": "3.1.3",
"vfile-statistics": "2.0.0",
"what-input": "5.2.12",
"what-the-diff": "0.6.0",
"yaml": "1.10.2"
@ -137,7 +161,7 @@
"@octokit/core": "4.1.0",
"@octokit/rest": "16.43.2",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
"@types/codemirror": "5.60.5",
"@simbathesailor/use-what-changed": "2.0.0",
"@types/common-tags": "1.8.0",
"@types/create-react-class": "15.6.3",
"@types/fs-extra": "9.0.13",
@ -149,6 +173,7 @@
"@types/jwt-decode": "2.2.1",
"@types/lodash": "4.14.191",
"@types/minimatch": "5.1.2",
"@types/node": "16.18.4",
"@types/node-fetch": "2.6.2",
"@types/react": "18.0.25",
"@types/react-color": "3.0.6",
@ -156,6 +181,7 @@
"@types/react-scroll-sync": "0.8.4",
"@types/react-virtualized-auto-sizer": "1.0.1",
"@types/react-window": "1.8.5",
"@types/styled-components": "5.1.26",
"@types/url-join": "4.0.1",
"@types/uuid": "3.4.10",
"@typescript-eslint/eslint-plugin": "5.38.0",
@ -179,6 +205,7 @@
"css-loader": "3.6.0",
"dotenv": "10.0.0",
"eslint": "8.24.0",
"eslint-import-resolver-typescript": "3.5.2",
"eslint-plugin-cypress": "2.12.1",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-prettier": "4.2.1",
@ -208,6 +235,7 @@
"source-map-loader": "4.0.0",
"style-loader": "3.3.1",
"to-string-loader": "1.2.0",
"tsconfig-paths-webpack-plugin": "4.0.0",
"typescript": "4.8.4",
"webpack": "5.74.0",
"webpack-cli": "4.10.0",

View File

@ -5,7 +5,7 @@ import trimStart from 'lodash/trimStart';
import yaml from 'yaml';
import { resolveBackend } from '../backend';
import { validateConfig } from '../constants/configSchema';
import validateConfig from '../constants/configSchema';
import { I18N, I18N_FIELD, I18N_STRUCTURE } from '../lib/i18n';
import { selectDefaultSortableFields } from '../lib/util/collection.util';
import { getIntegrations, selectIntegration } from '../reducers/integrations';

View File

@ -14,7 +14,7 @@ import { selectEntriesSortFields, selectIsFetching } from '../reducers/entries';
import { navigateToEntry } from '../routing/history';
import { addSnackbar } from '../store/slices/snackbars';
import { createAssetProxy } from '../valueObjects/AssetProxy';
import { createEntry } from '../valueObjects/Entry';
import createEntry from '../valueObjects/createEntry';
import { addAssets, getAsset } from './media';
import { loadMedia, waitForMediaLibraryToLoad } from './mediaLibrary';
import { waitUntil } from './waitUntil';
@ -475,18 +475,16 @@ export function changeDraftField({
path,
field,
value,
entry,
i18n,
}: {
path: string;
field: Field;
value: ValueOrNestedValue;
entry?: Entry | null;
i18n?: I18nSettings;
}) {
return {
type: DRAFT_CHANGE_FIELD,
payload: { path, field, value, entry, i18n },
payload: { path, field, value, i18n },
} as const;
}
@ -884,7 +882,7 @@ export function createEmptyDraftData(
fields: Field[],
skipField: (field: Field) => boolean = () => false,
) {
return fields.reduce((acc, item) => {
const ddd = fields.reduce((acc, item) => {
if (skipField(item)) {
return acc;
}
@ -904,7 +902,7 @@ export function createEmptyDraftData(
} else {
const asList = Array.isArray(subfields) ? subfields : [subfields];
const subDefaultValue = Array.isArray(subfields)
const subDefaultValue = list
? [createEmptyDraftData(asList, skipField)]
: createEmptyDraftData(asList, skipField);
@ -921,6 +919,8 @@ export function createEmptyDraftData(
return acc;
}, {} as ObjectValue);
return ddd;
}
function createEmptyDraftI18nData(collection: Collection, dataFields: Field[]) {

View File

@ -42,7 +42,7 @@ export function loadAssetFailure(path: string, error: Error) {
return { type: LOAD_ASSET_FAILURE, payload: { path, error } } as const;
}
const emptyAsset = createAssetProxy({
export const emptyAsset = createAssetProxy({
path: 'empty.svg',
file: new File([`<svg xmlns="http://www.w3.org/2000/svg"></svg>`], 'empty.svg', {
type: 'image/svg+xml',
@ -84,7 +84,7 @@ async function loadAsset(
const promiseCache: Record<string, Promise<AssetProxy>> = {};
export function getAsset<F extends BaseField = UnknownField>(
collection: Collection | null | undefined,
collection: Collection<F> | null | undefined,
entry: Entry | null | undefined,
path: string,
field?: F,
@ -104,7 +104,7 @@ export function getAsset<F extends BaseField = UnknownField>(
const resolvedPath = selectMediaFilePath(
state.config.config,
collection,
collection as Collection,
entry,
path,
field as Field,

View File

@ -41,7 +41,7 @@ import {
import { selectMediaFilePath } from './lib/util/media.util';
import { set } from './lib/util/object.util';
import { dateParsers, expandPath, extractTemplateVars } from './lib/widgets/stringTemplate';
import { createEntry } from './valueObjects/Entry';
import createEntry from './valueObjects/createEntry';
import type {
BackendClass,

View File

@ -15,11 +15,11 @@ import {
then,
throwOnConflictingBranches,
unsentRequest,
} from '../../lib/util';
} from '@staticcms/core/lib/util';
import type { DataFile, PersistOptions } from '../../interface';
import type { ApiRequest, FetchError } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
import type { DataFile, PersistOptions } from '@staticcms/core/interface';
import type { ApiRequest, FetchError } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
interface Config {
apiRoot?: string;

View File

@ -1,12 +1,12 @@
import { styled } from '@mui/material/styles';
import React, { useCallback, useMemo, useState } from 'react';
import AuthenticationPage from '../../components/UI/AuthenticationPage';
import Icon from '../../components/UI/Icon';
import { ImplicitAuthenticator, NetlifyAuthenticator } from '../../lib/auth';
import AuthenticationPage from '@staticcms/core/components/UI/AuthenticationPage';
import Icon from '@staticcms/core/components/UI/Icon';
import { ImplicitAuthenticator, NetlifyAuthenticator } from '@staticcms/core/lib/auth';
import type { MouseEvent } from 'react';
import type { AuthenticationPageProps, TranslatedProps } from '../../interface';
import type { AuthenticationPageProps, TranslatedProps } from '@staticcms/core/interface';
const LoginButtonIcon = styled(Icon)`
margin-right: 18px;

View File

@ -1,8 +1,8 @@
import minimatch from 'minimatch';
import { unsentRequest } from '../../lib/util';
import { unsentRequest } from '@staticcms/core/lib/util';
import type { ApiRequest, PointerFile } from '../../lib/util';
import type { ApiRequest, PointerFile } from '@staticcms/core/lib/util';
type MakeAuthorizedRequest = (req: ApiRequest) => Promise<Response>;
@ -37,7 +37,7 @@ interface LfsBatchUploadResponse {
objects: (LfsBatchObjectUpload | LfsBatchObjectError)[];
}
export class GitLfsClient {
export default class GitLfsClient {
private static defaultContentHeaders = {
Accept: 'application/vnd.git-lfs+json',
['Content-Type']: 'application/vnd.git-lfs+json',

View File

@ -2,7 +2,7 @@ import { stripIndent } from 'common-tags';
import trimStart from 'lodash/trimStart';
import semaphore from 'semaphore';
import { NetlifyAuthenticator } from '../../lib/auth';
import { NetlifyAuthenticator } from '@staticcms/core/lib/auth';
import {
AccessTokenError,
allEntriesByFolder,
@ -22,10 +22,10 @@ import {
localForage,
runWithLock,
unsentRequest,
} from '../../lib/util';
} from '@staticcms/core/lib/util';
import API, { API_NAME } from './API';
import AuthenticationPage from './AuthenticationPage';
import { GitLfsClient } from './git-lfs-client';
import GitLfsClient from './git-lfs-client';
import type { Semaphore } from 'semaphore';
import type {
@ -37,9 +37,9 @@ import type {
ImplementationFile,
PersistOptions,
User,
} from '../../interface';
import type { ApiRequest, AsyncLock, Cursor, FetchError } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
} from '@staticcms/core/interface';
import type { ApiRequest, AsyncLock, Cursor, FetchError } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
const MAX_CONCURRENT_DOWNLOADS = 10;

View File

@ -1,8 +1,8 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import AuthenticationPage from '../../components/UI/AuthenticationPage';
import AuthenticationPage from '@staticcms/core/components/UI/AuthenticationPage';
import type { AuthenticationPageProps, TranslatedProps, User } from '../../interface';
import type { AuthenticationPageProps, TranslatedProps, User } from '@staticcms/core/interface';
function useNetlifyIdentifyEvent(eventName: 'login', callback: (login: User) => void): void;
function useNetlifyIdentifyEvent(eventName: 'logout', callback: () => void): void;

View File

@ -1,7 +1,7 @@
import { APIError } from '../../lib/util';
import { APIError } from '@staticcms/core/lib/util';
import { API as GithubAPI } from '../github';
import type { FetchError } from '../../lib/util';
import type { FetchError } from '@staticcms/core/lib/util';
import type { Config as GitHubConfig } from '../github/API';
type Config = GitHubConfig & {

View File

@ -1,8 +1,8 @@
import { unsentRequest } from '../../lib/util';
import { unsentRequest } from '@staticcms/core/lib/util';
import { API as GitlabAPI } from '../gitlab';
import type { Config as GitLabConfig, CommitAuthor } from '../gitlab/API';
import type { ApiRequest } from '../../lib/util';
import type { ApiRequest } from '@staticcms/core/lib/util';
type Config = GitLabConfig & { tokenPromise: () => Promise<string>; commitAuthor: CommitAuthor };

View File

@ -15,7 +15,7 @@ import {
getPointerFileForMediaFileObj,
parsePointerFile,
unsentRequest,
} from '../../lib/util';
} from '@staticcms/core/lib/util';
import { API as BitBucketAPI, BitbucketBackend } from '../bitbucket';
import { GitHubBackend } from '../github';
import { GitLabBackend } from '../gitlab';
@ -36,9 +36,9 @@ import type {
PersistOptions,
TranslatedProps,
User,
} from '../../interface';
import type { ApiRequest, Cursor } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
} from '@staticcms/core/interface';
import type { ApiRequest, Cursor } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
import type { Client } from './netlify-lfs-client';
const STATUS_PAGE = 'https://www.netlifystatus.com';

View File

@ -3,9 +3,9 @@ import isPlainObject from 'lodash/isPlainObject';
import isEmpty from 'lodash/isEmpty';
import minimatch from 'minimatch';
import { unsentRequest } from '../../lib/util';
import { unsentRequest } from '@staticcms/core/lib/util';
import type { ApiRequest, PointerFile } from '../../lib/util';
import type { ApiRequest, PointerFile } from '@staticcms/core/lib/util';
type MakeAuthorizedRequest = (req: ApiRequest) => Promise<Response>;

View File

@ -17,13 +17,13 @@ import {
readFileMetadata,
requestWithBackoff,
unsentRequest,
} from '../../lib/util';
} from '@staticcms/core/lib/util';
import type { Octokit } from '@octokit/rest';
import type { Semaphore } from 'semaphore';
import type { DataFile, PersistOptions } from '../../interface';
import type { ApiRequest, FetchError } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
import type { DataFile, PersistOptions } from '@staticcms/core/interface';
import type { ApiRequest, FetchError } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
type GitHubUser = Octokit.UsersGetAuthenticatedResponse;
type GitCreateTreeParamsTree = Octokit.GitCreateTreeParamsTree;

View File

@ -1,12 +1,12 @@
import { styled } from '@mui/material/styles';
import React, { useCallback, useState } from 'react';
import AuthenticationPage from '../../components/UI/AuthenticationPage';
import Icon from '../../components/UI/Icon';
import { NetlifyAuthenticator } from '../../lib/auth';
import AuthenticationPage from '@staticcms/core/components/UI/AuthenticationPage';
import Icon from '@staticcms/core/components/UI/Icon';
import { NetlifyAuthenticator } from '@staticcms/core/lib/auth';
import type { MouseEvent } from 'react';
import type { AuthenticationPageProps, TranslatedProps } from '../../interface';
import type { AuthenticationPageProps, TranslatedProps } from '@staticcms/core/interface';
const LoginButtonIcon = styled(Icon)`
margin-right: 18px;

View File

@ -16,7 +16,7 @@ import {
getMediaDisplayURL,
runWithLock,
unsentRequest,
} from '../../lib/util';
} from '@staticcms/core/lib/util';
import API, { API_NAME } from './API';
import AuthenticationPage from './AuthenticationPage';
@ -31,9 +31,9 @@ import type {
ImplementationFile,
PersistOptions,
User,
} from '../../interface';
import type { AsyncLock } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
} from '@staticcms/core/interface';
import type { AsyncLock } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
type GitHubUser = Octokit.UsersGetAuthenticatedResponse;

View File

@ -1,3 +1,4 @@
/* eslint-disable import/prefer-default-export */
import { gql } from 'graphql-tag';
import * as fragments from './fragments';

View File

@ -15,11 +15,11 @@ import {
responseParser,
throwOnConflictingBranches,
unsentRequest,
} from '../../lib/util';
} from '@staticcms/core/lib/util';
import type { DataFile, PersistOptions } from '../../interface';
import type { ApiRequest, FetchError } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
import type { DataFile, PersistOptions } from '@staticcms/core/interface';
import type { ApiRequest, FetchError } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
export const API_NAME = 'GitLab';

View File

@ -1,17 +1,17 @@
import { styled } from '@mui/material/styles';
import React, { useCallback, useMemo, useState } from 'react';
import AuthenticationPage from '../../components/UI/AuthenticationPage';
import Icon from '../../components/UI/Icon';
import { NetlifyAuthenticator, PkceAuthenticator } from '../../lib/auth';
import { isNotEmpty } from '../../lib/util/string.util';
import AuthenticationPage from '@staticcms/core/components/UI/AuthenticationPage';
import Icon from '@staticcms/core/components/UI/Icon';
import { NetlifyAuthenticator, PkceAuthenticator } from '@staticcms/core/lib/auth';
import { isNotEmpty } from '@staticcms/core/lib/util/string.util';
import type { MouseEvent } from 'react';
import type {
AuthenticationPageProps,
AuthenticatorConfig,
TranslatedProps,
} from '../../interface';
} from '@staticcms/core/interface';
const LoginButtonIcon = styled(Icon)`
margin-right: 18px;

View File

@ -17,12 +17,12 @@ import {
getMediaDisplayURL,
localForage,
runWithLock,
} from '../../lib/util';
} from '@staticcms/core/lib/util';
import API, { API_NAME } from './API';
import AuthenticationPage from './AuthenticationPage';
import type { Semaphore } from 'semaphore';
import type { AsyncLock, Cursor } from '../../lib/util';
import type { AsyncLock, Cursor } from '@staticcms/core/lib/util';
import type {
Config,
Credentials,
@ -32,8 +32,8 @@ import type {
ImplementationFile,
PersistOptions,
User,
} from '../../interface';
import type AssetProxy from '../../valueObjects/AssetProxy';
} from '@staticcms/core/interface';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
const MAX_CONCURRENT_DOWNLOADS = 10;

View File

@ -2,11 +2,11 @@ import Button from '@mui/material/Button';
import { styled } from '@mui/material/styles';
import React, { useCallback } from 'react';
import GoBackButton from '../../components/UI/GoBackButton';
import Icon from '../../components/UI/Icon';
import GoBackButton from '@staticcms/core/components/UI/GoBackButton';
import Icon from '@staticcms/core/components/UI/Icon';
import type { MouseEvent } from 'react';
import type { AuthenticationPageProps, TranslatedProps } from '../../interface';
import type { AuthenticationPageProps, TranslatedProps } from '@staticcms/core/interface';
const StyledAuthenticationPage = styled('section')`
display: flex;

View File

@ -1,4 +1,4 @@
import { APIError, basename, blobToFileObj, unsentRequest } from '../../lib/util';
import { APIError, basename, blobToFileObj, unsentRequest } from '@staticcms/core/lib/util';
import AuthenticationPage from './AuthenticationPage';
import type {
@ -10,9 +10,9 @@ import type {
ImplementationFile,
PersistOptions,
User,
} from '../../interface';
import type { Cursor } from '../../lib/util';
import type AssetProxy from '../../valueObjects/AssetProxy';
} from '@staticcms/core/interface';
import type { Cursor } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
async function serializeAsset(assetProxy: AssetProxy) {
const base64content = await assetProxy.toBase64!();

View File

@ -2,11 +2,11 @@ import Button from '@mui/material/Button';
import { styled } from '@mui/material/styles';
import React, { useCallback, useEffect } from 'react';
import GoBackButton from '../../components/UI/GoBackButton';
import Icon from '../../components/UI/Icon';
import GoBackButton from '@staticcms/core/components/UI/GoBackButton';
import Icon from '@staticcms/core/components/UI/Icon';
import type { MouseEvent } from 'react';
import type { AuthenticationPageProps, TranslatedProps } from '../../interface';
import type { AuthenticationPageProps, TranslatedProps } from '@staticcms/core/interface';
const StyledAuthenticationPage = styled('section')`
display: flex;

View File

@ -5,7 +5,7 @@ import unset from 'lodash/unset';
import { extname } from 'path';
import uuid from 'uuid/v4';
import { basename, Cursor, CURSOR_COMPATIBILITY_SYMBOL } from '../../lib/util';
import { basename, Cursor, CURSOR_COMPATIBILITY_SYMBOL } from '@staticcms/core/lib/util';
import AuthenticationPage from './AuthenticationPage';
import type {
@ -16,8 +16,8 @@ import type {
ImplementationEntry,
ImplementationFile,
User,
} from '../../interface';
import type AssetProxy from '../../valueObjects/AssetProxy';
} from '@staticcms/core/interface';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';
type RepoFile = { path: string; content: string | AssetProxy };
type RepoTree = { [key: string]: RepoFile | RepoTree };

View File

@ -12,7 +12,7 @@ import { loadConfig } from './actions/config';
import App from './components/App/App';
import './components/EditorWidgets';
import { ErrorBoundary } from './components/UI';
import { addExtensions } from './extensions';
import addExtensions from './extensions';
import { getPhrases } from './lib/phrases';
import './mediaLibrary';
import { selectLocale } from './reducers/config';

View File

@ -1,18 +1,18 @@
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import Fab from '@mui/material/Fab';
import { styled } from '@mui/material/styles';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo } from 'react';
import { translate } from 'react-polyglot';
import { connect } from 'react-redux';
import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom';
import { ScrollSync } from 'react-scroll-sync';
import TopBarProgress from 'react-topbar-progress-indicator';
import { loginUser as loginUserAction } from '../../actions/auth';
import { discardDraft as discardDraftAction } from '../../actions/entries';
import { currentBackend } from '../../backend';
import { colors, GlobalStyles } from '../../components/UI/styles';
import { history } from '../../routing/history';
import { loginUser as loginUserAction } from '@staticcms/core/actions/auth';
import { discardDraft as discardDraftAction } from '@staticcms/core/actions/entries';
import { currentBackend } from '@staticcms/core/backend';
import { colors, GlobalStyles } from '@staticcms/core/components/UI/styles';
import { history } from '@staticcms/core/routing/history';
import CollectionRoute from '../Collection/CollectionRoute';
import EditorRoute from '../Editor/EditorRoute';
import MediaLibrary from '../MediaLibrary/MediaLibrary';
@ -24,10 +24,10 @@ import Loader from '../UI/Loader';
import ScrollTop from '../UI/ScrollTop';
import NotFoundPage from './NotFoundPage';
import type { Collections, Credentials, TranslatedProps } from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
import type { ComponentType } from 'react';
import type { ConnectedProps } from 'react-redux';
import type { Collections, Credentials, TranslatedProps } from '../../interface';
import type { RootState } from '../../store';
TopBarProgress.config({
barColors: {
@ -162,7 +162,7 @@ const App = ({
const defaultPath = useMemo(() => getDefaultPath(collections), [collections]);
const { pathname } = useLocation();
React.useEffect(() => {
useEffect(() => {
if (!/\/collections\/[a-zA-Z0-9_-]+\/entries\/[a-zA-Z0-9_-]+/g.test(pathname)) {
discardDraft();
}

View File

@ -1,4 +1,3 @@
import { styled } from '@mui/material/styles';
import DescriptionIcon from '@mui/icons-material/Description';
import ImageIcon from '@mui/icons-material/Image';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
@ -8,24 +7,25 @@ import Button from '@mui/material/Button';
import Link from '@mui/material/Link';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import { styled } from '@mui/material/styles';
import Toolbar from '@mui/material/Toolbar';
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import { connect } from 'react-redux';
import { logoutUser as logoutUserAction } from '../../actions/auth';
import { createNewEntry } from '../../actions/collections';
import { openMediaLibrary as openMediaLibraryAction } from '../../actions/mediaLibrary';
import { checkBackendStatus as checkBackendStatusAction } from '../../actions/status';
import { buttons, colors } from '../../components/UI/styles';
import { stripProtocol } from '../../lib/urlHelper';
import { logoutUser as logoutUserAction } from '@staticcms/core/actions/auth';
import { createNewEntry } from '@staticcms/core/actions/collections';
import { openMediaLibrary as openMediaLibraryAction } from '@staticcms/core/actions/mediaLibrary';
import { checkBackendStatus as checkBackendStatusAction } from '@staticcms/core/actions/status';
import { buttons, colors } from '@staticcms/core/components/UI/styles';
import { stripProtocol } from '@staticcms/core/lib/urlHelper';
import NavLink from '../UI/NavLink';
import SettingsDropdown from '../UI/SettingsDropdown';
import type { ComponentType } from 'react';
import type { TranslatedProps } from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
import type { ComponentType, MouseEvent } from 'react';
import type { ConnectedProps } from 'react-redux';
import type { TranslatedProps } from '../../interface';
import type { RootState } from '../../store';
const StyledAppBar = styled(AppBar)`
background-color: ${colors.foreground};
@ -73,9 +73,9 @@ const Header = ({
showMediaButton,
checkBackendStatus,
}: TranslatedProps<HeaderProps>) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {

View File

@ -2,7 +2,7 @@ import { styled } from '@mui/material/styles';
import React from 'react';
import TopBarProgress from 'react-topbar-progress-indicator';
import { colors } from '../../components/UI/styles';
import { colors } from '@staticcms/core/components/UI/styles';
import Header from './Header';
import type { ReactNode } from 'react';

View File

@ -2,7 +2,7 @@ import { styled } from '@mui/material/styles';
import React from 'react';
import { translate } from 'react-polyglot';
import { lengths } from '../../components/UI/styles';
import { lengths } from '@staticcms/core/components/UI/styles';
import type { ComponentType } from 'react';
import type { TranslateProps } from 'react-polyglot';

View File

@ -8,21 +8,21 @@ import {
filterByField as filterByFieldAction,
groupByField as groupByFieldAction,
sortByField as sortByFieldAction,
} from '../../actions/entries';
import { components } from '../../components/UI/styles';
import { SORT_DIRECTION_ASCENDING } from '../../constants';
import { getNewEntryUrl } from '../../lib/urlHelper';
} from '@staticcms/core/actions/entries';
import { components } from '@staticcms/core/components/UI/styles';
import { SORT_DIRECTION_ASCENDING } from '@staticcms/core/constants';
import { getNewEntryUrl } from '@staticcms/core/lib/urlHelper';
import {
selectSortableFields,
selectViewFilters,
selectViewGroups,
} from '../../lib/util/collection.util';
} from '@staticcms/core/lib/util/collection.util';
import {
selectEntriesFilter,
selectEntriesGroup,
selectEntriesSort,
selectViewStyle,
} from '../../reducers/entries';
} from '@staticcms/core/reducers/entries';
import CollectionControls from './CollectionControls';
import CollectionTop from './CollectionTop';
import EntriesCollection from './Entries/EntriesCollection';
@ -37,8 +37,8 @@ import type {
TranslatedProps,
ViewFilter,
ViewGroup,
} from '../../interface';
import type { RootState } from '../../store';
} from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
const CollectionMain = styled('main')`
width: 100%;

View File

@ -6,7 +6,7 @@ import GroupControl from './GroupControl';
import SortControl from './SortControl';
import ViewStyleControl from './ViewStyleControl';
import type { CollectionViewStyle } from '../../constants/collectionViews';
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
import type {
FilterMap,
GroupMap,
@ -16,7 +16,7 @@ import type {
TranslatedProps,
ViewFilter,
ViewGroup,
} from '../../interface';
} from '@staticcms/core/interface';
const CollectionControlsContainer = styled('div')`
display: flex;

View File

@ -4,7 +4,7 @@ import { Navigate, useParams } from 'react-router-dom';
import MainView from '../App/MainView';
import Collection from './Collection';
import type { Collections } from '../../interface';
import type { Collections } from '@staticcms/core/interface';
function getDefaultPath(collections: Collections) {
const first = Object.values(collections).filter(collection => collection.hide !== true)[0];

View File

@ -6,11 +6,11 @@ import TextField from '@mui/material/TextField';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { translate } from 'react-polyglot';
import { colors, colorsRaw, lengths } from '../../components/UI/styles';
import { transientOptions } from '../../lib';
import { colors, colorsRaw, lengths } from '@staticcms/core/components/UI/styles';
import { transientOptions } from '@staticcms/core/lib';
import type { ChangeEvent, FocusEvent, KeyboardEvent, MouseEvent } from 'react';
import type { Collection, Collections, TranslatedProps } from '../../interface';
import type { Collection, Collections, TranslatedProps } from '@staticcms/core/interface';
const SearchContainer = styled('div')`
position: relative;

View File

@ -6,9 +6,9 @@ import React, { useCallback } from 'react';
import { translate } from 'react-polyglot';
import { useNavigate } from 'react-router-dom';
import { components } from '../../components/UI/styles';
import { components } from '@staticcms/core/components/UI/styles';
import type { Collection, TranslatedProps } from '../../interface';
import type { Collection, TranslatedProps } from '@staticcms/core/interface';
const CollectionTopRow = styled('div')`
display: flex;

View File

@ -2,12 +2,12 @@ import { styled } from '@mui/material/styles';
import React from 'react';
import { translate } from 'react-polyglot';
import Loader from '../../UI/Loader';
import Loader from '@staticcms/core/components/UI/Loader';
import EntryListing from './EntryListing';
import type { CollectionViewStyle } from '../../../constants/collectionViews';
import type { Collection, Collections, Entry, TranslatedProps } from '../../../interface';
import type Cursor from '../../../lib/util/Cursor';
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
import type { Collection, Collections, Entry, TranslatedProps } from '@staticcms/core/interface';
import type Cursor from '@staticcms/core/lib/util/Cursor';
const PaginationMessage = styled('div')`
padding: 16px;

View File

@ -6,24 +6,24 @@ import { connect } from 'react-redux';
import {
loadEntries as loadEntriesAction,
traverseCollectionCursor as traverseCollectionCursorAction,
} from '../../../actions/entries';
import { colors } from '../../../components/UI/styles';
import { Cursor } from '../../../lib/util';
import { selectCollectionEntriesCursor } from '../../../reducers/cursors';
} from '@staticcms/core/actions/entries';
import { colors } from '@staticcms/core/components/UI/styles';
import { Cursor } from '@staticcms/core/lib/util';
import { selectCollectionEntriesCursor } from '@staticcms/core/reducers/cursors';
import {
selectEntries,
selectEntriesLoaded,
selectGroups,
selectIsFetching,
} from '../../../reducers/entries';
} from '@staticcms/core/reducers/entries';
import Entries from './Entries';
import type { ComponentType } from 'react';
import type { t } from 'react-polyglot';
import type { ConnectedProps } from 'react-redux';
import type { CollectionViewStyle } from '../../../constants/collectionViews';
import type { Collection, Entry, GroupOfEntries, TranslatedProps } from '../../../interface';
import type { RootState } from '../../../store';
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
import type { Collection, Entry, GroupOfEntries, TranslatedProps } from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
const GroupHeading = styled('h2')`
font-size: 23px;

View File

@ -5,15 +5,15 @@ import { connect } from 'react-redux';
import {
clearSearch as clearSearchAction,
searchEntries as searchEntriesAction,
} from '../../../actions/search';
import { Cursor } from '../../../lib/util';
import { selectSearchedEntries } from '../../../reducers';
} from '@staticcms/core/actions/search';
import { Cursor } from '@staticcms/core/lib/util';
import { selectSearchedEntries } from '@staticcms/core/reducers';
import Entries from './Entries';
import type { ConnectedProps } from 'react-redux';
import type { CollectionViewStyle } from '../../../constants/collectionViews';
import type { Collections } from '../../../interface';
import type { RootState } from '../../../store';
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
import type { Collections } from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
const EntriesSearch = ({
collections,

View File

@ -7,15 +7,15 @@ import React, { useEffect, useMemo, useState } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { getAsset as getAssetAction } from '../../../actions/media';
import { VIEW_STYLE_GRID, VIEW_STYLE_LIST } from '../../../constants/collectionViews';
import { selectEntryCollectionTitle } from '../../../lib/util/collection.util';
import { selectIsLoadingAsset } from '../../../reducers/medias';
import { getAsset as getAssetAction } from '@staticcms/core/actions/media';
import { VIEW_STYLE_GRID, VIEW_STYLE_LIST } from '@staticcms/core/constants/collectionViews';
import { selectEntryCollectionTitle } from '@staticcms/core/lib/util/collection.util';
import { selectIsLoadingAsset } from '@staticcms/core/reducers/medias';
import type { ConnectedProps } from 'react-redux';
import type { CollectionViewStyle } from '../../../constants/collectionViews';
import type { Field, Collection, Entry } from '../../../interface';
import type { RootState } from '../../../store';
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
import type { Field, Collection, Entry } from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
const EntryCard = ({
collection,

View File

@ -2,14 +2,14 @@ import { styled } from '@mui/material/styles';
import React, { useCallback, useMemo } from 'react';
import { Waypoint } from 'react-waypoint';
import { VIEW_STYLE_LIST } from '../../../constants/collectionViews';
import { transientOptions } from '../../../lib';
import { selectFields, selectInferedField } from '../../../lib/util/collection.util';
import { VIEW_STYLE_LIST } from '@staticcms/core/constants/collectionViews';
import { transientOptions } from '@staticcms/core/lib';
import { selectFields, selectInferedField } from '@staticcms/core/lib/util/collection.util';
import EntryCard from './EntryCard';
import type { CollectionViewStyle } from '../../../constants/collectionViews';
import type { Field, Collection, Collections, Entry } from '../../../interface';
import type Cursor from '../../../lib/util/Cursor';
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
import type { Field, Collection, Collections, Entry } from '@staticcms/core/interface';
import type Cursor from '@staticcms/core/lib/util/Cursor';
interface CardsGridProps {
$layout: CollectionViewStyle;

View File

@ -5,10 +5,11 @@ import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import type { FilterMap, TranslatedProps, ViewFilter } from '../../interface';
import type { FilterMap, TranslatedProps, ViewFilter } from '@staticcms/core/interface';
import type { MouseEvent } from 'react';
interface FilterControlProps {
filter: Record<string, FilterMap>;
@ -22,9 +23,9 @@ const FilterControl = ({
onFilterClick,
filter,
}: TranslatedProps<FilterControlProps>) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {

View File

@ -1,14 +1,15 @@
import CheckIcon from '@mui/icons-material/Check';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import Button from '@mui/material/Button/Button';
import Button from '@mui/material/Button';
import ListItemText from '@mui/material/ListItemText';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import React, { useCallback, useMemo } from 'react';
import { translate } from 'react-polyglot';
import CheckIcon from '@mui/icons-material/Check';
import { styled } from '@mui/material/styles';
import React, { useCallback, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import type { GroupMap, TranslatedProps, ViewGroup } from '../../interface';
import type { GroupMap, TranslatedProps, ViewGroup } from '@staticcms/core/interface';
import type { MouseEvent } from 'react';
const StyledMenuIconWrapper = styled('div')`
width: 32px;
@ -30,9 +31,9 @@ const GroupControl = ({
t,
onGroupClick,
}: TranslatedProps<GroupControlProps>) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {

View File

@ -1,20 +1,20 @@
import { styled } from '@mui/material/styles';
import ArticleIcon from '@mui/icons-material/Article';
import { styled } from '@mui/material/styles';
import sortBy from 'lodash/sortBy';
import { dirname, sep } from 'path';
import React, { useCallback, useEffect, useState } from 'react';
import React, { Fragment, useCallback, useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import { colors, components } from '../../components/UI/styles';
import { transientOptions } from '../../lib';
import { selectEntryCollectionTitle } from '../../lib/util/collection.util';
import { stringTemplate } from '../../lib/widgets';
import { selectEntries } from '../../reducers/entries';
import { colors, components } from '@staticcms/core/components/UI/styles';
import { transientOptions } from '@staticcms/core/lib';
import { selectEntryCollectionTitle } from '@staticcms/core/lib/util/collection.util';
import { stringTemplate } from '@staticcms/core/lib/widgets';
import { selectEntries } from '@staticcms/core/reducers/entries';
import type { Collection, Entry } from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
import type { ConnectedProps } from 'react-redux';
import type { Collection, Entry } from '../../interface';
import type { RootState } from '../../store';
const { addFileTemplateFields } = stringTemplate;
@ -124,7 +124,7 @@ const TreeNode = ({ collection, treeData, depth = 0, onToggle }: TreeNodeProps)
const hasChildren = depth === 0 || node.children.some(c => c.children.some(c => c.isDir));
return (
<React.Fragment key={node.path}>
<Fragment key={node.path}>
<TreeNavLink
to={to}
$activeClassName="sidebar-active"
@ -146,7 +146,7 @@ const TreeNode = ({ collection, treeData, depth = 0, onToggle }: TreeNodeProps)
onToggle={onToggle}
/>
)}
</React.Fragment>
</Fragment>
);
})}
</>

View File

@ -11,15 +11,15 @@ import Typography from '@mui/material/Typography';
import React, { useMemo } from 'react';
import { translate } from 'react-polyglot';
import { searchCollections } from '../../actions/collections';
import { colors } from '../../components/UI/styles';
import { getAdditionalLinks, getIcon } from '../../lib/registry';
import { searchCollections } from '@staticcms/core/actions/collections';
import { colors } from '@staticcms/core/components/UI/styles';
import { getAdditionalLinks, getIcon } from '@staticcms/core/lib/registry';
import NavLink from '../UI/NavLink';
import CollectionSearch from './CollectionSearch';
import NestedCollection from './NestedCollection';
import type { ReactNode } from 'react';
import type { Collection, Collections, TranslatedProps } from '../../interface';
import type { Collection, Collections, TranslatedProps } from '@staticcms/core/interface';
const StyledSidebar = styled('div')`
position: sticky;

View File

@ -1,20 +1,26 @@
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import { styled } from '@mui/material';
import Button from '@mui/material/Button';
import ListItemText from '@mui/material/ListItemText';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import React, { useCallback, useMemo } from 'react';
import { styled } from '@mui/material/styles';
import React, { useCallback, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import {
SORT_DIRECTION_ASCENDING,
SORT_DIRECTION_DESCENDING,
SORT_DIRECTION_NONE,
} from '../../constants';
} from '@staticcms/core/constants';
import type { SortableField, SortDirection, SortMap, TranslatedProps } from '../../interface';
import type {
SortableField,
SortDirection,
SortMap,
TranslatedProps,
} from '@staticcms/core/interface';
import type { MouseEvent } from 'react';
const StyledMenuIconWrapper = styled('div')`
width: 32px;
@ -42,9 +48,9 @@ interface SortControlProps {
}
const SortControl = ({ t, fields, onSortClick, sort }: TranslatedProps<SortControlProps>) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {

View File

@ -4,9 +4,9 @@ import ReorderSharpIcon from '@mui/icons-material/ReorderSharp';
import IconButton from '@mui/material/IconButton';
import React from 'react';
import { VIEW_STYLE_GRID, VIEW_STYLE_LIST } from '../../constants/collectionViews';
import { VIEW_STYLE_GRID, VIEW_STYLE_LIST } from '@staticcms/core/constants/collectionViews';
import type { CollectionViewStyle } from '../../constants/collectionViews';
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
const ViewControlsSection = styled('div')`
margin-left: 24px;

View File

@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import { connect } from 'react-redux';
import { logoutUser as logoutUserAction } from '../../actions/auth';
import { logoutUser as logoutUserAction } from '@staticcms/core/actions/auth';
import {
changeDraftFieldValidation as changeDraftFieldValidationAction,
createDraftDuplicateFromEntry as createDraftDuplicateFromEntryAction,
@ -18,15 +18,15 @@ import {
persistEntry as persistEntryAction,
persistLocalBackup as persistLocalBackupAction,
retrieveLocalBackup as retrieveLocalBackupAction,
} from '../../actions/entries';
} from '@staticcms/core/actions/entries';
import {
loadScroll as loadScrollAction,
toggleScroll as toggleScrollAction,
} from '../../actions/scroll';
import { selectFields } from '../../lib/util/collection.util';
import { useWindowEvent } from '../../lib/util/window.util';
import { selectEntry } from '../../reducers';
import { history, navigateToCollection, navigateToNewEntry } from '../../routing/history';
} from '@staticcms/core/actions/scroll';
import { selectFields } from '@staticcms/core/lib/util/collection.util';
import { useWindowEvent } from '@staticcms/core/lib/util/window.util';
import { selectEntry } from '@staticcms/core/reducers';
import { history, navigateToCollection, navigateToNewEntry } from '@staticcms/core/routing/history';
import confirm from '../UI/Confirm';
import Loader from '../UI/Loader';
import EditorInterface from './EditorInterface';
@ -34,8 +34,13 @@ import EditorInterface from './EditorInterface';
import type { TransitionPromptHook } from 'history';
import type { ComponentType } from 'react';
import type { ConnectedProps } from 'react-redux';
import type { Collection, EditorPersistOptions, Entry, TranslatedProps } from '../../interface';
import type { RootState } from '../../store';
import type {
Collection,
EditorPersistOptions,
Entry,
TranslatedProps,
} from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
const Editor = ({
entry,

View File

@ -1,30 +1,30 @@
import { styled } from '@mui/material/styles';
import { isEqual } from 'lodash';
import isEmpty from 'lodash/isEmpty';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { createElement, useCallback, useEffect, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import { connect } from 'react-redux';
import {
changeDraftField as changeDraftFieldAction,
changeDraftFieldValidation as changeDraftFieldValidationAction,
} from '../../../actions/entries';
import { getAsset as getAssetAction } from '../../../actions/media';
} from '@staticcms/core/actions/entries';
import { getAsset as getAssetAction } from '@staticcms/core/actions/media';
import {
clearMediaControl as clearMediaControlAction,
openMediaLibrary as openMediaLibraryAction,
removeInsertedMedia as removeInsertedMediaAction,
removeMediaControl as removeMediaControlAction,
} from '../../../actions/mediaLibrary';
import { query as queryAction } from '../../../actions/search';
import { borders, colors, lengths, transitions } from '../../../components/UI/styles';
import { transientOptions } from '../../../lib';
import { resolveWidget } from '../../../lib/registry';
import { getFieldLabel } from '../../../lib/util/field.util';
import { validate } from '../../../lib/util/validation.util';
import { selectIsLoadingAsset } from '../../../reducers/medias';
} from '@staticcms/core/actions/mediaLibrary';
import { query as queryAction } from '@staticcms/core/actions/search';
import { borders, colors, lengths, transitions } from '@staticcms/core/components/UI/styles';
import { transientOptions } from '@staticcms/core/lib';
import useMemoCompare from '@staticcms/core/lib/hooks/useMemoCompare';
import { resolveWidget } from '@staticcms/core/lib/registry';
import { getFieldLabel } from '@staticcms/core/lib/util/field.util';
import { validate } from '@staticcms/core/lib/util/validation.util';
import { selectIsLoadingAsset } from '@staticcms/core/reducers/medias';
import type { ComponentType } from 'react';
import type { ConnectedProps } from 'react-redux';
import type {
Field,
FieldsErrors,
@ -34,8 +34,10 @@ import type {
UnknownField,
ValueOrNestedValue,
Widget,
} from '../../../interface';
import type { RootState } from '../../../store';
} from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
import type { ComponentType } from 'react';
import type { ConnectedProps } from 'react-redux';
/**
* This is a necessary bridge as we are still passing classnames to widgets
@ -130,7 +132,6 @@ const ControlHint = styled(
);
const EditorControl = ({
className,
clearMediaControl,
collection,
config: configState,
@ -167,7 +168,8 @@ const EditorControl = ({
);
const [dirty, setDirty] = useState(!isEmpty(value));
const errors = useMemo(() => fieldsErrors[path] ?? [], [fieldsErrors, path]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const errors = useMemo(() => fieldsErrors[path] ?? [], [fieldsErrors[path]]);
const hasErrors = (submitted || dirty) && Boolean(errors.length);
const handleGetAsset: GetAssetFunction = useMemo(
@ -189,71 +191,101 @@ const EditorControl = ({
const handleChangeDraftField = useCallback(
(value: ValueOrNestedValue) => {
setDirty(true);
changeDraftField({ path, field, value, entry, i18n });
changeDraftField({ path, field, value, i18n });
},
[changeDraftField, entry, field, i18n, path],
[changeDraftField, field, i18n, path],
);
const config = useMemo(() => configState.config, [configState.config]);
if (!collection || !entry || !config) {
return null;
}
return (
<ControlContainer className={className} $isHidden={isHidden}>
<>
{React.createElement(widget.control, {
key: `field_${path}`,
collection,
config,
entry,
field: field as UnknownField,
fieldsErrors,
submitted,
getAsset: handleGetAsset,
isDisabled: isDisabled ?? false,
isFieldDuplicate,
isFieldHidden,
label: getFieldLabel(field, t),
locale,
mediaPaths,
onChange: handleChangeDraftField,
clearMediaControl,
openMediaLibrary,
removeInsertedMedia,
removeMediaControl,
path,
query,
t,
value,
forList,
i18n,
hasErrors,
})}
{fieldHint ? (
<ControlHint key="hint" $error={hasErrors}>
{fieldHint}
</ControlHint>
) : null}
{hasErrors ? (
<ControlErrorsList key="errors">
{errors.map(error => {
return (
error.message &&
typeof error.message === 'string' && (
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>{error.message}</li>
)
);
})}
</ControlErrorsList>
) : null}
</>
</ControlContainer>
);
const finalValue = useMemoCompare(value, isEqual);
return useMemo(() => {
if (!collection || !entry || !config) {
return null;
}
return (
<ControlContainer $isHidden={isHidden}>
<>
{createElement(widget.control, {
key: `field_${path}`,
collection,
config,
entry,
field: field as UnknownField,
fieldsErrors,
submitted,
getAsset: handleGetAsset,
isDisabled: isDisabled ?? false,
isFieldDuplicate,
isFieldHidden,
label: getFieldLabel(field, t),
locale,
mediaPaths,
onChange: handleChangeDraftField,
clearMediaControl,
openMediaLibrary,
removeInsertedMedia,
removeMediaControl,
path,
query,
t,
value: finalValue,
forList,
i18n,
hasErrors,
})}
{fieldHint ? (
<ControlHint key="hint" $error={hasErrors}>
{fieldHint}
</ControlHint>
) : null}
{hasErrors ? (
<ControlErrorsList key="errors">
{errors.map(error => {
return (
error.message &&
typeof error.message === 'string' && (
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>{error.message}</li>
)
);
})}
</ControlErrorsList>
) : null}
</>
</ControlContainer>
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
collection,
config,
path,
errors,
isHidden,
widget.control,
field,
submitted,
handleGetAsset,
isDisabled,
t,
locale,
mediaPaths,
handleChangeDraftField,
clearMediaControl,
openMediaLibrary,
removeInsertedMedia,
removeMediaControl,
query,
finalValue,
forList,
i18n,
hasErrors,
fieldHint,
]);
};
interface EditorControlOwnProps {
className?: string;
field: Field;
fieldsErrors: FieldsErrors;
submitted: boolean;

View File

@ -3,11 +3,11 @@ import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import { styled } from '@mui/material/styles';
import get from 'lodash/get';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { connect } from 'react-redux';
import { changeDraftField as changeDraftFieldAction } from '../../../actions/entries';
import confirm from '../../../components/UI/Confirm';
import { changeDraftField as changeDraftFieldAction } from '@staticcms/core/actions/entries';
import confirm from '@staticcms/core/components/UI/Confirm';
import {
getI18nInfo,
getLocaleDataPath,
@ -15,10 +15,9 @@ import {
isFieldDuplicate,
isFieldHidden,
isFieldTranslatable,
} from '../../../lib/i18n';
} from '@staticcms/core/lib/i18n';
import EditorControl from './EditorControl';
import type { ConnectedProps } from 'react-redux';
import type {
Collection,
Entry,
@ -27,8 +26,10 @@ import type {
I18nSettings,
TranslatedProps,
ValueOrNestedValue,
} from '../../../interface';
import type { RootState } from '../../../store';
} from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
import type { MouseEvent } from 'react';
import type { ConnectedProps } from 'react-redux';
const ControlPaneContainer = styled('div')`
max-width: 1000px;
@ -50,9 +51,9 @@ interface LocaleDropdownProps {
}
const LocaleDropdown = ({ locales, dropdownText, onLocaleChange }: LocaleDropdownProps) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {
@ -154,7 +155,7 @@ const EditorControlPane = ({
sourceLocale !== i18n?.defaultLocale,
sourceLocale,
);
changeDraftField({ path: field.name, field, value: copyValue, entry, i18n });
changeDraftField({ path: field.name, field, value: copyValue, i18n });
}
});
},

View File

@ -6,10 +6,10 @@ import { styled } from '@mui/material/styles';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
import { colorsRaw, components, zIndex } from '../../components/UI/styles';
import { transientOptions } from '../../lib';
import { getI18nInfo, getPreviewEntry, hasI18n } from '../../lib/i18n';
import { getFileFromSlug } from '../../lib/util/collection.util';
import { colorsRaw, components, zIndex } from '@staticcms/core/components/UI/styles';
import { transientOptions } from '@staticcms/core/lib';
import { getI18nInfo, getPreviewEntry, hasI18n } from '@staticcms/core/lib/i18n';
import { getFileFromSlug } from '@staticcms/core/lib/util/collection.util';
import EditorControlPane from './EditorControlPane/EditorControlPane';
import EditorPreviewPane from './EditorPreviewPane/EditorPreviewPane';
import EditorToolbar from './EditorToolbar';
@ -22,7 +22,7 @@ import type {
FieldsErrors,
TranslatedProps,
User,
} from '../../interface';
} from '@staticcms/core/interface';
const PREVIEW_VISIBLE = 'cms.preview-visible';
const I18N_VISIBLE = 'cms.i18n-visible';

View File

@ -1,7 +1,7 @@
import React from 'react';
import { styled } from '@mui/material/styles';
import type { Field, TemplatePreviewProps } from '../../../interface';
import type { Field, TemplatePreviewProps } from '@staticcms/core/interface';
function isVisible(field: Field) {
return field.widget !== 'hidden';

View File

@ -1,6 +1,6 @@
import React, { memo } from 'react';
import { createElement, memo } from 'react';
import type { TemplatePreviewComponent, TemplatePreviewProps } from '../../../interface';
import type { TemplatePreviewComponent, TemplatePreviewProps } from '@staticcms/core/interface';
interface EditorPreviewContentProps {
previewComponent?: TemplatePreviewComponent;
@ -13,7 +13,7 @@ const EditorPreviewContent = memo(
return null;
}
return React.createElement(previewComponent, previewProps);
return createElement(previewComponent, previewProps);
},
);

View File

@ -1,26 +1,24 @@
import { styled } from '@mui/material/styles';
import React, { isValidElement, useCallback, useMemo } from 'react';
import React, { Fragment, isValidElement, useCallback, useMemo } from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer } from 'react-frame-component';
import { translate } from 'react-polyglot';
import { connect } from 'react-redux';
import { ScrollSyncPane } from 'react-scroll-sync';
import { getAsset as getAssetAction } from '../../../actions/media';
import { lengths } from '../../../components/UI/styles';
import { getPreviewStyles, getPreviewTemplate, resolveWidget } from '../../../lib/registry';
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 { getAsset as getAssetAction } from '@staticcms/core/actions/media';
import { ErrorBoundary } from '@staticcms/core/components/UI';
import { lengths } from '@staticcms/core/components/UI/styles';
import { getPreviewStyles, getPreviewTemplate, resolveWidget } from '@staticcms/core/lib/registry';
import { selectTemplateName, useInferedFields } from '@staticcms/core/lib/util/collection.util';
import { selectField } from '@staticcms/core/lib/util/field.util';
import { selectIsLoadingAsset } from '@staticcms/core/reducers/medias';
import { getTypedFieldForValue } from '@staticcms/list/typedListHelpers';
import EditorPreview from './EditorPreview';
import EditorPreviewContent from './EditorPreviewContent';
import PreviewHOC from './PreviewHOC';
import type { ComponentType, ReactFragment, ReactNode } from 'react';
import type { ConnectedProps } from 'react-redux';
import type { InferredField } from '../../../constants/fieldInference';
import type { InferredField } from '@staticcms/core/constants/fieldInference';
import type {
Collection,
Config,
@ -35,8 +33,10 @@ import type {
TranslatedProps,
ValueOrNestedValue,
WidgetPreviewComponent,
} from '../../../interface';
import type { RootState } from '../../../store';
} from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
import type { ComponentType, ReactFragment, ReactNode } from 'react';
import type { ConnectedProps } from 'react-redux';
const PreviewPaneFrame = styled(Frame)`
width: 100%;
@ -180,10 +180,10 @@ function isJsxElement(value: any): value is JSX.Element {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isReactFragment(value: any): value is ReactFragment {
if (value.type) {
return value.type === React.Fragment;
return value.type === Fragment;
}
return value === React.Fragment;
return value === Fragment;
}
function getWidget(
@ -478,7 +478,9 @@ const PreviewPane = (props: TranslatedProps<EditorPreviewPaneProps>) => {
() => `
<!DOCTYPE html>
<html>
<head><base target="_blank"/></head>
<head>
<base target="_blank"/>
</head>
<body><div></div></body>
</html>
`,

View File

@ -1,6 +1,6 @@
import React from 'react';
import { cloneElement, createElement, isValidElement } from 'react';
import type { WidgetPreviewComponent, WidgetPreviewProps } from '../../../interface';
import type { WidgetPreviewComponent, WidgetPreviewProps } from '@staticcms/core/interface';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
interface PreviewHOCProps extends Omit<WidgetPreviewProps, 'widgetFor'> {
@ -11,10 +11,10 @@ interface PreviewHOCProps extends Omit<WidgetPreviewProps, 'widgetFor'> {
const PreviewHOC = ({ previewComponent, ...props }: PreviewHOCProps) => {
if (!previewComponent) {
return null;
} else if (React.isValidElement(previewComponent)) {
return React.cloneElement(previewComponent, props);
} else if (isValidElement(previewComponent)) {
return cloneElement(previewComponent, props);
} else {
return React.createElement(previewComponent, props);
return createElement(previewComponent, props);
}
};

View File

@ -3,7 +3,7 @@ import { Navigate, useParams } from 'react-router-dom';
import Editor from './Editor';
import type { Collections } from '../../interface';
import type { Collections } from '@staticcms/core/interface';
function getDefaultPath(collections: Collections) {
const first = Object.values(collections).filter(collection => collection.hide !== true)[0];

View File

@ -3,7 +3,7 @@ import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import AppBar from '@mui/material/AppBar';
import Button from '@mui/material/Button';
import CircularProgress from '@mui/material/CircularProgress';
import green from '@mui/material/colors/green';
import { green } from '@mui/material/colors';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import { styled } from '@mui/material/styles';
@ -11,13 +11,18 @@ import Toolbar from '@mui/material/Toolbar';
import React, { useCallback, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import { colors, components, zIndex } from '../../components/UI/styles';
import { selectAllowDeletion } from '../../lib/util/collection.util';
import { colors, components, zIndex } from '@staticcms/core/components/UI/styles';
import { selectAllowDeletion } from '@staticcms/core/lib/util/collection.util';
import { SettingsDropdown } from '../UI';
import NavLink from '../UI/NavLink';
import type { MouseEvent } from 'react';
import type { Collection, EditorPersistOptions, TranslatedProps, User } from '../../interface';
import type {
Collection,
EditorPersistOptions,
TranslatedProps,
User,
} from '@staticcms/core/interface';
const StyledAppBar = styled(AppBar)`
background-color: ${colors.foreground};

View File

@ -1,7 +1,7 @@
import React from 'react';
import { translate } from 'react-polyglot';
import type { WidgetControlProps, TranslatedProps } from '../../../interface';
import type { WidgetControlProps, TranslatedProps } from '@staticcms/core/interface';
const UnknownControl = ({ field, t }: TranslatedProps<WidgetControlProps<unknown>>) => {
return <div>{t('editor.editorWidgets.unknownControl.noControl', { widget: field.widget })}</div>;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { translate } from 'react-polyglot';
import type { WidgetPreviewProps, TranslatedProps } from '../../../interface';
import type { WidgetPreviewProps, TranslatedProps } from '@staticcms/core/interface';
const UnknownPreview = ({ field, t }: TranslatedProps<WidgetPreviewProps>) => {
return (

View File

@ -1,4 +1,4 @@
import { registerWidget } from '../../lib/registry';
import { registerWidget } from '@staticcms/core/lib/registry';
import UnknownControl from './Unknown/UnknownControl';
import UnknownPreview from './Unknown/UnknownPreview';

View File

@ -10,17 +10,17 @@ import {
loadMedia as loadMediaAction,
loadMediaDisplayURL as loadMediaDisplayURLAction,
persistMedia as persistMediaAction,
} from '../../actions/mediaLibrary';
import { fileExtension } from '../../lib/util';
import { selectMediaFiles } from '../../reducers/mediaLibrary';
} from '@staticcms/core/actions/mediaLibrary';
import { fileExtension } from '@staticcms/core/lib/util';
import { selectMediaFiles } from '@staticcms/core/reducers/mediaLibrary';
import alert from '../UI/Alert';
import confirm from '../UI/Confirm';
import MediaLibraryModal from './MediaLibraryModal';
import type { ChangeEvent, KeyboardEvent } from 'react';
import type { ConnectedProps } from 'react-redux';
import type { MediaFile, TranslatedProps } from '../../interface';
import type { RootState } from '../../store';
import type { MediaFile, TranslatedProps } from '@staticcms/core/interface';
import type { RootState } from '@staticcms/core/store';
/**
* Extensions used to determine which files to show when the media library is

View File

@ -2,9 +2,9 @@ import Button from '@mui/material/Button';
import copyToClipboard from 'copy-text-to-clipboard';
import React, { useCallback, useEffect, useState } from 'react';
import { isAbsolutePath } from '../../lib/util';
import { isAbsolutePath } from '@staticcms/core/lib/util';
import type { TranslatedProps } from '../../interface';
import type { TranslatedProps } from '@staticcms/core/interface';
export interface CopyToClipBoardButtonProps {
disabled: boolean;

View File

@ -1,10 +1,10 @@
import { styled } from '@mui/material/styles';
import React, { useEffect, useMemo } from 'react';
import { borders, colors, effects, lengths, shadows } from '../../components/UI/styles';
import { transientOptions } from '../../lib';
import { borders, colors, effects, lengths, shadows } from '@staticcms/core/components/UI/styles';
import { transientOptions } from '@staticcms/core/lib';
import type { MediaLibraryDisplayURL } from '../../reducers/mediaLibrary';
import type { MediaLibraryDisplayURL } from '@staticcms/core/reducers/mediaLibrary';
const IMAGE_HEIGHT = 160;

View File

@ -7,8 +7,11 @@ import { FixedSizeGrid as Grid } from 'react-window';
import MediaLibraryCard from './MediaLibraryCard';
import type { GridChildComponentProps } from 'react-window';
import type { MediaFile } from '../../interface';
import type { MediaLibraryDisplayURL, MediaLibraryState } from '../../reducers/mediaLibrary';
import type { MediaFile } from '@staticcms/core/interface';
import type {
MediaLibraryDisplayURL,
MediaLibraryState,
} from '@staticcms/core/reducers/mediaLibrary';
export interface MediaLibraryCardItem {
displayURL?: MediaLibraryDisplayURL;

View File

@ -12,8 +12,8 @@ import MediaLibraryCardGrid from './MediaLibraryCardGrid';
import MediaLibraryTop from './MediaLibraryTop';
import type { ChangeEvent, ChangeEventHandler, KeyboardEventHandler } from 'react';
import type { MediaFile, TranslatedProps } from '../../interface';
import type { MediaLibraryState } from '../../reducers/mediaLibrary';
import type { MediaFile, TranslatedProps } from '@staticcms/core/interface';
import type { MediaLibraryState } from '@staticcms/core/reducers/mediaLibrary';
const StyledFab = styled(Fab)`
position: absolute;

View File

@ -5,11 +5,11 @@ import React from 'react';
import { CopyToClipBoardButton } from './MediaLibraryButtons';
import MediaLibrarySearch from './MediaLibrarySearch';
import { buttons, shadows, zIndex } from '../../components/UI/styles';
import { buttons, shadows, zIndex } from '@staticcms/core/components/UI/styles';
import FileUploadButton from '../UI/FileUploadButton';
import type { ChangeEvent, ChangeEventHandler, KeyboardEventHandler } from 'react';
import type { MediaFile, TranslatedProps } from '../../interface';
import type { MediaFile, TranslatedProps } from '@staticcms/core/interface';
const LibraryTop = styled('div')`
position: relative;

View File

@ -7,8 +7,8 @@ import DialogTitle from '@mui/material/DialogTitle';
import React, { useCallback, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import AlertEvent from '../../lib/util/events/AlertEvent';
import { useWindowEvent } from '../../lib/util/window.util';
import AlertEvent from '@staticcms/core/lib/util/events/AlertEvent';
import { useWindowEvent } from '@staticcms/core/lib/util/window.util';
import type { TranslateProps } from 'react-polyglot';

View File

@ -6,7 +6,7 @@ import GoBackButton from './GoBackButton';
import Icon from './Icon';
import type { MouseEventHandler, ReactNode } from 'react';
import type { TranslatedProps } from '../../interface';
import type { TranslatedProps } from '@staticcms/core/interface';
const StyledAuthenticationPage = styled('section')`
display: flex;

View File

@ -7,8 +7,8 @@ import DialogTitle from '@mui/material/DialogTitle';
import React, { useCallback, useMemo, useState } from 'react';
import { translate } from 'react-polyglot';
import ConfirmEvent from '../../lib/util/events/ConfirmEvent';
import { useWindowEvent } from '../../lib/util/window.util';
import ConfirmEvent from '@staticcms/core/lib/util/events/ConfirmEvent';
import { useWindowEvent } from '@staticcms/core/lib/util/window.util';
import type { TranslateProps } from 'react-polyglot';

View File

@ -2,15 +2,15 @@ import { styled } from '@mui/material/styles';
import cleanStack from 'clean-stack';
import copyToClipboard from 'copy-text-to-clipboard';
import truncate from 'lodash/truncate';
import React from 'react';
import React, { Component } from 'react';
import { translate } from 'react-polyglot';
import yaml from 'yaml';
import { localForage } from '../../lib/util';
import { buttons, colors } from '../../components/UI/styles';
import { buttons, colors } from '@staticcms/core/components/UI/styles';
import { localForage } from '@staticcms/core/lib/util';
import type { Config, TranslatedProps } from '@staticcms/core/interface';
import type { ReactNode } from 'react';
import type { Config, TranslatedProps } from '../../interface';
const ISSUE_URL = 'https://github.com/StaticJsCMS/static-cms/issues/new?';
@ -45,7 +45,7 @@ function buildIssueTemplate(config: Config) {
}
const template = getIssueTemplate(
version,
config.backend.name,
config?.backend?.name,
navigator.userAgent,
yaml.stringify(config),
);
@ -145,7 +145,7 @@ interface ErrorBoundaryState {
backup: string;
}
export class ErrorBoundary extends React.Component<
export class ErrorBoundary extends Component<
TranslatedProps<ErrorBoundaryProps>,
ErrorBoundaryState
> {

View File

@ -1,10 +1,12 @@
import Button from '@mui/material/Button';
import React from 'react';
import type { ChangeEventHandler } from 'react';
export interface FileUploadButtonProps {
label: string;
imagesOnly?: boolean;
onChange: React.ChangeEventHandler<HTMLInputElement>;
onChange: ChangeEventHandler<HTMLInputElement>;
disabled?: boolean;
}

View File

@ -2,7 +2,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Button from '@mui/material/Button';
import React from 'react';
import type { TranslatedProps } from '../../interface';
import type { TranslatedProps } from '@staticcms/core/interface';
interface GoBackButtonProps {
href: string;

View File

@ -2,7 +2,7 @@ import { styled } from '@mui/material/styles';
import React from 'react';
import icons from './Icon/icons';
import transientOptions from '../../lib/util/transientOptions';
import transientOptions from '@staticcms/core/lib/util/transientOptions';
import type { IconType } from './Icon/icons';

View File

@ -5,7 +5,7 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import IconButton from '@mui/material/IconButton';
import React from 'react';
import { transientOptions } from '../../lib/util';
import { transientOptions } from '@staticcms/core/lib/util';
import { buttons, colors, lengths, transitions } from './styles';
import type { ComponentClass, MouseEvent, ReactNode } from 'react';

View File

@ -2,8 +2,8 @@ import React, { forwardRef } from 'react';
import { NavLink as NavLinkBase } from 'react-router-dom';
import { styled } from '@mui/material/styles';
import { colors } from '../../components/UI/styles';
import { transientOptions } from '../../lib';
import { colors } from '@staticcms/core/components/UI/styles';
import { transientOptions } from '@staticcms/core/lib';
import type { RefAttributes } from 'react';
import type { NavLinkProps as RouterNavLinkProps } from 'react-router-dom';

View File

@ -5,13 +5,13 @@ import IconButton from '@mui/material/IconButton';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import { styled } from '@mui/material/styles';
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import { transientOptions } from '../../lib';
import { transientOptions } from '@staticcms/core/lib';
import { colors, colorsRaw, transitions } from './styles';
import type { ObjectField, TranslatedProps } from '@staticcms/core/interface';
import type { MouseEvent, ReactNode } from 'react';
import type { ObjectField, TranslatedProps } from '../../interface';
const TopBarContainer = styled('div')`
position: relative;
@ -67,9 +67,9 @@ const ObjectWidgetTopBar = ({
hasError = false,
t,
}: TranslatedProps<ObjectWidgetTopBarProps>) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {

View File

@ -1,7 +1,7 @@
import { styled } from '@mui/material/styles';
import React from 'react';
import transientOptions from '../../lib/util/transientOptions';
import transientOptions from '@staticcms/core/lib/util/transientOptions';
interface StyledOutlineProps {
$active: boolean;

View File

@ -3,6 +3,8 @@ import { styled } from '@mui/material/styles';
import useScrollTrigger from '@mui/material/useScrollTrigger';
import React, { useCallback } from 'react';
import type { ReactNode, MouseEvent } from 'react';
const StyledScrollTop = styled('div')`
position: fixed;
bottom: 16px;
@ -10,7 +12,7 @@ const StyledScrollTop = styled('div')`
`;
interface ScrollTopProps {
children: React.ReactNode;
children: ReactNode;
}
const ScrollTop = ({ children }: ScrollTopProps) => {
@ -19,7 +21,7 @@ const ScrollTop = ({ children }: ScrollTopProps) => {
threshold: 100,
});
const handleClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLDivElement>) => {
const anchor = ((event.target as HTMLDivElement).ownerDocument || document).querySelector(
'#back-to-top-anchor',
);

View File

@ -1,13 +1,14 @@
import PersonIcon from '@mui/icons-material/Person';
import Avatar from '@mui/material/Avatar';
import IconButton from '@mui/material/IconButton';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Tooltip from '@mui/material/Tooltip';
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import { translate } from 'react-polyglot';
import PersonIcon from '@mui/icons-material/Person';
import type { TranslatedProps } from '../../interface';
import type { TranslatedProps } from '@staticcms/core/interface';
import type { MouseEvent } from 'react';
interface AvatarImageProps {
imageUrl: string | undefined;
@ -33,9 +34,9 @@ const SettingsDropdown = ({
onLogoutClick,
t,
}: TranslatedProps<SettingsDropdownProps>) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {

View File

@ -519,10 +519,7 @@ function GlobalStyles() {
}
${quantifier} {
font-family: ${fonts.primary};
font-weight: normal;
background-color: ${colors.background};
color: ${colors.text};
margin: 0;
.ol-viewport {

View File

@ -4,13 +4,13 @@ import { translate } from 'react-polyglot';
import { connect } from 'react-redux';
import { useParams } from 'react-router-dom';
import { getAdditionalLink } from '../../lib/registry';
import { getAdditionalLink } from '@staticcms/core/lib/registry';
import MainView from '../App/MainView';
import Sidebar from '../Collection/Sidebar';
import type { ComponentType } from 'react';
import type { ConnectedProps } from 'react-redux';
import type { RootState } from '../../store';
import type { RootState } from '@staticcms/core/store';
const StyledPageContent = styled('div')`
width: 100%;

View File

@ -5,17 +5,18 @@ import Snackbar from '@mui/material/Snackbar';
import React, { useCallback, useEffect, useState } from 'react';
import { translate } from 'react-polyglot';
import { useAppDispatch, useAppSelector } from '../../store/hooks';
import { removeSnackbarById, selectSnackbars } from '../../store/slices/snackbars';
import { useAppDispatch, useAppSelector } from '@staticcms/core/store/hooks';
import { removeSnackbarById, selectSnackbars } from '@staticcms/core/store/slices/snackbars';
import type { TranslatedProps } from '../../interface';
import type { SnackbarMessage } from '../../store/slices/snackbars';
import type { TranslatedProps } from '@staticcms/core/interface';
import type { SnackbarMessage } from '@staticcms/core/store/slices/snackbars';
import type { SyntheticEvent } from 'react';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface SnackbarsProps {}
const Snackbars = ({ t }: TranslatedProps<SnackbarsProps>) => {
const [open, setOpen] = React.useState(false);
const [open, setOpen] = useState(false);
const [messageInfo, setMessageInfo] = useState<SnackbarMessage | undefined>(undefined);
const snackbars = useAppSelector(selectSnackbars);
@ -34,7 +35,7 @@ const Snackbars = ({ t }: TranslatedProps<SnackbarsProps>) => {
}
}, [snackbars, messageInfo, open, dispatch]);
const handleClose = useCallback((_event?: React.SyntheticEvent | Event, reason?: string) => {
const handleClose = useCallback((_event?: SyntheticEvent | Event, reason?: string) => {
if (reason === 'clickaway') {
return;
}

View File

@ -343,7 +343,7 @@ class ConfigError extends Error {
* `validateConfig` is a pure function. It does not mutate
* the config that is passed in.
*/
export function validateConfig(config: Config) {
export default function validateConfig(config: Config) {
const ajv = new AJV({ allErrors: true, allowUnionTypes: true, $data: true });
uniqueItemProperties(ajv);
select(ajv);

View File

@ -1,2 +1,3 @@
/* eslint-disable import/prefer-default-export */
export const IMAGE_EXTENSION_REGEX =
/(\.apng|\.avif|\.gif|\.jpg|\.jpeg|\.jfif|\.pjpeg|\.pjp|\.png|\.svg|\.webp)$/g;

View File

@ -7,7 +7,7 @@ import {
TestBackend,
} from './backends';
import { registerBackend, registerLocale, registerWidget } from './lib/registry';
import { locales } from './locales';
import locales from './locales';
import {
BooleanWidget,
CodeWidget,
@ -26,7 +26,7 @@ import {
TextWidget,
} from './widgets';
export function addExtensions() {
export default function addExtensions() {
// Register all the things
registerBackend('git-gateway', GitGatewayBackend);
registerBackend('github', GitHubBackend);

View File

@ -1,4 +1,4 @@
export abstract class FileFormatter {
export default abstract class FileFormatter {
abstract fromFile(content: string): object;
abstract toFile(data: object, sortedKeys?: string[], comments?: Record<string, string>): string;
}

View File

@ -1,4 +1,4 @@
import { FileFormatter } from './FileFormatter';
import FileFormatter from './FileFormatter';
class JsonFormatter extends FileFormatter {
fromFile(content: string) {

View File

@ -1,6 +1,6 @@
import toml from '@ltd/j-toml';
import { FileFormatter } from './FileFormatter';
import FileFormatter from './FileFormatter';
class TomlFormatter extends FileFormatter {
fromFile(content: string) {

View File

@ -1,7 +1,7 @@
import yaml from 'yaml';
import { sortKeys } from './helpers';
import { FileFormatter } from './FileFormatter';
import FileFormatter from './FileFormatter';
import type { Pair, YAMLMap, YAMLSeq } from 'yaml/types';

View File

@ -5,7 +5,7 @@ import { FrontmatterInfer, frontmatterJSON, frontmatterTOML, frontmatterYAML } f
import type { Delimiter } from './frontmatter';
import type { Collection, Entry, Format } from '../interface';
import type { FileFormatter } from './FileFormatter';
import type FileFormatter from './FileFormatter';
export const frontmatterFormats = ['yaml-frontmatter', 'toml-frontmatter', 'json-frontmatter'];

View File

@ -3,7 +3,7 @@ import matter from 'gray-matter';
import YamlFormatter from './YamlFormatter';
import TomlFormatter from './TomlFormatter';
import JsonFormatter from './JsonFormatter';
import { FileFormatter } from './FileFormatter';
import FileFormatter from './FileFormatter';
const Languages = {
YAML: 'yaml',

View File

@ -1,3 +1,4 @@
/* eslint-disable import/prefer-default-export */
export function sortKeys<Item>(
sortedKeys: string[],
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -1,5 +1,5 @@
import createReactClass from 'create-react-class';
import React from 'react';
import { createElement, useCallback, useEffect, useMemo, useState } from 'react';
import bootstrap from './bootstrap';
import Registry from './lib/registry';
@ -7,7 +7,7 @@ import Registry from './lib/registry';
export * from './backends';
export * from './widgets';
export * from './media-libraries';
export * from './locales';
export { default as locales } from './locales';
export * from './lib';
export * from './interface';
@ -20,11 +20,11 @@ const CMS = {
if (typeof window !== 'undefined') {
window.CMS = CMS;
window.createClass = window.createClass || createReactClass;
window.useState = window.useState || React.useState;
window.useMemo = window.useMemo || React.useMemo;
window.useEffect = window.useEffect || React.useEffect;
window.useCallback = window.useCallback || React.useCallback;
window.h = window.h || React.createElement;
window.useState = window.useState || useState;
window.useMemo = window.useMemo || useMemo;
window.useEffect = window.useEffect || useEffect;
window.useCallback = window.useCallback || useCallback;
window.h = window.h || createElement;
}
export default CMS;

View File

@ -1,10 +1,10 @@
import flatten from 'lodash/flatten';
import { unsentRequest } from '../../../lib/util';
import { selectEntrySlug } from '../../../lib/util/collection.util';
import { createEntry } from '../../../valueObjects/Entry';
import { unsentRequest } from '@staticcms/core/lib/util';
import { selectEntrySlug } from '@staticcms/core/lib/util/collection.util';
import createEntry from '@staticcms/core/valueObjects/createEntry';
import type { AlgoliaConfig, Collection, Entry, SearchResponse } from '../../../interface';
import type { AlgoliaConfig, Collection, Entry, SearchResponse } from '@staticcms/core/interface';
const { fetchWithTimeout: fetch } = unsentRequest;

View File

@ -1,10 +1,12 @@
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 { LanguageName } from '@uiw/codemirror-extensions-langs';
import type { PropertiesSchema } from 'ajv/dist/types/json-schema';
import type { ComponentType, FunctionComponent, ReactNode } from 'react';
import type {
ComponentType,
FunctionComponent,
JSXElementConstructor,
ReactElement,
ReactNode,
} from 'react';
import type { t, TranslateProps as ReactPolyglotTranslateProps } from 'react-polyglot';
import type { MediaFile as BackendMediaFile } from './backend';
import type { EditorControlProps } from './components/Editor/EditorControlPane/EditorControl';
@ -18,7 +20,6 @@ import type { I18N_STRUCTURE } from './lib/i18n';
import type { AllowedEvent } from './lib/registry';
import type Cursor from './lib/util/Cursor';
import type AssetProxy from './valueObjects/AssetProxy';
import type { MediaHolder } from './widgets/markdown/hooks/useMedia';
export interface Pages {
[collection: string]: { isFetching?: boolean; page?: number; ids: string[] };
@ -278,7 +279,7 @@ export interface WidgetPreviewProps<T = unknown, F extends BaseField = UnknownFi
export type WidgetPreviewComponent<T = unknown, F extends BaseField = UnknownField> =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
| React.ReactElement<unknown, string | React.JSXElementConstructor<any>>
| ReactElement<unknown, string | JSXElementConstructor<any>>
| ComponentType<WidgetPreviewProps<T, F>>;
export type WidgetsFor<P = EntryData> = <K extends keyof P>(
@ -286,11 +287,11 @@ export type WidgetsFor<P = EntryData> = <K extends keyof P>(
) => P[K] extends Array<infer U>
? {
data: U | null;
widgets: Record<keyof U, React.ReactNode>;
widgets: Record<keyof U, ReactNode>;
}[]
: {
data: P[K] | null;
widgets: Record<keyof P[K], React.ReactNode>;
widgets: Record<keyof P[K], ReactNode>;
};
export interface TemplatePreviewProps<T = EntryData, EF extends BaseField = UnknownField> {
@ -841,7 +842,7 @@ export interface I18nInfo {
export interface ProcessedCodeLanguage {
label: string;
identifiers: string[];
codemirror_mode: string;
codemirror_mode: LanguageName;
codemirror_mime_type: string;
}
@ -862,22 +863,25 @@ export interface PreviewStyle {
export interface MarkdownPluginFactoryProps {
config: Config<MarkdownField>;
field: MarkdownField;
media: MediaHolder;
mode: 'editor' | 'preview';
}
export type MarkdownPluginFactory = (props: MarkdownPluginFactoryProps) => MarkdownPlugin;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type MarkdownPluginFactory = (props: MarkdownPluginFactoryProps) => any;
export interface MarkdownToolbarItemsFactoryProps {
imageToolbarButton: MarkdownToolbarItemOptions;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
imageToolbarButton: any;
}
export type MarkdownToolbarItemsFactory = (
props: MarkdownToolbarItemsFactoryProps,
) => (string | MarkdownToolbarItemOptions)[][];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => (string | any)[][];
export interface MarkdownEditorOptions {
initialEditType?: MarkdownEditorType;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
initialEditType?: any;
height?: string;
toolbarItems?: MarkdownToolbarItemsFactory;
plugins?: MarkdownPluginFactory[];

View File

@ -3,7 +3,7 @@ import trimEnd from 'lodash/trimEnd';
import { createNonce, isInsecureProtocol, validateNonce } from './utils';
import type { User, AuthenticatorConfig } from '../../interface';
import type { User, AuthenticatorConfig } from '@staticcms/core/interface';
import type { NetlifyError } from './netlify-auth';
export default class ImplicitAuthenticator {

View File

@ -1,7 +1,7 @@
import trim from 'lodash/trim';
import trimEnd from 'lodash/trimEnd';
import type { User, AuthenticatorConfig } from '../../interface';
import type { User, AuthenticatorConfig } from '@staticcms/core/interface';
const NETLIFY_API = 'https://api.netlify.com';
const AUTH_ENDPOINT = 'auth';

View File

@ -3,7 +3,7 @@ import trimEnd from 'lodash/trimEnd';
import { createNonce, isInsecureProtocol, validateNonce } from './utils';
import type { User, AuthenticatorConfig } from '../../interface';
import type { User, AuthenticatorConfig } from '@staticcms/core/interface';
import type { NetlifyError } from './netlify-auth';
async function sha256(text: string) {

View File

@ -0,0 +1,22 @@
import { useEffect, useState } from 'react';
export default function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
if (delay === 0) {
setDebouncedValue(value);
return;
}
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return delay === 0 ? value : debouncedValue;
}

Some files were not shown because too many files have changed in this diff Show More