From bd5f2c110d7b489cf55c6e9a04b7c7e29ee94ac9 Mon Sep 17 00:00:00 2001 From: nshenderov <103522779+nshenderov@users.noreply.github.com> Date: Thu, 5 Dec 2024 19:38:35 +0300 Subject: [PATCH] feat: update word counter display (#192) --- admin/src/components/CKEReact.tsx | 50 ++++++++++++++++++------- admin/src/components/Editor.tsx | 2 +- admin/src/components/EditorProvider.tsx | 41 ++------------------ admin/src/components/Field.tsx | 2 +- 4 files changed, 42 insertions(+), 53 deletions(-) diff --git a/admin/src/components/CKEReact.tsx b/admin/src/components/CKEReact.tsx index 2a6b3e2..9e79d48 100644 --- a/admin/src/components/CKEReact.tsx +++ b/admin/src/components/CKEReact.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useRef, useState } from 'react'; import { useField } from '@strapi/strapi/admin'; import { Flex } from '@strapi/design-system'; +import { styled, css } from 'styled-components'; import { ClassicEditor } from 'ckeditor5'; import { CKEditor } from '@ckeditor/ckeditor5-react'; import 'ckeditor5/ckeditor5.css'; @@ -22,13 +23,24 @@ export type WordCountPluginStats = { export function CKEReact() { const [mediaLibVisible, setMediaLibVisible] = useState(false); const [editorInstance, setEditorInstance] = useState(null); + const [isWordsMax, setIsWordsMax] = useState(false); + const [isCharsMax, setIsCharsMax] = useState(false); - const { name, disabled, error, preset, wordsLimit, charsLimit, validateInputLength } = - useEditorContext(); + const { name, disabled, preset, wordsLimit, charsLimit } = useEditorContext(); const { onChange: fieldOnChange, value: fieldValue } = useField(name); const wordCounterRef = useRef(null); + const onEditorReady = (editor: ClassicEditor): void => { + setUpPlugins(editor); + setEditorInstance(editor); + }; + + const onEditorChange = (_e: any, editor: ClassicEditor): void => { + const data = editor.getData(); + fieldOnChange(name, data); + }; + const toggleMediaLib = useCallback(() => setMediaLibVisible(prev => !prev), [setMediaLibVisible]); const handleChangeAssets = useCallback( @@ -46,16 +58,6 @@ export function CKEReact() { [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; } @@ -70,7 +72,7 @@ export function CKEReact() { onReady={onEditorReady} onChange={onEditorChange} /> - + validateInputLength(stats)); + const { words, characters } = wordCountPlugin; + validateInputLength({ words, characters }); } wordCounterRef.current?.appendChild(wordCountPlugin.wordCountContainer); @@ -137,4 +141,24 @@ export function CKEReact() { StrapiUploadAdapterPlugin.initAdapter(config); } + + function validateInputLength(stats: WordCountPluginStats): void { + if (wordsLimit) { + setIsWordsMax(stats.words > wordsLimit); + } + if (charsLimit) { + setIsCharsMax(stats.characters > charsLimit); + } + } } + +const WordCounter = styled(Flex)<{ $isWordsMax: boolean; $isCharsMax: boolean }>` + ${({ theme, $isWordsMax, $isCharsMax }) => css` + .ck-word-count__words { + color: ${$isWordsMax ? theme.colors.danger600 : theme.colors.neutral400}; + } + .ck-word-count__characters { + color: ${$isCharsMax ? theme.colors.danger600 : theme.colors.neutral400}; + } + `} +`; diff --git a/admin/src/components/Editor.tsx b/admin/src/components/Editor.tsx index 54dcd27..d3f8756 100644 --- a/admin/src/components/Editor.tsx +++ b/admin/src/components/Editor.tsx @@ -11,7 +11,7 @@ export function Editor() { const { name, hint, required, labelAction, label, error, preset } = useEditorContext(); return ( - + {label} {preset ? ( diff --git a/admin/src/components/EditorProvider.tsx b/admin/src/components/EditorProvider.tsx index af2e592..5ba1ad7 100644 --- a/admin/src/components/EditorProvider.tsx +++ b/admin/src/components/EditorProvider.tsx @@ -1,8 +1,7 @@ -import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import type { InputProps } from '@strapi/strapi/admin'; import { type Preset, setUpLanguage, getPluginConfig } from '../config'; -import type { WordCountPluginStats } from './CKEReact'; type EditorProviderBaseProps = Pick< InputProps, @@ -13,16 +12,14 @@ type EditorProviderBaseProps = Pick< wordsLimit?: number; charsLimit?: number; isFieldLocalized: boolean; + error?: string; }; type EditorContextValue = EditorProviderBaseProps & { preset: Preset | null; - error: string | null; - validateInputLength: (stats: WordCountPluginStats) => void; }; type EditorProviderProps = EditorProviderBaseProps & { - fieldError: string | undefined; children: React.ReactElement; }; @@ -37,7 +34,7 @@ export function useEditorContext(): EditorContextValue { export function EditorProvider({ name, disabled, - fieldError, + error, placeholder, hint, label, @@ -50,7 +47,6 @@ export function EditorProvider({ isFieldLocalized, }: EditorProviderProps) { const [preset, setPreset] = useState(null); - const [error, setError] = useState(fieldError ?? null); useEffect(() => { (async () => { @@ -66,35 +62,6 @@ export function EditorProvider({ })(); }, [presetName, placeholder, isFieldLocalized]); - useEffect(() => { - setError(fieldError ?? null); - }, [fieldError]); - - const validateInputLength = useCallback( - (stats: WordCountPluginStats): void => { - const maxWordsErrMsg = 'Max words limit is exceeded'; - const maxCharsErrMsg = 'Max characters limit is exceeded'; - - setError(prevErr => { - const isWordLimitExceeded = wordsLimit && stats.words > wordsLimit; - const isCharLimitExceeded = charsLimit && stats.characters > charsLimit; - const isErrSet = prevErr && (prevErr === maxWordsErrMsg || prevErr === maxCharsErrMsg); - - if (isWordLimitExceeded) { - return maxWordsErrMsg; - } - if (isCharLimitExceeded) { - return maxCharsErrMsg; - } - if (isErrSet) { - return null; - } - return prevErr; - }); - }, - [wordsLimit, charsLimit] - ); - const EditorContextValue = useMemo( () => ({ name, @@ -109,7 +76,6 @@ export function EditorProvider({ error, wordsLimit, charsLimit, - validateInputLength, isFieldLocalized, }), [ @@ -125,7 +91,6 @@ export function EditorProvider({ charsLimit, preset, error, - validateInputLength, isFieldLocalized, ] ); diff --git a/admin/src/components/Field.tsx b/admin/src/components/Field.tsx index 007933c..b17965a 100644 --- a/admin/src/components/Field.tsx +++ b/admin/src/components/Field.tsx @@ -40,7 +40,7 @@ function Field({ return (