Feature: Add TipTap editor package
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@crux-kit/editor-tiptap": "*",
|
||||
"@crux-kit/hooks": "*",
|
||||
"@crux-kit/intl-date": "*",
|
||||
"@crux-kit/intl-name": "*",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
@plugin "@tailwindcss/typography";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
/* @crux-kit/editor-tiptap — Tailwind class scanning + editor content styles */
|
||||
@source '../../../../../packages/design-system/editor-tiptap/src';
|
||||
@import '@crux-kit/editor-tiptap/styles.css';
|
||||
|
||||
/* ReUI Configs */
|
||||
@import './config.reui.css';
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useForm } from 'react-hook-form';
|
||||
import SandboxShowcase from '#devtools/view/components/SandboxShowcase';
|
||||
import { FormElement, FormGrid, GridElement, InputMCEEditor } from '#shared/forms/composed';
|
||||
import { FormElement, FormGrid, GridElement, InputMCEEditor, InputTiptapEditor } from '#shared/forms/composed';
|
||||
import { Form as FormProvider } from '#shared/ui/form';
|
||||
import template from '../lib/templates/richTextField';
|
||||
import FormActions from './FormActions';
|
||||
|
||||
export default function RichTextFieldSection() {
|
||||
const form = useForm({ defaultValues: { content: '' } });
|
||||
const form = useForm({ defaultValues: { content: '', contentLight: '' } });
|
||||
|
||||
return (
|
||||
<SandboxShowcase
|
||||
@@ -21,11 +21,19 @@ export default function RichTextFieldSection() {
|
||||
<GridElement colSpan={12}>
|
||||
<InputMCEEditor
|
||||
name="content"
|
||||
label="Rich text editor"
|
||||
label="Rich text editor (TinyMCE)"
|
||||
placeholder="Write formatted content…"
|
||||
description="Full rich-text editing with the shared TinyMCE configuration."
|
||||
/>
|
||||
</GridElement>
|
||||
<GridElement colSpan={12}>
|
||||
<InputTiptapEditor
|
||||
name="contentLight"
|
||||
label="Rich text editor (TipTap · light preset)"
|
||||
placeholder="Write formatted content…"
|
||||
description="Free, headless editor from @crux-kit/editor-tiptap. HTML in / HTML out."
|
||||
/>
|
||||
</GridElement>
|
||||
<GridElement colSpan={12}>
|
||||
<FormActions form={form} />
|
||||
</GridElement>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { RichTextEditor, type EditorPreset } from '@crux-kit/editor-tiptap';
|
||||
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '#shared/ui/form';
|
||||
|
||||
/**
|
||||
* Free rich-text field backed by @crux-kit/editor-tiptap (TipTap). Same
|
||||
* value/onChange (HTML string) seam as InputMCEEditor, so it is a drop-in
|
||||
* replacement.
|
||||
*/
|
||||
export const InputTiptapEditor: React.FC<{
|
||||
name: string;
|
||||
label?: string;
|
||||
description?: React.ReactNode;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
preset?: EditorPreset;
|
||||
}> = ({
|
||||
name,
|
||||
description,
|
||||
label,
|
||||
placeholder,
|
||||
required = false,
|
||||
disabled = false,
|
||||
preset = 'light'
|
||||
}) => {
|
||||
const form = useFormContext();
|
||||
|
||||
return (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem>
|
||||
{label && (
|
||||
<FormLabel>
|
||||
{label}
|
||||
{required && <span className="ml-1 text-red-500">*</span>}
|
||||
</FormLabel>
|
||||
)}
|
||||
<FormControl>
|
||||
<RichTextEditor
|
||||
value={field.value ?? ''}
|
||||
onChange={field.onChange}
|
||||
disabled={field.disabled}
|
||||
placeholder={placeholder}
|
||||
preset={preset}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage>{description}</FormMessage>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -30,3 +30,4 @@ export * from './fields/InputDate';
|
||||
|
||||
/* Text editor fields */
|
||||
export * from './fields/InputMCEEditor';
|
||||
export * from './fields/InputTiptapEditor';
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '#shared/ui/Accordion';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '#shared/ui/Card';
|
||||
|
||||
export interface FaqItem {
|
||||
title: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export type FaqItems = Array<FaqItem>;
|
||||
|
||||
export function Faq() {
|
||||
const items: FaqItems = [
|
||||
{
|
||||
title: 'How is pricing determined for each plan ?',
|
||||
text: "Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision. Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision. Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision"
|
||||
},
|
||||
{
|
||||
title: 'What payment methods are accepted for subscriptions ?',
|
||||
text: "Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision"
|
||||
},
|
||||
{
|
||||
title: 'Are there any hidden fees in the pricing ?',
|
||||
text: "Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision"
|
||||
},
|
||||
{
|
||||
title: 'Is there a discount for annual subscriptions ?',
|
||||
text: "Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision"
|
||||
},
|
||||
{
|
||||
title: 'Do you offer refunds on subscription cancellations ?',
|
||||
text: "Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision"
|
||||
},
|
||||
{
|
||||
title: 'Can I add extra features to my current plan ?',
|
||||
text: "Metronic embraces flexible licensing options that empower you to choose the perfect fit for your project's needs and budget. Understanding the factors influencing each plan's pricing helps you make an informed decision"
|
||||
}
|
||||
];
|
||||
|
||||
const generateItems = () => {
|
||||
return (
|
||||
<Accordion type="single" collapsible>
|
||||
{items.map((item, index) => (
|
||||
<AccordionItem key={index} value={`item-${index}`}>
|
||||
<AccordionTrigger>{item.title}</AccordionTrigger>
|
||||
<AccordionContent>{item.text}</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>FAQ</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="py-3">{generateItems()}</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -30,6 +30,12 @@ export default defineConfig(({ mode }) => {
|
||||
new URL('../../packages/design-system/keenicon/src', import.meta.url)
|
||||
)
|
||||
},
|
||||
{
|
||||
find: '@crux-kit/editor-tiptap',
|
||||
replacement: fileURLToPath(
|
||||
new URL('../../packages/design-system/editor-tiptap/src', import.meta.url)
|
||||
)
|
||||
},
|
||||
{
|
||||
find: '@crux-kit/hooks',
|
||||
replacement: fileURLToPath(new URL('../../packages/hooks/src', import.meta.url))
|
||||
|
||||
Reference in New Issue
Block a user