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