Skip to content

Commit

Permalink
chore(richtext-lexical): enable strict: true (#9394)
Browse files Browse the repository at this point in the history
Thanks to @GermanJablo for doing most of the work by enabling
`noImplicitAny` and `strictNullChecks` in previous PRs
  • Loading branch information
AlessioGr authored Nov 21, 2024
1 parent 90e37fe commit 48b60fc
Show file tree
Hide file tree
Showing 25 changed files with 197 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export const BlockComponent: React.FC<Props> = (props) => {
const { i18n, t } = useTranslation<object, string>()

const onChange = useCallback(
async ({ formState: prevFormState, submit }: { formState: FormState; submit: boolean }) => {
async ({ formState: prevFormState, submit }: { formState: FormState; submit?: boolean }) => {
abortAndIgnore(onChangeAbortControllerRef.current)

const controller = new AbortController()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { recursivelyPopulateFieldsForGraphQL } from '../../../populateGraphQL/re
export const blockPopulationPromiseHOC = (
blocks: Block[],
): PopulationPromise<SerializedBlockNode | SerializedInlineBlockNode> => {
const blockPopulationPromise: PopulationPromise<SerializedBlockNode> = ({
const blockPopulationPromise: PopulationPromise<
SerializedBlockNode | SerializedInlineBlockNode
> = ({
context,
currentDepth,
depth,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { LinebreakHTMLConverter } from './converters/linebreak.js'
import { ParagraphHTMLConverter } from './converters/paragraph.js'
import { TextHTMLConverter } from './converters/text.js'

export const defaultHTMLConverters: HTMLConverter[] = [
export const defaultHTMLConverters: HTMLConverter<any>[] = [
ParagraphHTMLConverter,
TextHTMLConverter,
LinebreakHTMLConverter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,19 @@ function TableCellResizer({ editor }: { editor: LexicalEditor }): JSX.Element {
[activeCell, mouseUpHandler],
)

const getResizers = useCallback(() => {
const [resizerStyles, setResizerStyles] = useState<{
bottom?: null | React.CSSProperties
left?: null | React.CSSProperties
right?: null | React.CSSProperties
top?: null | React.CSSProperties
}>({
bottom: null,
left: null,
right: null,
top: null,
})

useEffect(() => {
if (activeCell) {
const { height, left, top, width } = activeCell.elem.getBoundingClientRect()
const zoom = calculateZoomLevel(activeCell.elem)
Expand All @@ -324,16 +336,16 @@ function TableCellResizer({ editor }: { editor: LexicalEditor }): JSX.Element {
backgroundColor: 'none',
cursor: 'row-resize',
height: `${zoneWidth}px`,
left: `${window.pageXOffset + left}px`,
top: `${window.pageYOffset + top + height - zoneWidth / 2}px`,
left: `${window.scrollX + left}px`,
top: `${window.scrollY + top + height - zoneWidth / 2}px`,
width: `${width}px`,
},
right: {
backgroundColor: 'none',
cursor: 'col-resize',
height: `${height}px`,
left: `${window.pageXOffset + left + width - zoneWidth / 2}px`,
top: `${window.pageYOffset + top}px`,
left: `${window.scrollX + left + width - zoneWidth / 2}px`,
top: `${window.scrollY + top}px`,
width: `${zoneWidth}px`,
},
}
Expand All @@ -342,33 +354,31 @@ function TableCellResizer({ editor }: { editor: LexicalEditor }): JSX.Element {

if (draggingDirection && mouseCurrentPos && tableRect) {
if (isHeightChanging(draggingDirection)) {
styles[draggingDirection].left = `${window.pageXOffset + tableRect.left}px`
styles[draggingDirection].top = `${window.pageYOffset + mouseCurrentPos.y / zoom}px`
styles[draggingDirection].left = `${window.scrollX + tableRect.left}px`
styles[draggingDirection].top = `${window.scrollY + mouseCurrentPos.y / zoom}px`
styles[draggingDirection].height = '3px'
styles[draggingDirection].width = `${tableRect.width}px`
} else {
styles[draggingDirection].top = `${window.pageYOffset + tableRect.top}px`
styles[draggingDirection].left = `${window.pageXOffset + mouseCurrentPos.x / zoom}px`
styles[draggingDirection].top = `${window.scrollY + tableRect.top}px`
styles[draggingDirection].left = `${window.scrollX + mouseCurrentPos.x / zoom}px`
styles[draggingDirection].width = '3px'
styles[draggingDirection].height = `${tableRect.height}px`
}

styles[draggingDirection].backgroundColor = '#adf'
}

return styles
}

return {
bottom: null,
left: null,
right: null,
top: null,
setResizerStyles(styles)
} else {
setResizerStyles({
bottom: null,
left: null,
right: null,
top: null,
})
}
}, [activeCell, draggingDirection, mouseCurrentPos])

const resizerStyles = getResizers()

return (
<div ref={resizerRef}>
{activeCell != null && !isMouseDown && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,15 @@ function TableHoverActionsContainer({
className={editorConfig.editorConfig.lexical.theme.tableAddRows}
onClick={() => insertAction(true)}
style={{ ...position }}
type="button"
/>
)}
{isShownColumn && (
<button
className={editorConfig.editorConfig.lexical.theme.tableAddColumns}
onClick={() => insertAction(false)}
style={{ ...position }}
type="button"
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
'use client'
import { useMemo, useRef } from 'react'
import { useCallback, useEffect, useRef } from 'react'

import debounce from './debounce.js'

// Define the type for debounced function that includes cancel method
interface DebouncedFunction<T extends (...args: any[]) => any> {
(...args: Parameters<T>): ReturnType<T>
cancel: () => void
}

export function useDebounce<T extends (...args: never[]) => void>(
fn: T,
ms: number,
maxWait?: number,
) {
const funcRef = useRef<null | T>(null)
funcRef.current = fn
// Update the ref type to include cancel method
const debouncedRef = useRef<DebouncedFunction<T> | null>(null)

useEffect(() => {
debouncedRef.current = debounce(fn, ms, { maxWait }) as DebouncedFunction<T>

return () => {
debouncedRef.current?.cancel()
}
}, [fn, ms, maxWait])

const callback = useCallback((...args: Parameters<T>) => {
if (debouncedRef.current) {
debouncedRef.current(...args)
}
}, [])

return useMemo(
() =>
debounce(
(...args: Parameters<T>) => {
if (funcRef.current) {
funcRef.current(...args)
}
},
ms,
{ maxWait },
),
[ms, maxWait],
)
return callback
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const ChecklistFeature = createServerFeature({
: [
createNode({
converters: {
html: ListHTMLConverter,
html: ListHTMLConverter as any, // ListHTMLConverter uses a different generic type than ListNode[exportJSON], thus we need to cast as any
},
node: ListNode,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const OrderedListFeature = createServerFeature({
: [
createNode({
converters: {
html: ListHTMLConverter,
html: ListHTMLConverter as any, // ListHTMLConverter uses a different generic type than ListNode[exportJSON], thus we need to cast as any
},
node: ListNode,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const UnorderedListFeature = createServerFeature({
nodes: [
createNode({
converters: {
html: ListHTMLConverter,
html: ListHTMLConverter as any, // ListHTMLConverter uses a different generic type than ListNode[exportJSON], thus we need to cast as any
},
node: ListNode,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,16 @@ export class RelationshipServerNode extends DecoratorBlockNode {
return false
}

decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element | null {
decorate(_editor: LexicalEditor, _config: EditorConfig): JSX.Element | null {
return null
}

exportDOM(): DOMExportOutput {
const element = document.createElement('div')
element.setAttribute('data-lexical-relationship-id', String(this.__data?.value))
element.setAttribute(
'data-lexical-relationship-id',
String(typeof this.__data?.value === 'object' ? this.__data?.value?.id : this.__data?.value),
)
element.setAttribute('data-lexical-relationship-relationTo', this.__data?.relationTo)

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

getTextContent(): string {
return `${this.__data?.relationTo} relation to ${this.__data?.value}`
return `${this.__data?.relationTo} relation to ${typeof this.__data?.value === 'object' ? this.__data?.value?.id : this.__data?.value}`
}

setData(data: RelationshipData): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useMemo } from 'react'

import type { EditorConfigContextType } from '../../../../../lexical/config/client/EditorConfigProvider.js'
import type { SanitizedClientEditorConfig } from '../../../../../lexical/config/types.js'
import type { PluginComponentWithAnchor } from '../../../../typesClient.js'
import type { PluginComponent } from '../../../../typesClient.js'
import type { ToolbarGroup, ToolbarGroupItem } from '../../../types.js'
import type { FixedToolbarFeatureProps } from '../../server/index.js'

Expand Down Expand Up @@ -58,7 +58,7 @@ function ToolbarGroupComponent({
group: ToolbarGroup
index: number
}): React.ReactNode {
const { i18n } = useTranslation()
const { i18n } = useTranslation<{}, string>()
const {
fieldProps: { featureClientSchemaMap, schemaPath },
} = useEditorConfigContext()
Expand Down Expand Up @@ -106,9 +106,8 @@ function ToolbarGroupComponent({

return (
<div className={`fixed-toolbar__group fixed-toolbar__group-${group.key}`} key={group.key}>
{group.type === 'dropdown' &&
group.items.length &&
(DropdownIcon ? (
{group.type === 'dropdown' && group.items.length ? (
DropdownIcon ? (
<ToolbarDropdown
anchorElem={anchorElem}
editor={editor}
Expand All @@ -129,14 +128,15 @@ function ToolbarGroupComponent({
maxActiveItems={1}
onActiveChange={onActiveChange}
/>
))}
{group.type === 'buttons' &&
group.items.length &&
group.items.map((item) => {
return (
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
)
})}
)
) : null}
{group.type === 'buttons' && group.items.length
? group.items.map((item) => {
return (
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
)
})
: null}
{index < editorConfig.features.toolbarFixed?.groups.length - 1 && <div className="divider" />}
</div>
)
Expand Down Expand Up @@ -196,14 +196,18 @@ function FixedToolbar({
)

if (overlapping) {
currentToolbarElem.className = 'fixed-toolbar fixed-toolbar--overlapping'
parentToolbarElem.className = 'fixed-toolbar fixed-toolbar--hide'
currentToolbarElem.classList.remove('fixed-toolbar')
currentToolbarElem.classList.add('fixed-toolbar', 'fixed-toolbar--overlapping')
parentToolbarElem.classList.remove('fixed-toolbar')
parentToolbarElem.classList.add('fixed-toolbar', 'fixed-toolbar--hide')
} else {
if (!currentToolbarElem.classList.contains('fixed-toolbar--overlapping')) {
return
}
currentToolbarElem.className = 'fixed-toolbar'
parentToolbarElem.className = 'fixed-toolbar'
currentToolbarElem.classList.remove('fixed-toolbar--overlapping')
currentToolbarElem.classList.add('fixed-toolbar')
parentToolbarElem.classList.remove('fixed-toolbar--hide')
parentToolbarElem.classList.add('fixed-toolbar')
}
},
50,
Expand Down Expand Up @@ -256,10 +260,7 @@ const getParentEditorWithFixedToolbar = (
return false
}

export const FixedToolbarPlugin: PluginComponentWithAnchor<FixedToolbarFeatureProps> = ({
anchorElem,
clientProps,
}) => {
export const FixedToolbarPlugin: PluginComponent<FixedToolbarFeatureProps> = ({ clientProps }) => {
const [currentEditor] = useLexicalComposerContext()
const editorConfigContext = useEditorConfigContext()

Expand Down Expand Up @@ -287,7 +288,7 @@ export const FixedToolbarPlugin: PluginComponentWithAnchor<FixedToolbarFeaturePr

return (
<FixedToolbar
anchorElem={anchorElem}
anchorElem={document.body}
editor={editor}
editorConfig={editorConfig}
parentWithFixedToolbar={parentWithFixedToolbar}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@ function ToolbarGroupComponent({
className={`inline-toolbar-popup__group inline-toolbar-popup__group-${group.key}`}
key={group.key}
>
{group.type === 'dropdown' &&
group.items.length &&
(DropdownIcon ? (
{group.type === 'dropdown' && group.items.length ? (
DropdownIcon ? (
<ToolbarDropdown
anchorElem={anchorElem}
editor={editor}
Expand All @@ -114,14 +113,15 @@ function ToolbarGroupComponent({
maxActiveItems={1}
onActiveChange={onActiveChange}
/>
))}
{group.type === 'buttons' &&
group.items.length &&
group.items.map((item) => {
return (
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
)
})}
)
) : null}
{group.type === 'buttons' && group.items.length
? group.items.map((item) => {
return (
<ButtonGroupItem anchorElem={anchorElem} editor={editor} item={item} key={item.key} />
)
})
: null}
{index < editorConfig.features.toolbarInline?.groups.length - 1 && (
<div className="divider" />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { LexicalEditor } from 'lexical'

import { mergeRegister } from '@lexical/utils'
import { $getSelection } from 'lexical'
import { $addUpdateTag, $getSelection } from 'lexical'
import React, { useCallback, useEffect, useState } from 'react'

import type { ToolbarGroupItem } from '../../types.js'
Expand Down Expand Up @@ -84,9 +84,10 @@ export const ToolbarButton = ({
className={className}
onClick={() => {
if (enabled !== false) {
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.

editor.focus(() => {
editor.update(() => {
$addUpdateTag('toolbar')
})
// We need to wrap the onSelect in the callback, so the editor is properly focused before the onSelect is called.
item.onSelect?.({
editor,
Expand Down
Loading

0 comments on commit 48b60fc

Please sign in to comment.