fix(richtext-lexical): unify indent between different converters and make paragraphs and lists match without CSS (#13274)
Previously, the Lexical editor was using px, and the JSX converter was using rem. #12848 fixed the inconsistency by changing the editor to rem, but it should have been the other way around, changing the JSX converter to px. You can see the latest explanation about why it should be 40px [here](https://github.com/payloadcms/payload/issues/13130#issuecomment-3058348085). In short, that's the default indentation all browsers use for lists. This time I'm making sure to leave clear comments everywhere and a test to avoid another regression. Here is an image of what the e2e test looks like: <img width="321" height="678" alt="image" src="https://github.com/user-attachments/assets/8880c7cb-a954-4487-8377-aee17c06754c" /> The first part is the Lexical editor, the second is the JSX converter. As you can see, the checkbox in JSX looks a little odd because it uses an input checkbox (as opposed to a pseudo-element in the Lexical editor). I thought about adding an inline style to move it slightly to the left, but I found that browsers don't have a standard size for the checkbox; it varies by browser and device. That requires a little more thought; I'll address that in a future PR. Fixes #13130
This commit is contained in:
@@ -53,7 +53,11 @@ export const BlockquoteFeature = createServerFeature({
|
||||
})
|
||||
const style = [
|
||||
node.format ? `text-align: ${node.format};` : '',
|
||||
node.indent > 0 ? `padding-inline-start: ${Number(node.indent) * 2}rem;` : '',
|
||||
// the unit should be px. Do not change it to rem, em, or something else.
|
||||
// The quantity should be 40px. Do not change it either.
|
||||
// See rationale in
|
||||
// https://github.com/payloadcms/payload/issues/13130#issuecomment-3058348085
|
||||
node.indent > 0 ? `padding-inline-start: ${node.indent * 40}px;` : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
@@ -83,7 +83,11 @@ export function findConverterForNode<
|
||||
|
||||
if (!disableIndent && (!Array.isArray(disableIndent) || !disableIndent?.includes(node.type))) {
|
||||
if ('indent' in node && node.indent && node.type !== 'listitem') {
|
||||
style['padding-inline-start'] = `${Number(node.indent) * 2}rem`
|
||||
// the unit should be px. Do not change it to rem, em, or something else.
|
||||
// The quantity should be 40px. Do not change it either.
|
||||
// See rationale in
|
||||
// https://github.com/payloadcms/payload/issues/13130#issuecomment-3058348085
|
||||
style['padding-inline-start'] = `${Number(node.indent) * 40}px`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,11 @@ export const ParagraphHTMLConverter: HTMLConverter<SerializedParagraphNode> = {
|
||||
})
|
||||
const style = [
|
||||
node.format ? `text-align: ${node.format};` : '',
|
||||
node.indent > 0 ? `padding-inline-start: ${Number(node.indent) * 2}rem;` : '',
|
||||
// the unit should be px. Do not change it to rem, em, or something else.
|
||||
// The quantity should be 40px. Do not change it either.
|
||||
// See rationale in
|
||||
// https://github.com/payloadcms/payload/issues/13130#issuecomment-3058348085
|
||||
node.indent > 0 ? `padding-inline-start: ${node.indent * 40}px;` : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
@@ -139,7 +139,11 @@ export function convertLexicalNodesToJSX({
|
||||
(!Array.isArray(disableIndent) || !disableIndent?.includes(node.type))
|
||||
) {
|
||||
if ('indent' in node && node.indent && node.type !== 'listitem') {
|
||||
style.paddingInlineStart = `${Number(node.indent) * 2}em`
|
||||
// the unit should be px. Do not change it to rem, em, or something else.
|
||||
// The quantity should be 40px. Do not change it either.
|
||||
// See rationale in
|
||||
// https://github.com/payloadcms/payload/issues/13130#issuecomment-3058348085
|
||||
style.paddingInlineStart = `${Number(node.indent) * 40}px`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react'
|
||||
|
||||
// eslint-disable-next-line payload/no-imports-from-exports-dir
|
||||
import { defaultJSXConverters, RichText } from '../../../../../exports/react/index.js'
|
||||
import './style.scss'
|
||||
|
||||
export function RichTextPlugin() {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
@@ -16,5 +17,9 @@ export function RichTextPlugin() {
|
||||
})
|
||||
}, [editor])
|
||||
|
||||
return <RichText converters={defaultJSXConverters} data={editorState} />
|
||||
return (
|
||||
<div className="debug-jsx-converter">
|
||||
<RichText converters={defaultJSXConverters} data={editorState} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.debug-jsx-converter {
|
||||
// this is to match the editor component, and be able to compare aligned styles
|
||||
padding-left: 36px;
|
||||
|
||||
// We revert to the browser defaults (user-agent), because we want to see
|
||||
// the indentations look good without the need for CSS.
|
||||
ul,
|
||||
ol {
|
||||
padding-left: revert;
|
||||
margin: revert;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,11 @@ export const HeadingFeature = createServerFeature<
|
||||
})
|
||||
const style = [
|
||||
node.format ? `text-align: ${node.format};` : '',
|
||||
node.indent > 0 ? `padding-inline-start: ${Number(node.indent) * 2}rem;` : '',
|
||||
// the unit should be px. Do not change it to rem, em, or something else.
|
||||
// The quantity should be 40px. Do not change it either.
|
||||
// See rationale in
|
||||
// https://github.com/payloadcms/payload/issues/13130#issuecomment-3058348085
|
||||
node.indent > 0 ? `padding-inline-start: ${node.indent * 40}px;` : '',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from './collections/Lexical/index.js'
|
||||
import { LexicalAccessControl } from './collections/LexicalAccessControl/index.js'
|
||||
import { LexicalInBlock } from './collections/LexicalInBlock/index.js'
|
||||
import { LexicalJSXConverter } from './collections/LexicalJSXConverter/index.js'
|
||||
import { LexicalLinkFeature } from './collections/LexicalLinkFeature/index.js'
|
||||
import { LexicalLocalizedFields } from './collections/LexicalLocalized/index.js'
|
||||
import { LexicalMigrateFields } from './collections/LexicalMigrate/index.js'
|
||||
@@ -30,6 +31,7 @@ export const baseConfig: Partial<Config> = {
|
||||
collections: [
|
||||
LexicalFullyFeatured,
|
||||
LexicalLinkFeature,
|
||||
LexicalJSXConverter,
|
||||
getLexicalFieldsCollection({
|
||||
blocks: lexicalBlocks,
|
||||
inlineBlocks: lexicalInlineBlocks,
|
||||
|
||||
@@ -1296,24 +1296,23 @@ describe('lexicalMain', () => {
|
||||
await page.getByLabel('Title*').fill('Indent and Text-align')
|
||||
await page.getByRole('paragraph').nth(1).click()
|
||||
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
|
||||
const getHTMLContent: (indentToSize: (indent: number) => string) => string = (indentToSize) =>
|
||||
`<p style='text-align: center;'>paragraph centered</p><h1 style='text-align: right;'>Heading right</h1><p>paragraph without indent</p><p style='padding-inline-start: ${indentToSize(1)};'>paragraph indent 1</p><h2 style='padding-inline-start: ${indentToSize(2)};'>heading indent 2</h2><blockquote style='padding-inline-start: ${indentToSize(3)};'>quote indent 3</blockquote>`
|
||||
const htmlContent = `<p style='text-align: center;'>paragraph centered</p><h1 style='text-align: right;'>Heading right</h1><p>paragraph without indent</p><p style='padding-inline-start: 40px;'>paragraph indent 1</p><h2 style='padding-inline-start: 80px;'>heading indent 2</h2><blockquote style='padding-inline-start: 120px;'>quote indent 3</blockquote>`
|
||||
await page.evaluate(
|
||||
async ([htmlContent]) => {
|
||||
const blob = new Blob([htmlContent as string], { type: 'text/html' })
|
||||
const blob = new Blob([htmlContent], { type: 'text/html' })
|
||||
const clipboardItem = new ClipboardItem({ 'text/html': blob })
|
||||
await navigator.clipboard.write([clipboardItem])
|
||||
},
|
||||
[getHTMLContent((indent: number) => `${indent * 40}px`)],
|
||||
[htmlContent],
|
||||
)
|
||||
// eslint-disable-next-line playwright/no-conditional-in-test
|
||||
const pasteKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||
await page.keyboard.press(`${pasteKey}+v`)
|
||||
await page.locator('#field-richText').click()
|
||||
await page.locator('#field-richText').fill('asd')
|
||||
await saveDocAndAssert(page)
|
||||
await page.getByRole('button', { name: 'Save' }).click()
|
||||
await page.getByRole('link', { name: 'API' }).click()
|
||||
const htmlOutput = page.getByText(getHTMLContent((indent: number) => `${indent * 2}rem`))
|
||||
const htmlOutput = page.getByText(htmlContent)
|
||||
await expect(htmlOutput).toBeVisible()
|
||||
})
|
||||
|
||||
|
||||
117
test/lexical/collections/LexicalJSXConverter/e2e.spec.ts
Normal file
117
test/lexical/collections/LexicalJSXConverter/e2e.spec.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
import { AdminUrlUtil } from 'helpers/adminUrlUtil.js'
|
||||
import { reInitializeDB } from 'helpers/reInitializeDB.js'
|
||||
import { lexicalJSXConverterSlug } from 'lexical/slugs.js'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { ensureCompilationIsDone } from '../../../helpers.js'
|
||||
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
|
||||
import { LexicalHelpers } from '../utils.js'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const currentFolder = path.dirname(filename)
|
||||
const dirname = path.resolve(currentFolder, '../../')
|
||||
|
||||
const { beforeAll, beforeEach, describe } = test
|
||||
|
||||
// Unlike other suites, this one runs in parallel, as they run on the `/create` URL and are "pure" tests
|
||||
test.describe.configure({ mode: 'parallel' })
|
||||
|
||||
const { serverURL } = await initPayloadE2ENoConfig({
|
||||
dirname,
|
||||
})
|
||||
|
||||
describe('Lexical JSX Converter', () => {
|
||||
beforeAll(async ({ browser }, testInfo) => {
|
||||
testInfo.setTimeout(TEST_TIMEOUT_LONG)
|
||||
process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit
|
||||
const page = await browser.newPage()
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
await page.close()
|
||||
})
|
||||
beforeEach(async ({ page }) => {
|
||||
await reInitializeDB({
|
||||
serverURL,
|
||||
snapshotKey: 'lexicalTest',
|
||||
uploadsDir: [path.resolve(dirname, './collections/Upload/uploads')],
|
||||
})
|
||||
const url = new AdminUrlUtil(serverURL, lexicalJSXConverterSlug)
|
||||
const lexical = new LexicalHelpers(page)
|
||||
await page.goto(url.create)
|
||||
await lexical.editor.first().focus()
|
||||
})
|
||||
|
||||
// See rationale in https://github.com/payloadcms/payload/issues/13130#issuecomment-3058348085
|
||||
test('indents should be 40px in the editor and in the jsx converter', async ({ page }) => {
|
||||
const lexical = new LexicalHelpers(page)
|
||||
// 40px
|
||||
await lexical.addLine('ordered', 'HelloA0', 1, false)
|
||||
await lexical.addLine('paragraph', 'HelloA1', 1)
|
||||
await lexical.addLine('unordered', 'HelloA2', 1)
|
||||
await lexical.addLine('h1', 'HelloA3', 1)
|
||||
await lexical.addLine('check', 'HelloA4', 1)
|
||||
|
||||
// 80px
|
||||
await lexical.addLine('ordered', 'HelloB0', 2)
|
||||
await lexical.addLine('paragraph', 'HelloB1', 2)
|
||||
await lexical.addLine('unordered', 'HelloB2', 2)
|
||||
await lexical.addLine('h1', 'HelloB3', 2)
|
||||
await lexical.addLine('check', 'HelloB4', 2)
|
||||
|
||||
const [offsetA0_ed, offsetA0_jsx] = await getIndentOffset(page, 'HelloA0')
|
||||
const [offsetA1_ed, offsetA1_jsx] = await getIndentOffset(page, 'HelloA1')
|
||||
const [offsetA2_ed, offsetA2_jsx] = await getIndentOffset(page, 'HelloA2')
|
||||
const [offsetA3_ed, offsetA3_jsx] = await getIndentOffset(page, 'HelloA3')
|
||||
const [offsetA4_ed, offsetA4_jsx] = await getIndentOffset(page, 'HelloA4')
|
||||
|
||||
const [offsetB0_ed, offsetB0_jsx] = await getIndentOffset(page, 'HelloB0')
|
||||
const [offsetB1_ed, offsetB1_jsx] = await getIndentOffset(page, 'HelloB1')
|
||||
const [offsetB2_ed, offsetB2_jsx] = await getIndentOffset(page, 'HelloB2')
|
||||
const [offsetB3_ed, offsetB3_jsx] = await getIndentOffset(page, 'HelloB3')
|
||||
const [offsetB4_ed, offsetB4_jsx] = await getIndentOffset(page, 'HelloB4')
|
||||
|
||||
await expect(() => {
|
||||
expect(offsetA0_ed).toBe(offsetB0_ed - 40)
|
||||
expect(offsetA1_ed).toBe(offsetB1_ed - 40)
|
||||
expect(offsetA2_ed).toBe(offsetB2_ed - 40)
|
||||
expect(offsetA3_ed).toBe(offsetB3_ed - 40)
|
||||
expect(offsetA4_ed).toBe(offsetB4_ed - 40)
|
||||
expect(offsetA0_jsx).toBe(offsetB0_jsx - 40)
|
||||
expect(offsetA1_jsx).toBe(offsetB1_jsx - 40)
|
||||
expect(offsetA2_jsx).toBe(offsetB2_jsx - 40)
|
||||
expect(offsetA3_jsx).toBe(offsetB3_jsx - 40)
|
||||
// TODO: Checklist item in JSX needs more thought
|
||||
// expect(offsetA4_jsx).toBe(offsetB4_jsx - 40)
|
||||
}).toPass()
|
||||
|
||||
// HTML in JSX converter should contain as few inline styles as possible
|
||||
await expect(async () => {
|
||||
const innerHTML = await page.locator('.payload-richtext').innerHTML()
|
||||
const normalized = normalizeCheckboxUUIDs(innerHTML)
|
||||
expect(normalized).toBe(
|
||||
`<ol class="list-number"><li class="" value="1">HelloA0</li></ol><p style="padding-inline-start: 40px;">HelloA1</p><ul class="list-bullet"><li class="" value="1">HelloA2</li></ul><h1 style="padding-inline-start: 40px;">HelloA3</h1><ol class="list-number"><li class="" value="1">HelloA4</li><li class="nestedListItem" value="2" style="list-style-type: none;"><ol class="list-number"><li class="" value="1">HelloB0</li></ol></li></ol><p style="padding-inline-start: 80px;">HelloB1</p><ul class="list-bullet"><li class="nestedListItem" value="1" style="list-style-type: none;"><ul class="list-bullet"><li class="" value="1">HelloB2</li></ul></li></ul><h1 style="padding-inline-start: 80px;">HelloB3</h1><ul class="list-check"><li aria-checked="false" class="list-item-checkbox list-item-checkbox-unchecked nestedListItem" role="checkbox" tabindex="-1" value="1" style="list-style-type: none;"><ul class="list-check"><li aria-checked="false" class="list-item-checkbox list-item-checkbox-unchecked" role="checkbox" tabindex="-1" value="1" style="list-style-type: none;"><input id="DETERMINISTIC_UUID" readonly="" type="checkbox"><label for="DETERMINISTIC_UUID">HelloB4</label><br></li></ul></li></ul>`,
|
||||
)
|
||||
}).toPass()
|
||||
})
|
||||
})
|
||||
|
||||
async function getIndentOffset(page: Page, text: string): Promise<[number, number]> {
|
||||
const textElement = page.getByText(text)
|
||||
await expect(textElement).toHaveCount(2)
|
||||
const startLeft = (locator: Locator) =>
|
||||
locator.evaluate((el) => {
|
||||
const leftRect = el.getBoundingClientRect().left
|
||||
const paddingLeft = getComputedStyle(el).paddingLeft
|
||||
return leftRect + parseFloat(paddingLeft)
|
||||
})
|
||||
return [await startLeft(textElement.first()), await startLeft(textElement.last())]
|
||||
}
|
||||
|
||||
function normalizeCheckboxUUIDs(html: string): string {
|
||||
return html
|
||||
.replace(/id="[a-f0-9-]{36}"/g, 'id="DETERMINISTIC_UUID"')
|
||||
.replace(/for="[a-f0-9-]{36}"/g, 'for="DETERMINISTIC_UUID"')
|
||||
}
|
||||
18
test/lexical/collections/LexicalJSXConverter/index.ts
Normal file
18
test/lexical/collections/LexicalJSXConverter/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { DebugJsxConverterFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
import { lexicalJSXConverterSlug } from '../../slugs.js'
|
||||
|
||||
export const LexicalJSXConverter: CollectionConfig = {
|
||||
slug: lexicalJSXConverterSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [...defaultFeatures, DebugJsxConverterFeature()],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'url'
|
||||
import { ensureCompilationIsDone } from '../../../helpers.js'
|
||||
import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../../../playwright.config.js'
|
||||
import { LexicalHelpers } from './utils.js'
|
||||
import { LexicalHelpers } from '../utils.js'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const currentFolder = path.dirname(filename)
|
||||
const dirname = path.resolve(currentFolder, '../../')
|
||||
|
||||
@@ -8,6 +8,27 @@ export class LexicalHelpers {
|
||||
this.page = page
|
||||
}
|
||||
|
||||
async addLine(
|
||||
type: 'check' | 'h1' | 'h2' | 'ordered' | 'paragraph' | 'unordered',
|
||||
text: string,
|
||||
indent: number,
|
||||
startWithEnter = true,
|
||||
) {
|
||||
if (startWithEnter) {
|
||||
await this.page.keyboard.press('Enter')
|
||||
}
|
||||
await this.slashCommand(type)
|
||||
// Outdent 10 times to be sure we are at the beginning of the line
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await this.page.keyboard.press('Shift+Tab')
|
||||
}
|
||||
const adjustedIndent = ['check', 'ordered', 'unordered'].includes(type) ? indent - 1 : indent
|
||||
for (let i = 0; i < adjustedIndent; i++) {
|
||||
await this.page.keyboard.press('Tab')
|
||||
}
|
||||
await this.page.keyboard.type(text)
|
||||
}
|
||||
|
||||
async save(container: 'document' | 'drawer') {
|
||||
if (container === 'drawer') {
|
||||
await this.drawer.getByText('Save').click()
|
||||
@@ -19,7 +40,7 @@ export class LexicalHelpers {
|
||||
|
||||
async slashCommand(
|
||||
// prettier-ignore
|
||||
command: 'block' | 'check' | 'code' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' |'h6' | 'inline'
|
||||
command: 'block' | 'check' | 'code' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' |'h6' | 'inline'
|
||||
| 'link' | 'ordered' | 'paragraph' | 'quote' | 'relationship' | 'unordered' | 'upload',
|
||||
) {
|
||||
await this.page.keyboard.press(`/`)
|
||||
@@ -85,6 +85,7 @@ export interface Config {
|
||||
collections: {
|
||||
'lexical-fully-featured': LexicalFullyFeatured;
|
||||
'lexical-link-feature': LexicalLinkFeature;
|
||||
'lexical-jsx-converter': LexicalJsxConverter;
|
||||
'lexical-fields': LexicalField;
|
||||
'lexical-migrate-fields': LexicalMigrateField;
|
||||
'lexical-localized-fields': LexicalLocalizedField;
|
||||
@@ -105,6 +106,7 @@ export interface Config {
|
||||
collectionsSelect: {
|
||||
'lexical-fully-featured': LexicalFullyFeaturedSelect<false> | LexicalFullyFeaturedSelect<true>;
|
||||
'lexical-link-feature': LexicalLinkFeatureSelect<false> | LexicalLinkFeatureSelect<true>;
|
||||
'lexical-jsx-converter': LexicalJsxConverterSelect<false> | LexicalJsxConverterSelect<true>;
|
||||
'lexical-fields': LexicalFieldsSelect<false> | LexicalFieldsSelect<true>;
|
||||
'lexical-migrate-fields': LexicalMigrateFieldsSelect<false> | LexicalMigrateFieldsSelect<true>;
|
||||
'lexical-localized-fields': LexicalLocalizedFieldsSelect<false> | LexicalLocalizedFieldsSelect<true>;
|
||||
@@ -205,6 +207,30 @@ export interface LexicalLinkFeature {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "lexical-jsx-converter".
|
||||
*/
|
||||
export interface LexicalJsxConverter {
|
||||
id: string;
|
||||
richText?: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "lexical-fields".
|
||||
@@ -841,6 +867,10 @@ export interface PayloadLockedDocument {
|
||||
relationTo: 'lexical-link-feature';
|
||||
value: string | LexicalLinkFeature;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'lexical-jsx-converter';
|
||||
value: string | LexicalJsxConverter;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'lexical-fields';
|
||||
value: string | LexicalField;
|
||||
@@ -949,6 +979,15 @@ export interface LexicalLinkFeatureSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "lexical-jsx-converter_select".
|
||||
*/
|
||||
export interface LexicalJsxConverterSelect<T extends boolean = true> {
|
||||
richText?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "lexical-fields_select".
|
||||
|
||||
@@ -2,6 +2,7 @@ export const usersSlug = 'users'
|
||||
|
||||
export const lexicalFullyFeaturedSlug = 'lexical-fully-featured'
|
||||
export const lexicalFieldsSlug = 'lexical-fields'
|
||||
export const lexicalJSXConverterSlug = 'lexical-jsx-converter'
|
||||
|
||||
export const lexicalLinkFeatureSlug = 'lexical-link-feature'
|
||||
export const lexicalLocalizedFieldsSlug = 'lexical-localized-fields'
|
||||
|
||||
Reference in New Issue
Block a user