Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: TypeScript migration #189

Merged
merged 7 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dist
node_modules
.github
assets
27 changes: 27 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"extends": ["airbnb", "airbnb-typescript", "airbnb/hooks", "prettier"],
"plugins": ["prettier"],
"parserOptions": {
"project": ["./admin/tsconfig.json", "./server/tsconfig.json"]
},
"rules": {
"@typescript-eslint/no-use-before-define": "off",
"class-methods-use-this": "off",
"max-classes-per-file": "off",
"import/prefer-default-export": "off",
"import/no-default-export": "warn",
"import/extensions": "warn",
"react/require-default-props": "warn",
"no-param-reassign": "warn",
"react/jsx-no-bind": "warn",
"no-console": ["warn", { "allow": ["warn", "error"] }]
},
"overrides": [
{
"files": ["server/**/*"],
"rules": {
"import/no-default-export": "off"
}
}
]
}
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dist
node_modules
.github
assets
14 changes: 14 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"useTabs": false,
"trailingComma": "es5",
"endOfLine": "lf",
"printWidth": 100,
"arrowParens": "avoid",
"bracketSpacing": true,
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"embeddedLanguageFormatting": "auto"
}
37 changes: 19 additions & 18 deletions .release-it.js → .release-it.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Config } from 'release-it';

const template = `
{{> header}}

Expand All @@ -18,34 +20,33 @@ const template = `
**Full Changelog**: [{{previousTag}}...{{currentTag}}]({{@root.host}}/{{#if @root.owner}}{{@root.owner}}/{{/if}}{{@root.repository}}/compare/{{previousTag}}...{{currentTag}})
`;

module.exports = {
export default {
git: {
commitMessage: "chore: release v${version}",
requireCleanWorkingDir: false
commitMessage: 'chore: release v${version}',
requireCleanWorkingDir: false,
},
github: {
release: true,
releaseName: "v${version}"
releaseName: 'v${version}',
},
npm: {
publish: false
publish: false,
},
plugins: {
"@release-it/conventional-changelog": {
'@release-it/conventional-changelog': {
writerOpts: {
headerPartial: "## What's Changed\n",
mainTemplate: template
mainTemplate: template,
},
preset: {
name: "conventionalcommits",
name: 'conventionalcommits',
types: [
{ type: "feat", section: "🚀 Features" },
{ type: "fix", section: "👾 Bug Fixes" },
{ type: "docs", section: "📚 Docs" },
{ type: "chore", section: "⚙️ Chore" },
{ type: "style", section: "💅 Style" }
]
}
}
}
};
{ type: 'feat', section: '🚀 Features' },
{ type: 'fix', section: '👾 Bug Fixes' },
{ type: 'chore', section: '⚙️ Chore' },
{ type: 'docs', section: '⚙️ Chore' },
],
},
},
},
} satisfies Config;
9 changes: 9 additions & 0 deletions admin/custom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export {};

declare global {
interface Window {
strapi: {
backendURL: string;
};
}
}
10 changes: 0 additions & 10 deletions admin/jsconfig.json

This file was deleted.

50 changes: 0 additions & 50 deletions admin/src/CKEditorIcon.jsx

This file was deleted.

143 changes: 143 additions & 0 deletions admin/src/components/CKEReact.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import React, { useCallback, useRef, useState } from 'react';
import { useField } from '@strapi/strapi/admin';
import { Flex } from '@strapi/design-system';
import { ClassicEditor } from 'ckeditor5';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import 'ckeditor5/ckeditor5.css';

import { useEditorContext } from './EditorProvider';
import { getStoredToken } from '../utils';
import { MediaLib } from './MediaLib';
import type {
StrapiMediaLibPlugin,
StrapiUploadAdapterConfig,
StrapiUploadAdapterPlugin,
} from '../plugins';

export type WordCountPluginStats = {
words: number;
characters: number;
};

export function CKEReact() {
const [mediaLibVisible, setMediaLibVisible] = useState<boolean>(false);
const [editorInstance, setEditorInstance] = useState<ClassicEditor | null>(null);

const { name, disabled, error, preset, wordsLimit, charsLimit, validateInputLength } =
useEditorContext();
const { onChange: fieldOnChange, value: fieldValue } = useField(name);

const wordCounterRef = useRef<HTMLElement>(null);

const toggleMediaLib = useCallback(() => setMediaLibVisible(prev => !prev), [setMediaLibVisible]);

const handleChangeAssets = useCallback(
(newElems: string): void => {
if (!editorInstance) {
throw new Error('CKEditor: Editor instance is not initialized');
}

const viewFragment = editorInstance.data.processor.toView(newElems);
const modelFragment = editorInstance.data.toModel(viewFragment);
editorInstance?.model.insertContent(modelFragment);

toggleMediaLib();
},
[toggleMediaLib, editorInstance]
);

const onEditorReady = (editor: ClassicEditor): void => {
setUpPlugins(editor);
setEditorInstance(editor);
};

const onEditorChange = (_e: any, editor: ClassicEditor): void => {
const data = editor.getData();
fieldOnChange(name, data);
};

if (!preset) {
return null;
}

return (
<>
<CKEditor
editor={ClassicEditor}
config={preset.editorConfig}
disabled={disabled}
data={fieldValue ?? ''}
onReady={onEditorReady}
onChange={onEditorChange}
/>
<Flex ref={wordCounterRef} color={error ? 'danger600' : 'neutral400'} />
<MediaLib
isOpen={mediaLibVisible}
toggle={toggleMediaLib}
handleChangeAssets={handleChangeAssets}
/>
</>
);

function setUpPlugins(editor: ClassicEditor): void {
const pluginsToSetup: Record<string, (editor: ClassicEditor) => void> = {
WordCount: setUpWordCount,
ImageUploadEditing: setUpImageUploadEditing,
StrapiMediaLib: setUpStrapiMediaLib,
StrapiUploadAdapter: setUpStrapiUploadAdapter,
};

Object.entries(pluginsToSetup).forEach(([pluginName, setUpFn]) => {
if (editor.plugins.has(pluginName)) {
try {
setUpFn(editor);
} catch (err) {
console.error(`CKEditor: Error setting up ${pluginName} plugin `, err);
}
}
});
}

function setUpWordCount(editor: ClassicEditor): void {
const wordCountPlugin = editor.plugins.get('WordCount');

if (wordsLimit || charsLimit) {
wordCountPlugin.on('update', (_e, stats: WordCountPluginStats) => validateInputLength(stats));
}

wordCounterRef.current?.appendChild(wordCountPlugin.wordCountContainer);
}

function setUpImageUploadEditing(editor: ClassicEditor): void {
const imageUploadEditingPlugin = editor.plugins.get('ImageUploadEditing');

const setAltAttribute = (_e: any, { data, imageElement }: any) => {
editor.model.change(writer => {
writer.setAttribute('alt', data.alt, imageElement);
});
};

imageUploadEditingPlugin.on('uploadComplete', setAltAttribute);
}

function setUpStrapiMediaLib(editor: ClassicEditor): void {
const strapiMediaLibPlugin = editor.plugins.get('StrapiMediaLib') as StrapiMediaLibPlugin;
strapiMediaLibPlugin.connect(toggleMediaLib);
}

function setUpStrapiUploadAdapter(editor: ClassicEditor): void {
const StrapiUploadAdapterPlugin = editor.plugins.get(
'StrapiUploadAdapter'
) as StrapiUploadAdapterPlugin;
const token = getStoredToken();
const { backendURL } = window.strapi;
const config: StrapiUploadAdapterConfig = {
uploadUrl: `${backendURL}/upload`,
backendUrl: backendURL,
headers: { Authorization: `Bearer ${token}` },
responsive: window.SH_CKE_UPLOAD_ADAPTER_IS_RESPONSIVE,
};

StrapiUploadAdapterPlugin.initAdapter(config);
}
}
31 changes: 31 additions & 0 deletions admin/src/components/CKEditorIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { styled } from 'styled-components';
import { lightTheme, Flex } from '@strapi/design-system';

export function CKEditorIcon() {
return (
<IconBox justifyContent="center" alignItems="center" width={7} height={6} hasRadius aria-hidden>
<SvgIcon />
</IconBox>
);
}

function SvgIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-8 -7 37 37">
<path
d="M15.04 16.726a3.98 3.98 0 0 0-1.535.524 3.96 3.96 0 0 0-1.402 1.364c-.108.18-.716 1.347-.716 1.347l-2.09 3.82.022.016c.097.063.2.116.308.159.317.113.65.175.987.18 1.264.058 2.529-.016 3.793-.007.725.023 1.45.005 2.172-.053.348-.017.687-.117.99-.29.31-.178.576-.423.78-.717.138-.205.283-.407.409-.62.6-1.02 1.199-2.043 1.794-3.065.187-.321.37-.644.555-.966l.281-.481c.236-.392.367-.838.381-1.294l-3.258.057s-3.147-.012-3.472.026Zm2.764.903Z"
fill={lightTheme.colors.secondary700}
/>
<path
d="m7.12 22.61 1.901-3.477.46-.877c-.31-.168-.614-.35-.918-.528-.866-.508-1.766-.957-2.613-1.498a2.459 2.459 0 0 1-.609-.517c-.27-.336-.341-.736-.362-1.15-.052-1.022-.003-2.045-.02-3.068-.01-.487 0-.975.005-1.462.01-.806.384-1.382 1.069-1.783L8.115 7.03c.55-.322 1.102-.642 1.654-.961.127-.073.263-.13.395-.192.68-.321 1.298-.119 1.9.213.039.02.075.045.112.068.306.149.605.313.895.491.794.445 1.587.893 2.378 1.343.239.139.469.292.688.458.485.36.636.875.666 1.445.039.71.017 1.422.013 2.134-.002.698.01 1.396.003 2.094 1.478-.006 3.146 0 3.146 0l1.807-.032c-.006-.73-.016-1.46-.017-2.19 0-1.31.037-2.62-.039-3.928-.061-1.05-.34-2-1.232-2.666a12.549 12.549 0 0 0-1.264-.848c-1.454-.834-2.91-1.664-4.37-2.49-.545-.308-1.067-.659-1.644-.91-.069-.043-.135-.089-.205-.128-1.106-.613-2.24-.992-3.485-.405-.242.115-.49.218-.723.352-1.011.58-2.02 1.166-3.026 1.757-1.271.744-2.54 1.488-3.81 2.234C.705 5.602.025 6.66.012 8.144c-.008.897-.02 1.794 0 2.691.039 1.884-.045 3.77.058 5.652.042.761.174 1.499.672 2.12.32.377.698.7 1.121.956 1.556 1.001 3.209 1.835 4.8 2.775l.457.271Z"
fill={lightTheme.colors.secondary600}
/>
</svg>
);
}

const IconBox = styled(Flex)`
background-color: ${lightTheme.colors.secondary100};
border: 1px solid ${lightTheme.colors.secondary200};
`;
Loading