import { useState, useEffect, useRef } from '@wordpress/element'; import Editor from '@monaco-editor/react'; import { Button } from '@wordpress/components'; import apiFetch from '@wordpress/api-fetch'; import { Notice } from '@wordpress/components'; import has from 'lodash/has'; import { Modal, ModalClose, ModalContent, ModalFooter, ModalHeader, ModalTitle, ModalTrigger, } from '@components/ui/Modal'; import { createTailwind } from '@core/blockbitePurge'; export const StylesEditorModal = () => { const [open, setOpen] = useState(false); const [message, setMessage] = useState(null); const [saving, setSaving] = useState(false); const [defaultValue, setDefaultValue] = useState(''); const [validationError, setValidationError] = useState(null); const editorRef = useRef(null); useEffect(() => { apiFetch({ path: '/blockbite/v1/items/get-item/?handle=blockbite-global-css', }).then((res: any) => { if (has(res, 'status') && res.status === 200 && res.data?.content) { setDefaultValue(res.data.content); } }); }, [open]); function handleEditorDidMount(editor: any, monaco: any) { editorRef.current = editor; // Extend CSS language configuration to allow `@apply` monaco.languages.css.cssDefaults.setOptions({ validate: true, lint: { // Disable unknown at-rules validation to avoid flagging @apply unknownAtRules: 'ignore', }, }); } function handleEditorValidation(markers: any) { if (!markers.length) { setValidationError(null); return; } else { const errorsArr: string[] = []; markers.forEach((marker: any) => { errorsArr.push(marker.message); }); const validationString = errorsArr.join('; '); setValidationError(`Validation error: ${validationString}`); } } async function saveCode() { if (validationError) { setMessage({ status: 'error', value: validationError }); return; } setSaving(true); const editorValue = editorRef.current.getValue(); // Extract all the tailwind classes from @apply directives const tailwindClassesMatches = editorValue.match(/@apply\s+(.*?);/g) || []; const tailwindClasses = tailwindClassesMatches.map((match: any) => { // Extract the classes from the match string return match.replace(/@apply\s+|;/g, '').trim(); }); // Generate the Tailwind CSS const tailwindCss = await createTailwind( `
`, editorValue ); if (!tailwindCss) { setMessage({ status: 'error', value: 'Failed to generate Tailwind styles. Please check your styles for syntax errors.', }); setSaving(false); return; } // Perform the API fetch to save styles apiFetch({ path: '/blockbite/v1/items/upsert-handle', method: 'POST', data: { handle: 'blockbite-global-css', category: 'user-styles', css: tailwindCss, tailwind: tailwindClasses.join(' '), // Join classes into a single string content: editorValue, }, }) .then((res) => { if (has(res, 'status') && res.status === 200) { setMessage({ status: 'success', value: 'Styles saved successfully.', }); // Live update global styles const settings = wp.data.select('core/editor').getEditorSettings(); wp.data.dispatch('core/editor').updateEditorSettings({ ...settings, styles: (settings?.styles || []).map((s: any) => { if (s.source !== 'blockbite-global-css') { return s; } return { ...s, css: tailwindCss, }; }), }); setTimeout(() => { setOpen(false); }, 1000); } else { setMessage({ status: 'error', value: 'Failed to save styles.' }); } }) .catch((error) => { // Handle any errors that occur during the API request console.error('Error saving styles:', error); setMessage({ status: 'error', value: 'An error occurred while saving styles.', }); }) .finally(() => { setSaving(false); }); } return ( Styles Editor

Add custom styles here. This will be applied globally. You can use Tailwind's @apply directive to apply utility classes.

{message && ( { setMessage(''); }} > {message.value} )}
{ setValidationError(null); }} onValidate={handleEditorValidation} />
); };