mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-03 07:42:03 +00:00
implement generic markdown input form for content
This commit is contained in:
parent
027bf28e2f
commit
7a805f0988
@ -1,19 +1,17 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), { ssr: false });
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const CDN_ENDPOINT = process.env.NEXT_PUBLIC_CDN_ENDPOINT;
|
||||
|
||||
@ -199,9 +197,7 @@ const CombinedResourceForm = () => {
|
||||
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Content</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<MarkdownEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex-col w-full">
|
||||
|
@ -1,19 +1,17 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), { ssr: false });
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const CDN_ENDPOINT = process.env.NEXT_PUBLIC_CDN_ENDPOINT;
|
||||
|
||||
@ -242,9 +240,7 @@ const EditDraftCombinedResourceForm = ({ draft }) => {
|
||||
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Content</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<MarkdownEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex-col w-full">
|
||||
|
@ -1,23 +1,22 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { validateEvent } from '@/utils/nostr';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { validateEvent } from '@/utils/nostr';
|
||||
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
|
||||
import MoreInfo from '@/components/MoreInfo';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), {
|
||||
ssr: false,
|
||||
});
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const EditPublishedCombinedResourceForm = ({ event }) => {
|
||||
const router = useRouter();
|
||||
@ -220,9 +219,7 @@ const EditPublishedCombinedResourceForm = ({ event }) => {
|
||||
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Video Embed</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={videoEmbed} onChange={handleVideoEmbedChange} height={250} />
|
||||
</div>
|
||||
<MarkdownEditor value={videoEmbed} onChange={handleVideoEmbedChange} height={250} />
|
||||
<small className="text-gray-400 mt-2">
|
||||
You can customize your video embed using markdown or HTML. For example, paste iframe
|
||||
embeds from YouTube or Vimeo, or use video tags for direct video files.
|
||||
@ -239,9 +236,7 @@ const EditPublishedCombinedResourceForm = ({ event }) => {
|
||||
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Content</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<MarkdownEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex-col w-full">
|
||||
|
@ -1,21 +1,20 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import { Calendar } from 'primereact/calendar';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), {
|
||||
ssr: false,
|
||||
});
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const EmbeddedDocumentForm = ({ draft = null, isPublished = false, onSave, isPaid }) => {
|
||||
const [title, setTitle] = useState(draft?.title || '');
|
||||
@ -183,9 +182,7 @@ const EmbeddedDocumentForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
</div>
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Content</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<MarkdownEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<div className="mt-8 flex-col w-full">
|
||||
<span className="pl-1 flex items-center">
|
||||
@ -219,7 +216,6 @@ const EmbeddedDocumentForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
<div className="w-full flex flex-row items-end justify-end py-2">
|
||||
<GenericButton icon="pi pi-plus" onClick={addAdditionalLink} />
|
||||
</div>
|
||||
<Tooltip target=".pi-info-circle" />
|
||||
</div>
|
||||
<div className="mt-8 flex-col w-full">
|
||||
{topics.map((topic, index) => (
|
||||
|
@ -8,14 +8,10 @@ import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import dynamic from 'next/dynamic';
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primeicons/primeicons.css';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), {
|
||||
ssr: false,
|
||||
});
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const DocumentForm = () => {
|
||||
const [title, setTitle] = useState('');
|
||||
@ -149,9 +145,11 @@ const DocumentForm = () => {
|
||||
</div>
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Content</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<MarkdownEditor
|
||||
value={content}
|
||||
onChange={handleContentChange}
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-8 flex-col w-full">
|
||||
<span className="pl-1 flex items-center">
|
||||
|
@ -8,10 +8,10 @@ import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import dynamic from 'next/dynamic';
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), { ssr: false });
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const EditDraftDocumentForm = ({ draft }) => {
|
||||
const [title, setTitle] = useState(draft?.title || '');
|
||||
@ -143,9 +143,7 @@ const EditDraftDocumentForm = ({ draft }) => {
|
||||
</div>
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Content</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<MarkdownEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<div className="mt-8 flex-col w-full">
|
||||
<span className="pl-1 flex items-center">
|
||||
|
@ -1,21 +1,21 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { validateEvent } from '@/utils/nostr';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { validateEvent } from '@/utils/nostr';
|
||||
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), { ssr: false });
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const EditPublishedDocumentForm = ({ event }) => {
|
||||
const router = useRouter();
|
||||
@ -198,9 +198,7 @@ const EditPublishedDocumentForm = ({ event }) => {
|
||||
</div>
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Content</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<MarkdownEditor value={content} onChange={handleContentChange} height={350} />
|
||||
</div>
|
||||
<div className="mt-8 flex-col w-full">
|
||||
<span className="pl-1 flex items-center">
|
||||
|
@ -1,23 +1,22 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { validateEvent } from '@/utils/nostr';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { InputNumber } from 'primereact/inputnumber';
|
||||
import { InputSwitch } from 'primereact/inputswitch';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { validateEvent } from '@/utils/nostr';
|
||||
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
|
||||
import MoreInfo from '@/components/MoreInfo';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), {
|
||||
ssr: false,
|
||||
});
|
||||
import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
import MarkdownEditor from '@/components/markdown/MarkdownEditor';
|
||||
|
||||
const EditPublishedVideoForm = ({ event }) => {
|
||||
const router = useRouter();
|
||||
@ -190,9 +189,7 @@ const EditPublishedVideoForm = ({ event }) => {
|
||||
</div>
|
||||
<div className="p-inputgroup flex-1 flex-col mt-4">
|
||||
<span>Video Embed</span>
|
||||
<div data-color-mode="dark">
|
||||
<MDEditor value={videoEmbed} onChange={handleVideoEmbedChange} height={250} />
|
||||
</div>
|
||||
<MarkdownEditor value={videoEmbed} onChange={handleVideoEmbedChange} height={250} />
|
||||
<small className="text-gray-400 mt-2">
|
||||
You can customize your video embed using markdown or HTML. For example, paste iframe
|
||||
embeds from YouTube or Vimeo, or use video tags for direct video files.
|
||||
|
128
src/components/markdown/MarkdownEditor.js
Normal file
128
src/components/markdown/MarkdownEditor.js
Normal file
@ -0,0 +1,128 @@
|
||||
import React from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import '@uiw/react-md-editor/markdown-editor.css';
|
||||
import '@uiw/react-markdown-preview/markdown.css';
|
||||
import 'github-markdown-css/github-markdown-dark.css';
|
||||
|
||||
// Custom theme for MDEditor
|
||||
const mdEditorDarkTheme = {
|
||||
markdown: '#fff',
|
||||
markdownH1: '#fff',
|
||||
markdownH2: '#fff',
|
||||
markdownH3: '#fff',
|
||||
markdownH4: '#fff',
|
||||
markdownH5: '#fff',
|
||||
markdownH6: '#fff',
|
||||
markdownParagraph: '#fff',
|
||||
markdownLink: '#58a6ff',
|
||||
markdownCode: '#fff',
|
||||
markdownList: '#fff',
|
||||
markdownBlockquote: '#fff',
|
||||
markdownTable: '#fff',
|
||||
};
|
||||
|
||||
// Dynamically import MDEditor with custom theming
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor').then(mod => {
|
||||
// Override the module's default theme
|
||||
if (mod.default) {
|
||||
mod.default.Markdown = {
|
||||
...mod.default.Markdown,
|
||||
...mdEditorDarkTheme
|
||||
};
|
||||
}
|
||||
return mod;
|
||||
}), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* A reusable markdown editor component with proper dark mode styling
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {string} props.value - The markdown content
|
||||
* @param {Function} props.onChange - Callback function when content changes
|
||||
* @param {number} props.height - Height of the editor (default: 300)
|
||||
* @param {string} props.placeholder - Placeholder text for the editor
|
||||
* @param {string} props.preview - Preview mode ('edit', 'preview', 'live') (default: 'edit')
|
||||
* @param {string} props.className - Additional class names
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
const MarkdownEditor = ({
|
||||
value,
|
||||
onChange,
|
||||
height = 300,
|
||||
placeholder = "Write your content here...",
|
||||
preview = "edit",
|
||||
className = "",
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<div data-color-mode="dark" className={`w-full ${className}`} style={{ colorScheme: 'dark' }}>
|
||||
<MDEditor
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
height={height}
|
||||
preview={preview}
|
||||
className="md-editor-dark"
|
||||
textareaProps={{
|
||||
placeholder,
|
||||
style: { color: "white" }
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
<style jsx global>{`
|
||||
/* Force all text to white in editor */
|
||||
.w-md-editor * {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/* Reset preview text color */
|
||||
.w-md-editor-preview * {
|
||||
color: #c9d1d9 !important;
|
||||
}
|
||||
|
||||
/* Editor backgrounds */
|
||||
.md-editor-dark {
|
||||
background-color: #0d1117 !important;
|
||||
}
|
||||
|
||||
.w-md-editor-text-input {
|
||||
caret-color: white !important;
|
||||
-webkit-text-fill-color: white !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.w-md-editor-toolbar {
|
||||
background-color: #161b22 !important;
|
||||
border-bottom: 1px solid #30363d !important;
|
||||
}
|
||||
|
||||
/* Preview styling */
|
||||
.w-md-editor-preview {
|
||||
background-color: #0d1117 !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* Make code blocks maintain their styling */
|
||||
.w-md-editor-preview pre {
|
||||
background-color: #1e1e1e !important;
|
||||
color: #d4d4d4 !important;
|
||||
padding: 1em !important;
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.w-md-editor-preview code {
|
||||
font-family: 'Consolas', 'Monaco', 'Andale Mono', 'Ubuntu Mono', monospace !important;
|
||||
color: #d4d4d4 !important;
|
||||
}
|
||||
|
||||
/* Force anything with text-rendering to be white */
|
||||
[style*="text-rendering"] {
|
||||
color: white !important;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarkdownEditor;
|
Loading…
x
Reference in New Issue
Block a user