Skip to content

Commit 48b60fc

Browse files
authored
chore(richtext-lexical): enable strict: true (#9394)
Thanks to @GermanJablo for doing most of the work by enabling `noImplicitAny` and `strictNullChecks` in previous PRs
1 parent 90e37fe commit 48b60fc

File tree

25 files changed

+197
-168
lines changed

25 files changed

+197
-168
lines changed

packages/richtext-lexical/src/features/blocks/client/component/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export const BlockComponent: React.FC<Props> = (props) => {
162162
const { i18n, t } = useTranslation<object, string>()
163163

164164
const onChange = useCallback(
165-
async ({ formState: prevFormState, submit }: { formState: FormState; submit: boolean }) => {
165+
async ({ formState: prevFormState, submit }: { formState: FormState; submit?: boolean }) => {
166166
abortAndIgnore(onChangeAbortControllerRef.current)
167167

168168
const controller = new AbortController()

packages/richtext-lexical/src/features/blocks/server/graphQLPopulationPromise.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import { recursivelyPopulateFieldsForGraphQL } from '../../../populateGraphQL/re
99
export const blockPopulationPromiseHOC = (
1010
blocks: Block[],
1111
): PopulationPromise<SerializedBlockNode | SerializedInlineBlockNode> => {
12-
const blockPopulationPromise: PopulationPromise<SerializedBlockNode> = ({
12+
const blockPopulationPromise: PopulationPromise<
13+
SerializedBlockNode | SerializedInlineBlockNode
14+
> = ({
1315
context,
1416
currentDepth,
1517
depth,

packages/richtext-lexical/src/features/converters/html/converter/defaultConverters.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { LinebreakHTMLConverter } from './converters/linebreak.js'
44
import { ParagraphHTMLConverter } from './converters/paragraph.js'
55
import { TextHTMLConverter } from './converters/text.js'
66

7-
export const defaultHTMLConverters: HTMLConverter[] = [
7+
export const defaultHTMLConverters: HTMLConverter<any>[] = [
88
ParagraphHTMLConverter,
99
TextHTMLConverter,
1010
LinebreakHTMLConverter,

packages/richtext-lexical/src/features/experimental_table/client/plugins/TableCellResizerPlugin/index.tsx

+29-19
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,19 @@ function TableCellResizer({ editor }: { editor: LexicalEditor }): JSX.Element {
314314
[activeCell, mouseUpHandler],
315315
)
316316

317-
const getResizers = useCallback(() => {
317+
const [resizerStyles, setResizerStyles] = useState<{
318+
bottom?: null | React.CSSProperties
319+
left?: null | React.CSSProperties
320+
right?: null | React.CSSProperties
321+
top?: null | React.CSSProperties
322+
}>({
323+
bottom: null,
324+
left: null,
325+
right: null,
326+
top: null,
327+
})
328+
329+
useEffect(() => {
318330
if (activeCell) {
319331
const { height, left, top, width } = activeCell.elem.getBoundingClientRect()
320332
const zoom = calculateZoomLevel(activeCell.elem)
@@ -324,16 +336,16 @@ function TableCellResizer({ editor }: { editor: LexicalEditor }): JSX.Element {
324336
backgroundColor: 'none',
325337
cursor: 'row-resize',
326338
height: `${zoneWidth}px`,
327-
left: `${window.pageXOffset + left}px`,
328-
top: `${window.pageYOffset + top + height - zoneWidth / 2}px`,
339+
left: `${window.scrollX + left}px`,
340+
top: `${window.scrollY + top + height - zoneWidth / 2}px`,
329341
width: `${width}px`,
330342
},
331343
right: {
332344
backgroundColor: 'none',
333345
cursor: 'col-resize',
334346
height: `${height}px`,
335-
left: `${window.pageXOffset + left + width - zoneWidth / 2}px`,
336-
top: `${window.pageYOffset + top}px`,
347+
left: `${window.scrollX + left + width - zoneWidth / 2}px`,
348+
top: `${window.scrollY + top}px`,
337349
width: `${zoneWidth}px`,
338350
},
339351
}
@@ -342,33 +354,31 @@ function TableCellResizer({ editor }: { editor: LexicalEditor }): JSX.Element {
342354

343355
if (draggingDirection && mouseCurrentPos && tableRect) {
344356
if (isHeightChanging(draggingDirection)) {
345-
styles[draggingDirection].left = `${window.pageXOffset + tableRect.left}px`
346-
styles[draggingDirection].top = `${window.pageYOffset + mouseCurrentPos.y / zoom}px`
357+
styles[draggingDirection].left = `${window.scrollX + tableRect.left}px`
358+
styles[draggingDirection].top = `${window.scrollY + mouseCurrentPos.y / zoom}px`
347359
styles[draggingDirection].height = '3px'
348360
styles[draggingDirection].width = `${tableRect.width}px`
349361
} else {
350-
styles[draggingDirection].top = `${window.pageYOffset + tableRect.top}px`
351-
styles[draggingDirection].left = `${window.pageXOffset + mouseCurrentPos.x / zoom}px`
362+
styles[draggingDirection].top = `${window.scrollY + tableRect.top}px`
363+
styles[draggingDirection].left = `${window.scrollX + mouseCurrentPos.x / zoom}px`
352364
styles[draggingDirection].width = '3px'
353365
styles[draggingDirection].height = `${tableRect.height}px`
354366
}
355367

356368
styles[draggingDirection].backgroundColor = '#adf'
357369
}
358370

359-
return styles
360-
}
361-
362-
return {
363-
bottom: null,
364-
left: null,
365-
right: null,
366-
top: null,
371+
setResizerStyles(styles)
372+
} else {
373+
setResizerStyles({
374+
bottom: null,
375+
left: null,
376+
right: null,
377+
top: null,
378+
})
367379
}
368380
}, [activeCell, draggingDirection, mouseCurrentPos])
369381

370-
const resizerStyles = getResizers()
371-
372382
return (
373383
<div ref={resizerRef}>
374384
{activeCell != null && !isMouseDown && (

packages/richtext-lexical/src/features/experimental_table/client/plugins/TableHoverActionsPlugin/index.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,15 @@ function TableHoverActionsContainer({
218218
className={editorConfig.editorConfig.lexical.theme.tableAddRows}
219219
onClick={() => insertAction(true)}
220220
style={{ ...position }}
221+
type="button"
221222
/>
222223
)}
223224
{isShownColumn && (
224225
<button
225226
className={editorConfig.editorConfig.lexical.theme.tableAddColumns}
226227
onClick={() => insertAction(false)}
227228
style={{ ...position }}
229+
type="button"
228230
/>
229231
)}
230232
</>
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
'use client'
2-
import { useMemo, useRef } from 'react'
2+
import { useCallback, useEffect, useRef } from 'react'
33

44
import debounce from './debounce.js'
55

6+
// Define the type for debounced function that includes cancel method
7+
interface DebouncedFunction<T extends (...args: any[]) => any> {
8+
(...args: Parameters<T>): ReturnType<T>
9+
cancel: () => void
10+
}
11+
612
export function useDebounce<T extends (...args: never[]) => void>(
713
fn: T,
814
ms: number,
915
maxWait?: number,
1016
) {
11-
const funcRef = useRef<null | T>(null)
12-
funcRef.current = fn
17+
// Update the ref type to include cancel method
18+
const debouncedRef = useRef<DebouncedFunction<T> | null>(null)
19+
20+
useEffect(() => {
21+
debouncedRef.current = debounce(fn, ms, { maxWait }) as DebouncedFunction<T>
22+
23+
return () => {
24+
debouncedRef.current?.cancel()
25+
}
26+
}, [fn, ms, maxWait])
27+
28+
const callback = useCallback((...args: Parameters<T>) => {
29+
if (debouncedRef.current) {
30+
debouncedRef.current(...args)
31+
}
32+
}, [])
1333

14-
return useMemo(
15-
() =>
16-
debounce(
17-
(...args: Parameters<T>) => {
18-
if (funcRef.current) {
19-
funcRef.current(...args)
20-
}
21-
},
22-
ms,
23-
{ maxWait },
24-
),
25-
[ms, maxWait],
26-
)
34+
return callback
2735
}

packages/richtext-lexical/src/features/lists/checklist/server/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const ChecklistFeature = createServerFeature({
1818
: [
1919
createNode({
2020
converters: {
21-
html: ListHTMLConverter,
21+
html: ListHTMLConverter as any, // ListHTMLConverter uses a different generic type than ListNode[exportJSON], thus we need to cast as any
2222
},
2323
node: ListNode,
2424
}),

packages/richtext-lexical/src/features/lists/orderedList/server/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const OrderedListFeature = createServerFeature({
1717
: [
1818
createNode({
1919
converters: {
20-
html: ListHTMLConverter,
20+
html: ListHTMLConverter as any, // ListHTMLConverter uses a different generic type than ListNode[exportJSON], thus we need to cast as any
2121
},
2222
node: ListNode,
2323
}),

packages/richtext-lexical/src/features/lists/unorderedList/server/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const UnorderedListFeature = createServerFeature({
1414
nodes: [
1515
createNode({
1616
converters: {
17-
html: ListHTMLConverter,
17+
html: ListHTMLConverter as any, // ListHTMLConverter uses a different generic type than ListNode[exportJSON], thus we need to cast as any
1818
},
1919
node: ListNode,
2020
}),

packages/richtext-lexical/src/features/relationship/server/nodes/RelationshipNode.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,16 @@ export class RelationshipServerNode extends DecoratorBlockNode {
104104
return false
105105
}
106106

107-
decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element | null {
107+
decorate(_editor: LexicalEditor, _config: EditorConfig): JSX.Element | null {
108108
return null
109109
}
110110

111111
exportDOM(): DOMExportOutput {
112112
const element = document.createElement('div')
113-
element.setAttribute('data-lexical-relationship-id', String(this.__data?.value))
113+
element.setAttribute(
114+
'data-lexical-relationship-id',
115+
String(typeof this.__data?.value === 'object' ? this.__data?.value?.id : this.__data?.value),
116+
)
114117
element.setAttribute('data-lexical-relationship-relationTo', this.__data?.relationTo)
115118

116119
const text = document.createTextNode(this.getTextContent())
@@ -132,7 +135,7 @@ export class RelationshipServerNode extends DecoratorBlockNode {
132135
}
133136

134137
getTextContent(): string {
135-
return `${this.__data?.relationTo} relation to ${this.__data?.value}`
138+
return `${this.__data?.relationTo} relation to ${typeof this.__data?.value === 'object' ? this.__data?.value?.id : this.__data?.value}`
136139
}
137140

138141
setData(data: RelationshipData): void {

packages/richtext-lexical/src/features/toolbars/fixed/client/Toolbar/index.tsx

+23-22
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useMemo } from 'react'
88

99
import type { EditorConfigContextType } from '../../../../../lexical/config/client/EditorConfigProvider.js'
1010
import type { SanitizedClientEditorConfig } from '../../../../../lexical/config/types.js'
11-
import type { PluginComponentWithAnchor } from '../../../../typesClient.js'
11+
import type { PluginComponent } from '../../../../typesClient.js'
1212
import type { ToolbarGroup, ToolbarGroupItem } from '../../../types.js'
1313
import type { FixedToolbarFeatureProps } from '../../server/index.js'
1414

@@ -58,7 +58,7 @@ function ToolbarGroupComponent({
5858
group: ToolbarGroup
5959
index: number
6060
}): React.ReactNode {
61-
const { i18n } = useTranslation()
61+
const { i18n } = useTranslation<{}, string>()
6262
const {
6363
fieldProps: { featureClientSchemaMap, schemaPath },
6464
} = useEditorConfigContext()
@@ -106,9 +106,8 @@ function ToolbarGroupComponent({
106106

107107
return (
108108
<div className={`fixed-toolbar__group fixed-toolbar__group-${group.key}`} key={group.key}>
109-
{group.type === 'dropdown' &&
110-
group.items.length &&
111-
(DropdownIcon ? (
109+
{group.type === 'dropdown' && group.items.length ? (
110+
DropdownIcon ? (
112111
<ToolbarDropdown
113112
anchorElem={anchorElem}
114113
editor={editor}
@@ -129,14 +128,15 @@ function ToolbarGroupComponent({
129128
maxActiveItems={1}
130129
onActiveChange={onActiveChange}
131130
/>
132-
))}
133-
{group.type === 'buttons' &&
134-
group.items.length &&
135-
group.items.map((item) => {
136-
return (
137-
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
138-
)
139-
})}
131+
)
132+
) : null}
133+
{group.type === 'buttons' && group.items.length
134+
? group.items.map((item) => {
135+
return (
136+
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
137+
)
138+
})
139+
: null}
140140
{index < editorConfig.features.toolbarFixed?.groups.length - 1 && <div className="divider" />}
141141
</div>
142142
)
@@ -196,14 +196,18 @@ function FixedToolbar({
196196
)
197197

198198
if (overlapping) {
199-
currentToolbarElem.className = 'fixed-toolbar fixed-toolbar--overlapping'
200-
parentToolbarElem.className = 'fixed-toolbar fixed-toolbar--hide'
199+
currentToolbarElem.classList.remove('fixed-toolbar')
200+
currentToolbarElem.classList.add('fixed-toolbar', 'fixed-toolbar--overlapping')
201+
parentToolbarElem.classList.remove('fixed-toolbar')
202+
parentToolbarElem.classList.add('fixed-toolbar', 'fixed-toolbar--hide')
201203
} else {
202204
if (!currentToolbarElem.classList.contains('fixed-toolbar--overlapping')) {
203205
return
204206
}
205-
currentToolbarElem.className = 'fixed-toolbar'
206-
parentToolbarElem.className = 'fixed-toolbar'
207+
currentToolbarElem.classList.remove('fixed-toolbar--overlapping')
208+
currentToolbarElem.classList.add('fixed-toolbar')
209+
parentToolbarElem.classList.remove('fixed-toolbar--hide')
210+
parentToolbarElem.classList.add('fixed-toolbar')
207211
}
208212
},
209213
50,
@@ -256,10 +260,7 @@ const getParentEditorWithFixedToolbar = (
256260
return false
257261
}
258262

259-
export const FixedToolbarPlugin: PluginComponentWithAnchor<FixedToolbarFeatureProps> = ({
260-
anchorElem,
261-
clientProps,
262-
}) => {
263+
export const FixedToolbarPlugin: PluginComponent<FixedToolbarFeatureProps> = ({ clientProps }) => {
263264
const [currentEditor] = useLexicalComposerContext()
264265
const editorConfigContext = useEditorConfigContext()
265266

@@ -287,7 +288,7 @@ export const FixedToolbarPlugin: PluginComponentWithAnchor<FixedToolbarFeaturePr
287288

288289
return (
289290
<FixedToolbar
290-
anchorElem={anchorElem}
291+
anchorElem={document.body}
291292
editor={editor}
292293
editorConfig={editorConfig}
293294
parentWithFixedToolbar={parentWithFixedToolbar}

packages/richtext-lexical/src/features/toolbars/inline/client/Toolbar/index.tsx

+11-11
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,8 @@ function ToolbarGroupComponent({
9595
className={`inline-toolbar-popup__group inline-toolbar-popup__group-${group.key}`}
9696
key={group.key}
9797
>
98-
{group.type === 'dropdown' &&
99-
group.items.length &&
100-
(DropdownIcon ? (
98+
{group.type === 'dropdown' && group.items.length ? (
99+
DropdownIcon ? (
101100
<ToolbarDropdown
102101
anchorElem={anchorElem}
103102
editor={editor}
@@ -114,14 +113,15 @@ function ToolbarGroupComponent({
114113
maxActiveItems={1}
115114
onActiveChange={onActiveChange}
116115
/>
117-
))}
118-
{group.type === 'buttons' &&
119-
group.items.length &&
120-
group.items.map((item) => {
121-
return (
122-
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
123-
)
124-
})}
116+
)
117+
) : null}
118+
{group.type === 'buttons' && group.items.length
119+
? group.items.map((item) => {
120+
return (
121+
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
122+
)
123+
})
124+
: null}
125125
{index < editorConfig.features.toolbarInline?.groups.length - 1 && (
126126
<div className="divider" />
127127
)}

packages/richtext-lexical/src/features/toolbars/shared/ToolbarButton/index.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import type { LexicalEditor } from 'lexical'
33

44
import { mergeRegister } from '@lexical/utils'
5-
import { $getSelection } from 'lexical'
5+
import { $addUpdateTag, $getSelection } from 'lexical'
66
import React, { useCallback, useEffect, useState } from 'react'
77

88
import type { ToolbarGroupItem } from '../../types.js'
@@ -84,9 +84,10 @@ export const ToolbarButton = ({
8484
className={className}
8585
onClick={() => {
8686
if (enabled !== false) {
87-
editor._updateTags = new Set(['toolbar', ...editor._updateTags]) // without setting the tags, our onSelect will not be able to trigger our onChange as focus onChanges are ignored.
88-
8987
editor.focus(() => {
88+
editor.update(() => {
89+
$addUpdateTag('toolbar')
90+
})
9091
// We need to wrap the onSelect in the callback, so the editor is properly focused before the onSelect is called.
9192
item.onSelect?.({
9293
editor,

0 commit comments

Comments
 (0)