feat: implement FieldRichTextEditor component using TipTap and Mantine integration
This commit is contained in:
+21
-1
@@ -6,7 +6,8 @@ import {
|
||||
FieldMultiSelect, FieldNativeSelect, FieldTagsInput, FieldCheckbox,
|
||||
FieldRadioGroup, FieldSwitch, FieldChipGroup, FieldSegmentedControl,
|
||||
FieldSlider, FieldRangeSlider, FieldRating, FieldColorInput,
|
||||
FieldColorPicker, FieldFileInput, FieldLocalSelect, FieldAsyncSelect
|
||||
FieldColorPicker, FieldFileInput, FieldLocalSelect, FieldAsyncSelect,
|
||||
FieldRichTextEditor
|
||||
} from '@repo/ui/form';
|
||||
import type { LoadOptionsFn } from '@repo/ui/form';
|
||||
import { useFormDemoTranslation } from '../i18n/useFormDemoTranslation';
|
||||
@@ -91,6 +92,8 @@ export default function AllFieldsDemo() {
|
||||
{ id: 888, code: 'ASYNC-88', name: 'Ghost Async Vendor 1' },
|
||||
{ id: 999, code: 'ASYNC-99', name: 'Ghost Async Vendor 2' }
|
||||
],
|
||||
richTextEmpty: "",
|
||||
richTextPrefilled: "<h2 style=\"text-align: center\">ERP Release Notes</h2><p>This is a <b>highly important</b> update. Please observe the following:</p><ul><li>System maintenance at <i>midnight</i>.</li><li><u style=\"text-align: justify\">All users must log out.</u></li></ul><p style=\"text-align: justify\">Thank you for your cooperation.</p>",
|
||||
realPokeSelect: null,
|
||||
multiRealPokeSelect: []
|
||||
}
|
||||
@@ -305,6 +308,23 @@ export default function AllFieldsDemo() {
|
||||
clearable
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<Title order={5} mb="sm" mt="lg" c="brand">Rich Text Editor (TipTap)</Title>
|
||||
<Divider mb="md" />
|
||||
<FieldRichTextEditor
|
||||
name="richTextEmpty"
|
||||
control={control}
|
||||
label="Rich Text (Empty)"
|
||||
description="A fresh TipTap editor instance"
|
||||
/>
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<FieldRichTextEditor
|
||||
name="richTextPrefilled"
|
||||
control={control}
|
||||
label="Rich Text (Prefilled / Edit Mode)"
|
||||
description="HTML string successfully loaded from default values"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* --- Toggles & Choices --- */}
|
||||
|
||||
+24
-2
@@ -1,8 +1,8 @@
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { Button, Paper, Title, Divider, Stack, Code, Alert } from '@repo/ui/components';
|
||||
import { FieldTextInput, FieldSelect, FieldSwitch, FieldLocalSelect, FieldAsyncSelect } from '@repo/ui/form';
|
||||
import { Button, Paper, Title, Divider, Stack, Code, Alert, TypographyStylesProvider } from '@repo/ui/components';
|
||||
import { FieldTextInput, FieldSelect, FieldSwitch, FieldLocalSelect, FieldAsyncSelect, FieldRichTextEditor } from '@repo/ui/form';
|
||||
import { useConditionalField } from '@repo/ui/hooks';
|
||||
import { compose, required, emailValidator } from '@repo/ui/validators';
|
||||
import { useFormDemoTranslation } from '../i18n/useFormDemoTranslation';
|
||||
@@ -67,6 +67,7 @@ export default function ReactiveWatchDemo() {
|
||||
role: z.string().optional(),
|
||||
regions: z.array(z.object({ id: z.string(), code: z.string(), taxRate: z.number() })).optional(),
|
||||
warehouses: z.array(z.object({ id: z.string(), regionId: z.string(), name: z.string() })).optional(),
|
||||
richTextLive: z.string().optional(),
|
||||
})
|
||||
.and(
|
||||
z.discriminatedUnion('userType', [
|
||||
@@ -109,6 +110,7 @@ export default function ReactiveWatchDemo() {
|
||||
{ id: 'W-99', regionId: 'R1', name: 'APAC Central Hub' },
|
||||
{ id: 'W-98', regionId: 'R1', name: 'APAC Backup Hub' }
|
||||
],
|
||||
richTextLive: "<p>Start typing to see the live preview...</p>",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -119,6 +121,7 @@ export default function ReactiveWatchDemo() {
|
||||
const department = useWatch({ control, name: 'department' });
|
||||
const role = useWatch({ control, name: 'role' });
|
||||
const regions = useWatch({ control, name: 'regions' });
|
||||
const watchedRichTextLive = useWatch({ control, name: 'richTextLive' });
|
||||
|
||||
// Use the custom hook to cleanly unregister and reset fields when hidden
|
||||
useConditionalField({
|
||||
@@ -328,6 +331,25 @@ export default function ReactiveWatchDemo() {
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Title order={5} mb="sm" c="brand" mt="lg">
|
||||
Reactive Rich Text Preview
|
||||
</Title>
|
||||
<Divider mb="sm" />
|
||||
|
||||
<FieldRichTextEditor
|
||||
name="richTextLive"
|
||||
control={control as any}
|
||||
label="Live Editor"
|
||||
description="Type to see instantaneous reactive rendering below"
|
||||
/>
|
||||
|
||||
<Paper p="md" withBorder radius="md" mt="sm">
|
||||
<Title order={6} mb="xs">Live HTML Preview Render</Title>
|
||||
<TypographyStylesProvider>
|
||||
<div dangerouslySetInnerHTML={{ __html: watchedRichTextLive }} />
|
||||
</TypographyStylesProvider>
|
||||
</Paper>
|
||||
|
||||
<Button type="submit" mt="md">
|
||||
{t.common?.submit || 'Submit Reactive Form'}
|
||||
</Button>
|
||||
|
||||
+14
-1
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
||||
import { Button, Paper, Title, Group, Stack, Code, Divider } from '@repo/ui/components';
|
||||
import {
|
||||
FieldTextInput, FieldPasswordInput, FieldNumberInput,
|
||||
FieldLocalSelect, FieldAsyncSelect
|
||||
FieldLocalSelect, FieldAsyncSelect, FieldRichTextEditor
|
||||
} from '@repo/ui/form';
|
||||
import type { LoadOptionsFn } from '@repo/ui/form';
|
||||
|
||||
@@ -75,6 +75,7 @@ export default function ValidationBankDemo() {
|
||||
prefilledVendor: z.object({ id: z.union([z.string(), z.number()]), code: z.string(), name: z.string() }, { required_error: 'Vendor is required' }),
|
||||
emptyVendor: z.object({ id: z.union([z.string(), z.number()]), code: z.string(), name: z.string() }, { required_error: 'Vendor is required' }),
|
||||
prefilledAsyncMulti: z.array(z.object({ id: z.union([z.string(), z.number()]), code: z.string(), name: z.string() })).min(1, "Select at least 1 vendor"),
|
||||
richTextNotes: z.string().min(15, "Notes must be at least 15 characters long (including HTML tags)"),
|
||||
});
|
||||
|
||||
type ValidationFormValues = z.infer<typeof validationSchema>;
|
||||
@@ -96,6 +97,7 @@ export default function ValidationBankDemo() {
|
||||
{ id: 'V1', code: 'VN-01', name: 'Vendor One' },
|
||||
{ id: 'V2', code: 'VN-02', name: 'Vendor Two' }
|
||||
] as any,
|
||||
richTextNotes: '',
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,6 +228,17 @@ export default function ValidationBankDemo() {
|
||||
withAsterisk
|
||||
/>
|
||||
|
||||
<Title order={5} c="brand" mt="lg">Rich Text Editor Validations</Title>
|
||||
<Divider mb="sm" />
|
||||
|
||||
<FieldRichTextEditor
|
||||
name="richTextNotes"
|
||||
control={control as any}
|
||||
label="Important Notes"
|
||||
description="This uses Zod minimum length string validation"
|
||||
withAsterisk
|
||||
/>
|
||||
|
||||
<Button type="submit" mt="md">{t.common.submit}</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user