fix(*): fix null assertion issues (#6682)

This commit is contained in:
白熱
2026-03-14 16:37:12 +08:00
committed by GitHub
parent 3b7e5bdc75
commit b77fbb8027
14 changed files with 58 additions and 35 deletions
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { hsvToRgb } from './color-conversion';
interface IAlphaSliderProps {
@@ -29,16 +29,19 @@ export function AlphaSlider({ hsv, alpha, onChange, onChanged }: IAlphaSliderPro
const sliderRef = useRef<HTMLDivElement>(null);
const thumbRef = useRef<HTMLDivElement>(null);
const thumbSize = useMemo(() => {
return thumbRef.current?.clientWidth ?? 0;
}, [thumbRef.current]);
const calculateAlpha = useCallback((clientX: number) => {
const slider = sliderRef.current;
if (!slider) return;
const thumbSize = thumbRef.current?.clientWidth ?? 0;
const rect = slider.getBoundingClientRect();
const maxX = rect.width - thumbSize;
if (maxX <= 0) {
onChange(0);
return;
}
const x = Math.max(0, Math.min(clientX - rect.left, maxX));
onChange(Math.round(x / maxX * 100) / 100);
}, [onChange]);
@@ -69,7 +72,11 @@ export function AlphaSlider({ hsv, alpha, onChange, onChanged }: IAlphaSliderPro
const getThumbPosition = () => {
const safeAlpha = Math.min(Math.max(alpha * 100, 0), 100);
return `${(safeAlpha / 100) * (100 - (thumbSize / sliderRef.current?.clientWidth! * 100))}%`;
const thumbSize = thumbRef.current?.clientWidth ?? 0;
const sliderWidth = sliderRef.current?.clientWidth ?? 0;
const thumbOffsetPercent = sliderWidth > 0 ? (thumbSize / sliderWidth) * 100 : 0;
return `${(safeAlpha / 100) * (100 - thumbOffsetPercent)}%`;
};
const color = hsvToRgb(...hsv);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
interface IHueSliderProps {
hsv: [number, number, number];
@@ -27,22 +27,24 @@ export function HueSlider({ hsv, onChange, onChanged }: IHueSliderProps) {
const sliderRef = useRef<HTMLDivElement>(null);
const thumbRef = useRef<HTMLDivElement>(null);
const thumbSize = useMemo(() => {
return thumbRef.current?.clientWidth ?? 0;
}, []);
const calculateHue = useCallback((clientX: number) => {
const slider = sliderRef.current;
if (!slider) return;
const thumbSize = thumbRef.current?.clientWidth ?? 0;
const rect = slider.getBoundingClientRect();
const maxX = rect.width - thumbSize;
if (maxX <= 0) {
onChange(0, hsv[1], hsv[2]);
return;
}
const x = Math.max(0, Math.min(clientX - rect.left, maxX));
const newHue = Math.round((x / maxX) * 360);
onChange(newHue, hsv[1], hsv[2]);
}, [hsv, thumbSize, onChange]);
}, [hsv, onChange]);
const handlePointerMove = useCallback((e: PointerEvent) => {
e.stopPropagation();
@@ -71,7 +73,11 @@ export function HueSlider({ hsv, onChange, onChanged }: IHueSliderProps) {
const getThumbPosition = () => {
const safeHue = Math.min(Math.max(hsv[0], 0), 360);
return `${(safeHue / 360) * (100 - (thumbSize / sliderRef.current?.clientWidth! * 100))}%`;
const thumbSize = thumbRef.current?.clientWidth ?? 0;
const sliderWidth = sliderRef.current?.clientWidth ?? 0;
const thumbOffsetPercent = sliderWidth > 0 ? (thumbSize / sliderWidth) * 100 : 0;
return `${(safeHue / 360) * (100 - thumbOffsetPercent)}%`;
};
return (
@@ -50,7 +50,7 @@ export interface IRadioProps {
export function Radio(props: IRadioProps) {
const { children, checked, value, disabled = false, onChange } = props;
const inputRef = useRef<HTMLInputElement>(null);
const inputRef = useRef<HTMLInputElement>(null!);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
e.stopPropagation();
@@ -60,7 +60,7 @@ export function Radio(props: IRadioProps) {
if (typeof value !== 'undefined') {
onChange && onChange(value);
} else {
const checked = inputRef?.current?.checked!;
const checked = inputRef.current.checked;
onChange && onChange(checked);
}
}
@@ -297,8 +297,11 @@ export const DocTableInsertColumnCommand: ICommand<IDocTableInsertColumnCommandP
const pageWidth = (documentStyle.pageSize?.width ?? 800) - marginLeft - marginRight;
// eslint-disable-next-line ts/no-non-null-asserted-optional-chain
const tableColumns = snapshot?.tableSource?.[tableId].tableColumns!;
const tableColumns = snapshot?.tableSource?.[tableId]?.tableColumns;
if (!tableColumns) {
return false;
}
const { newColWidth, widths } = getColumnWidths(pageWidth, tableColumns, columnIndex);
@@ -556,7 +556,7 @@ export class DocClipboardService extends Disposable implements IDocClipboardServ
};
// clipboardHooks 应该被重新设计,用来处理多个 hook 处理同一个节点的能力
// 参考 interceptor
const onBeforePasteImage = (this._clipboardHooks.find((e) => e.onBeforePasteImage)?.onBeforePasteImage!) ?? fileToBase64;
const onBeforePasteImage = this._clipboardHooks.find((e) => e.onBeforePasteImage)?.onBeforePasteImage ?? fileToBase64;
await Promise.all(files.map(async (file, index) => {
const image = await onBeforePasteImage(file);
@@ -254,7 +254,7 @@ function processNode(node: DataStreamTreeNode, doc: IDocumentData, result: IHtml
case DataStreamTreeNodeType.PARAGRAPH: {
const { children, startIndex, endIndex } = node;
const paragraph = doc.body?.paragraphs!.find((p) => p.startIndex === endIndex) ?? {} as IParagraph;
const paragraph = doc.body?.paragraphs?.find((p) => p.startIndex === endIndex) ?? {} as IParagraph;
const { paragraphStyle = {} } = paragraph;
const { spaceAbove, spaceBelow, lineSpacing } = paragraphStyle;
const style = [];
@@ -368,10 +368,9 @@ function _divideOperator(
} else {
newGlyphGroup = [spanGroupCached[0]];
}
const column = currentLine.parent;
const column = currentLine.parent!;
// eslint-disable-next-line ts/no-non-null-asserted-optional-chain
const { paragraphStart: lineIsStart } = column?.lines.pop()!; // Delete the previous line and recalculate according to the maximum content height
const { paragraphStart: lineIsStart } = column.lines.pop()!; // Delete the previous line and recalculate according to the maximum content height
_lineOperator(
ctx,
@@ -27,7 +27,7 @@ import { useDocHight } from '../../formula-editor/hooks/use-highlight';
export function useRangesHighlight(editor: Nullable<Editor>, focusing: boolean, unitId: string, subUnitId: string) {
const lexerTreeBuilder = useDependency(LexerTreeBuilder);
const highlightDoc = useDocHight('');
const change = useObservable(editor?.getDocumentDataModel()!.change$);
const change = useObservable(editor?.getDocumentDataModel()?.change$);
const [sequenceNodes, setSequenceNodes] = useState<(string | ISequenceNode)[]>([]);
const markSelectionService = useDependency(IMarkSelectionService);
const last = useRef('');
@@ -270,7 +270,7 @@ export function RangeSelector(props: IRangeSelectorProps) {
const handleOpenModal = useEvent(() => {
blurEditor();
setRangeSelectorRanges(parseRanges(editor?.getDocumentDataModel()!.getPlainText() ?? ''));
setRangeSelectorRanges(parseRanges(editor?.getDocumentDataModel()?.getPlainText() ?? ''));
setPopupVisible(true);
});
@@ -294,12 +294,12 @@ export function RangeSelector(props: IRangeSelectorProps) {
setRangeSelectorRanges([]);
setPopupVisible(false);
},
getValue: () => editor?.getDocumentDataModel()!.getPlainText() ?? '',
getValue: () => editor?.getDocumentDataModel()?.getPlainText() ?? '',
};
}, [blurEditor, editor, editorService, selectorRef, sequenceNodesRef]);
useEffect(() => {
onVerify?.(verifyRange(sequenceNodes), editor?.getDocumentDataModel()!.getPlainText() ?? '');
onVerify?.(verifyRange(sequenceNodes), editor?.getDocumentDataModel()?.getPlainText() ?? '');
}, [sequenceNodes]);
useEffect(() => {
@@ -21,7 +21,6 @@ import { DeviceInputEventType } from '@univerjs/engine-render';
import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, MetaKeys, useDependency, useEvent, useObservable, useSidebarClick } from '@univerjs/ui';
import * as React from 'react';
import { useEffect, useRef, useState } from 'react';
import { SetCellEditVisibleArrowOperation, SetCellEditVisibleOperation } from '../../commands/operations/cell-edit.operation';
import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys';
import { IEditorBridgeService } from '../../services/editor-bridge.service';
@@ -124,7 +123,7 @@ export const EditorContainer: React.FC<ICellIEditorProps> = () => {
useSidebarClick(handleClickSideBar);
const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId!);
const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId);
const onMoveInEditor = useEvent((keycode: KeyCode, metaKey: MetaKeys) => {
commandService.executeCommand(SetCellEditVisibleArrowOperation.id, {
@@ -22,7 +22,7 @@ import { useMemo } from 'react';
import { SetCellEditVisibleOperation } from '../../commands/operations/cell-edit.operation';
import { IEditorBridgeService } from '../../services/editor-bridge.service';
export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | 2>, unitId: string) {
export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | 2>, unitId?: string) {
const editorBridgeService = useDependency(IEditorBridgeService);
const commandService = useDependency(ICommandService);
@@ -34,12 +34,16 @@ export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 |
],
handler: (keycode: KeyCode) => {
if (keycode === KeyCode.ENTER || keycode === KeyCode.ESC || keycode === KeyCode.TAB) {
if (unitId == null) {
return;
}
editorBridgeService.disableForceKeepVisible();
commandService.syncExecuteCommand(SetCellEditVisibleOperation.id, {
visible: false,
eventType: DeviceInputEventType.Keyboard,
keycode,
unitId: unitId!,
unitId,
});
}
},
@@ -83,7 +83,7 @@ export function FormulaBar(props: IProps) {
const workbook = useObservable(() => univerInstanceService.getCurrentTypeOfUnit$<Workbook>(UniverInstanceType.UNIVER_SHEET), undefined, undefined, [])!;
const isRefSelecting = useRef<0 | 1 | 2>(0);
const editState = useObservable(editorBridgeService.currentEditCellState$);
const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId ?? '');
const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId);
const FormulaEditor = componentManager.get(EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY);
const formulaAuxUIParts = useComponentsOfPart(SheetsUIPart.FORMULA_AUX);
const contextService = useDependency(IContextService);
@@ -54,9 +54,10 @@ export const DeleteRangeMoveUpCommand: ICommand = {
if (!target) return false;
const { unitId, subUnitId, workbook, worksheet } = target;
let range = params?.range;
let range: IRange | undefined = params?.range;
if (!range) {
range = selectionManagerService.getCurrentLastSelection()?.range!;
const currentSelection = selectionManagerService.getCurrentLastSelection();
range = currentSelection?.range as IRange | undefined;
}
if (!range) return false;
@@ -503,17 +503,21 @@ export const ThreadCommentTree = (props: IThreadCommentTreeProps) => {
unitId={unitId}
subUnitId={subUnitId}
onSave={async ({ text, attachments }) => {
if (!currentUser?.userID || !comments?.root) {
throw new Error('[ThreadCommentTree] No current user or root comment found');
}
const comment: IThreadComment = {
text,
attachments,
dT: getDT(),
id: generateRandomId(),
ref: refStr!,
personId: currentUser?.userID!,
personId: currentUser?.userID,
parentId: comments?.root.id,
unitId,
subUnitId,
threadId: comments?.root.threadId!,
threadId: comments?.root.threadId,
};
if (onAddComment?.(comment) === false) {