[Refactoring] Teact: Introduce TeactNode type for JSX typing
This commit is contained in:
parent
183a5fad5b
commit
c92a18895e
3
src/@types/teact.d.ts
vendored
3
src/@types/teact.d.ts
vendored
@ -1,3 +0,0 @@
|
||||
declare namespace JSX {
|
||||
type Element = VirtualElement;
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import React, { FC, memo, useCallback } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, memo, TeactNode, useCallback,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import {
|
||||
ApiChat, ApiMediaFormat, ApiPhoto, ApiUser, ApiUserStatus,
|
||||
@ -75,7 +77,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
let content: string | undefined = '';
|
||||
let content: TeactNode | undefined;
|
||||
const author = user ? getUserFullName(user) : (chat ? getChatTitle(lang, chat) : text);
|
||||
|
||||
if (isSavedMessages) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { FC, memo } from '../../lib/teact/teact';
|
||||
import React, { FC, memo, TeactNode } from '../../lib/teact/teact';
|
||||
import { withGlobal } from '../../global';
|
||||
|
||||
import { ApiChat, ApiUser } from '../../api/types';
|
||||
@ -42,7 +42,7 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
|
||||
}) => {
|
||||
const lang = useLang();
|
||||
|
||||
let iconElement: any;
|
||||
let iconElement: TeactNode | undefined;
|
||||
let titleText: any;
|
||||
|
||||
if (icon && title) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { FC, memo } from '../../lib/teact/teact';
|
||||
import React, { FC, memo, TeactNode } from '../../lib/teact/teact';
|
||||
|
||||
import {
|
||||
ApiChat, ApiMediaFormat, ApiPhoto, ApiUser,
|
||||
@ -68,7 +68,7 @@ const ProfilePhoto: FC<OwnProps> = ({
|
||||
const avatarBlobUrl = useMedia(avatarMediaHash, false, ApiMediaFormat.BlobUrl, lastSyncTime);
|
||||
const imageSrc = photoBlobUrl || avatarBlobUrl || photo?.thumbnail?.dataUri;
|
||||
|
||||
let content: string | undefined = '';
|
||||
let content: TeactNode | undefined;
|
||||
|
||||
if (isSavedMessages) {
|
||||
content = <i className="icon-avatar-saved-messages" />;
|
||||
|
||||
@ -21,7 +21,7 @@ const CodeBlock: FC<OwnProps> = ({ text, language, noCopy }) => {
|
||||
const [isWordWrap, setWordWrap] = useState(true);
|
||||
|
||||
const { result: highlighted } = useAsync(() => {
|
||||
if (!language) return Promise.resolve();
|
||||
if (!language) return Promise.resolve(undefined);
|
||||
return import('../../../util/highlightCode')
|
||||
.then((lib) => lib.default(text, language));
|
||||
}, [language, text]);
|
||||
|
||||
@ -76,7 +76,7 @@ export default function renderText(
|
||||
|
||||
function escapeHtml(textParts: TextPart[]): TextPart[] {
|
||||
const divEl = document.createElement('div');
|
||||
return textParts.reduce((result, part) => {
|
||||
return textParts.reduce((result: TextPart[], part) => {
|
||||
if (typeof part !== 'string') {
|
||||
result.push(part);
|
||||
return result;
|
||||
@ -86,7 +86,7 @@ function escapeHtml(textParts: TextPart[]): TextPart[] {
|
||||
result.push(divEl.innerHTML);
|
||||
|
||||
return result;
|
||||
}, [] as TextPart[]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
function replaceEmojis(textParts: TextPart[], size: 'big' | 'small', type: 'jsx' | 'html'): TextPart[] {
|
||||
@ -174,7 +174,7 @@ function addLineBreaks(textParts: TextPart[], type: 'jsx' | 'html'): TextPart[]
|
||||
}
|
||||
|
||||
function addHighlight(textParts: TextPart[], highlight: string | undefined): TextPart[] {
|
||||
return textParts.reduce((result, part) => {
|
||||
return textParts.reduce<TextPart[]>((result, part) => {
|
||||
if (typeof part !== 'string' || !highlight) {
|
||||
result.push(part);
|
||||
return result;
|
||||
@ -198,13 +198,13 @@ function addHighlight(textParts: TextPart[], highlight: string | undefined): Tex
|
||||
newParts.push(part.substring(queryPosition + highlight.length));
|
||||
|
||||
return [...result, ...newParts];
|
||||
}, [] as TextPart[]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
const RE_LINK = new RegExp(`${RE_LINK_TEMPLATE}|${RE_MENTION_TEMPLATE}`, 'ig');
|
||||
|
||||
function addLinks(textParts: TextPart[]): TextPart[] {
|
||||
return textParts.reduce((result, part) => {
|
||||
return textParts.reduce<TextPart[]>((result, part) => {
|
||||
if (typeof part !== 'string') {
|
||||
result.push(part);
|
||||
return result;
|
||||
@ -244,11 +244,11 @@ function addLinks(textParts: TextPart[]): TextPart[] {
|
||||
content.push(part.substring(lastIndex));
|
||||
|
||||
return [...result, ...content];
|
||||
}, [] as TextPart[]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
function replaceSimpleMarkdown(textParts: TextPart[], type: 'jsx' | 'html'): TextPart[] {
|
||||
return textParts.reduce((result, part) => {
|
||||
return textParts.reduce<TextPart[]>((result, part) => {
|
||||
if (typeof part !== 'string') {
|
||||
result.push(part);
|
||||
return result;
|
||||
@ -280,7 +280,7 @@ function replaceSimpleMarkdown(textParts: TextPart[], type: 'jsx' | 'html'): Tex
|
||||
|
||||
return entityResult;
|
||||
}, result);
|
||||
}, [] as TextPart[]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function areLinesWrapping(text: string, element: HTMLElement) {
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import React, { FC, memo } from '../../../../lib/teact/teact';
|
||||
import React, { FC, memo, TeactNode } from '../../../../lib/teact/teact';
|
||||
|
||||
import { ApiWebDocument } from '../../../../api/types';
|
||||
|
||||
import { getFirstLetters } from '../../../../util/textFormat';
|
||||
import renderText from '../../../common/helpers/renderText';
|
||||
import useMedia from '../../../../hooks/useMedia';
|
||||
import { preventMessageInputBlurWithBubbling } from '../../helpers/preventMessageInputBlur';
|
||||
|
||||
import ListItem from '../../../ui/ListItem';
|
||||
|
||||
import './BaseResult.scss';
|
||||
import { preventMessageInputBlurWithBubbling } from '../../helpers/preventMessageInputBlur';
|
||||
|
||||
export type OwnProps = {
|
||||
focus?: boolean;
|
||||
@ -30,7 +30,7 @@ const BaseResult: FC<OwnProps> = ({
|
||||
transitionClassNames = '',
|
||||
onClick,
|
||||
}) => {
|
||||
let content: string | undefined = '';
|
||||
let content: TeactNode | undefined;
|
||||
|
||||
const thumbnailDataUrl = useMedia(thumbnail ? `webDocument:${thumbnail.url}` : undefined);
|
||||
thumbUrl = thumbUrl || thumbnailDataUrl;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import React, { FC, memo, useCallback } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, memo, TeactNode, useCallback,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -13,7 +15,7 @@ type OwnProps = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
label: string;
|
||||
label: TeactNode;
|
||||
subLabel?: string;
|
||||
checked: boolean;
|
||||
disabled?: boolean;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
FC, memo, useCallback, useRef,
|
||||
FC, memo, TeactNode, useCallback, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import { TextPart } from '../../types';
|
||||
@ -15,7 +15,7 @@ type OwnProps = {
|
||||
onClose: () => void;
|
||||
onCloseAnimationEnd?: () => void;
|
||||
title?: string;
|
||||
header?: FC;
|
||||
header?: TeactNode;
|
||||
textParts?: TextPart[];
|
||||
text?: string;
|
||||
confirmLabel?: string;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { RefObject } from 'react';
|
||||
import React, {
|
||||
FC, useEffect, useRef,
|
||||
FC, TeactNode, useEffect, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import { TextPart } from '../../types';
|
||||
@ -25,7 +25,7 @@ type OwnProps = {
|
||||
title?: string | TextPart[];
|
||||
className?: string;
|
||||
isOpen?: boolean;
|
||||
header?: any;
|
||||
header?: TeactNode;
|
||||
hasCloseButton?: boolean;
|
||||
noBackdrop?: boolean;
|
||||
children: React.ReactNode;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import React, { FC, memo } from '../../lib/teact/teact';
|
||||
import React, { FC, memo, TeactNode } from '../../lib/teact/teact';
|
||||
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -11,7 +11,7 @@ import './Radio.scss';
|
||||
type OwnProps = {
|
||||
id?: string;
|
||||
name: string;
|
||||
label: string;
|
||||
label: TeactNode;
|
||||
subLabel?: string;
|
||||
value: string;
|
||||
checked: boolean;
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import React, { FC, useCallback, memo } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, useCallback, memo, TeactNode,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import Radio from './Radio';
|
||||
|
||||
export type IRadioOption = {
|
||||
label: string;
|
||||
label: TeactNode;
|
||||
subLabel?: string;
|
||||
value: string;
|
||||
hidden?: boolean;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { DEBUG, DEBUG_MORE } from '../../config';
|
||||
import {
|
||||
fastRaf, fastRafPrimary, onTickEnd, onTickEndPrimary, throttleWithPrimaryRaf, throttleWithRaf,
|
||||
@ -102,6 +103,9 @@ export type VirtualRealElement =
|
||||
| VirtualElementComponent;
|
||||
export type VirtualElementChildren = VirtualElement[];
|
||||
|
||||
// Compatibility with JSX types
|
||||
export type TeactNode = ReactElement | string | number | boolean;
|
||||
|
||||
const Fragment = Symbol('Fragment');
|
||||
|
||||
const DEBUG_RENDER_THRESHOLD = 7;
|
||||
@ -712,10 +716,7 @@ export function memo<T extends FC>(Component: T, debugKey?: string) {
|
||||
} as T;
|
||||
}
|
||||
|
||||
// We need to keep it here for JSX.
|
||||
const Teact = {
|
||||
export default {
|
||||
createElement,
|
||||
Fragment,
|
||||
};
|
||||
|
||||
export default Teact;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { TeactNode } from '../lib/teact/teact';
|
||||
import {
|
||||
ApiBotInlineMediaResult, ApiBotInlineResult, ApiBotInlineSwitchPm,
|
||||
ApiChatInviteImporter,
|
||||
@ -5,7 +6,7 @@ import {
|
||||
ApiLanguage, ApiMessage, ApiShippingAddress, ApiStickerSet,
|
||||
} from '../api/types';
|
||||
|
||||
export type TextPart = string | JSX.Element;
|
||||
export type TextPart = TeactNode;
|
||||
|
||||
export enum LoadMoreDirection {
|
||||
Backwards,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Element, Root } from 'hast';
|
||||
import { lowlight } from 'lowlight/lib/core';
|
||||
import Teact from '../lib/teact/teact';
|
||||
import Teact, { TeactNode } from '../lib/teact/teact';
|
||||
|
||||
// First element in alias array MUST BE a language package name
|
||||
const SUPPORTED_LANGUAGES = {
|
||||
@ -85,7 +85,7 @@ async function ensureLanguage(language: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function treeToElements(tree: Element | Root): JSX.Element {
|
||||
function treeToElements(tree: Element | Root): TeactNode {
|
||||
const children = tree.children.map((child) => {
|
||||
if (child.type === 'text') {
|
||||
return child.value;
|
||||
@ -97,12 +97,12 @@ function treeToElements(tree: Element | Root): JSX.Element {
|
||||
}).filter(Boolean);
|
||||
|
||||
if (tree.type === 'root') {
|
||||
return Teact.createElement('code', { className: 'hljs custom-scroll-x' }, children);
|
||||
return Teact.createElement('code', { className: 'hljs custom-scroll-x' }, children) as unknown as TeactNode;
|
||||
}
|
||||
|
||||
const name = tree.tagName;
|
||||
const classNameArray = tree.properties?.className as string[];
|
||||
const className = classNameArray?.join(' ');
|
||||
|
||||
return Teact.createElement(name, { className }, children);
|
||||
return Teact.createElement(name, { className }, children) as unknown as TeactNode;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user