Teact: Introduce extra classes manipulation
This commit is contained in:
parent
18fe9abbc2
commit
289694c13b
@ -638,8 +638,7 @@ export type ApiUpdate = (
|
|||||||
ApiUpdatePhoneCall | ApiUpdatePhoneCallSignalingData | ApiUpdatePhoneCallMediaState |
|
ApiUpdatePhoneCall | ApiUpdatePhoneCallSignalingData | ApiUpdatePhoneCallMediaState |
|
||||||
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus |
|
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus |
|
||||||
ApiUpdateMessageExtendedMedia | ApiUpdateConfig | ApiUpdateTopicNotifyExceptions | ApiUpdatePinnedTopic |
|
ApiUpdateMessageExtendedMedia | ApiUpdateConfig | ApiUpdateTopicNotifyExceptions | ApiUpdatePinnedTopic |
|
||||||
ApiUpdatePinnedTopicsOrder | ApiUpdateTopic | ApiUpdateTopics | ApiUpdateRecentEmojiStatuses |
|
ApiUpdatePinnedTopicsOrder | ApiUpdateTopic | ApiUpdateTopics | ApiUpdateRecentEmojiStatuses | ApiRequestInitApi
|
||||||
ApiRequestInitApi
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export type OnApiUpdate = (update: ApiUpdate) => void;
|
export type OnApiUpdate = (update: ApiUpdate) => void;
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import type { RefObject } from 'react';
|
import type { RefObject } from 'react';
|
||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import React, { useLayoutEffect, useRef } from '../../lib/teact/teact';
|
import React, { useLayoutEffect, useRef } from '../../lib/teact/teact';
|
||||||
|
import { addExtraClass, removeExtraClass, toggleExtraClass } from '../../lib/teact/teact-dom';
|
||||||
import { getGlobal } from '../../global';
|
import { getGlobal } from '../../global';
|
||||||
|
|
||||||
import type { GlobalState } from '../../global/types';
|
import type { GlobalState } from '../../global/types';
|
||||||
|
|
||||||
import { ANIMATION_LEVEL_MIN } from '../../config';
|
import { ANIMATION_LEVEL_MIN } from '../../config';
|
||||||
@ -36,10 +38,10 @@ export type TransitionProps = {
|
|||||||
children: React.ReactNode | ChildrenFn;
|
children: React.ReactNode | ChildrenFn;
|
||||||
};
|
};
|
||||||
|
|
||||||
const classNames = {
|
const FALLBACK_ANIMATION_END = 1000;
|
||||||
|
const CLASSES = {
|
||||||
active: 'Transition__slide--active',
|
active: 'Transition__slide--active',
|
||||||
};
|
};
|
||||||
const FALLBACK_ANIMATION_END = 1000;
|
|
||||||
|
|
||||||
const Transition: FC<TransitionProps> = ({
|
const Transition: FC<TransitionProps> = ({
|
||||||
ref,
|
ref,
|
||||||
@ -92,21 +94,21 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const container = containerRef.current!;
|
const container = containerRef.current!;
|
||||||
|
|
||||||
const childElements = container.children;
|
const childElements = container.children;
|
||||||
|
|
||||||
if (childElements.length === 1 && !activeKeyChanged) {
|
if (childElements.length === 1 && !activeKeyChanged) {
|
||||||
|
const firstChild = childElements[0] as HTMLElement;
|
||||||
if (name.startsWith('slide-optimized')) {
|
if (name.startsWith('slide-optimized')) {
|
||||||
(childElements[0] as HTMLElement).style.transition = 'none';
|
firstChild.style.transition = 'none';
|
||||||
(childElements[0] as HTMLElement).style.transform = 'translate3d(0, 0, 0)';
|
firstChild.style.transform = 'translate3d(0, 0, 0)';
|
||||||
}
|
}
|
||||||
|
|
||||||
childElements[0].classList.add(classNames.active);
|
addExtraClass(firstChild, CLASSES.active);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const childNodes = Array.from(container.childNodes);
|
const childNodes = Array.from(container.childNodes);
|
||||||
|
|
||||||
if (!activeKeyChanged || !childNodes.length) {
|
if (!activeKeyChanged || !childNodes.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -142,14 +144,16 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.classList.remove('animating');
|
removeExtraClass(container, 'animating');
|
||||||
container.classList.toggle('backwards', isBackwards);
|
toggleExtraClass(container, 'backwards', isBackwards);
|
||||||
|
|
||||||
if (name === 'none' || animationLevel === ANIMATION_LEVEL_MIN) {
|
if (name === 'none' || animationLevel === ANIMATION_LEVEL_MIN) {
|
||||||
childNodes.forEach((node, i) => {
|
childNodes.forEach((node, i) => {
|
||||||
if (node instanceof HTMLElement) {
|
if (node instanceof HTMLElement) {
|
||||||
node.classList.remove('from', 'through', 'to');
|
removeExtraClass(node, 'from');
|
||||||
node.classList.toggle(classNames.active, i === activeIndex);
|
removeExtraClass(node, 'through');
|
||||||
|
removeExtraClass(node, 'to');
|
||||||
|
toggleExtraClass(node, CLASSES.active, i === activeIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -160,19 +164,19 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
|
|
||||||
childNodes.forEach((node, i) => {
|
childNodes.forEach((node, i) => {
|
||||||
if (node instanceof HTMLElement) {
|
if (node instanceof HTMLElement) {
|
||||||
node.classList.remove(classNames.active);
|
removeExtraClass(node, CLASSES.active);
|
||||||
node.classList.toggle('from', i === prevActiveIndex);
|
|
||||||
node.classList.toggle('through', (
|
toggleExtraClass(node, 'from', i === prevActiveIndex);
|
||||||
(i > prevActiveIndex && i < activeIndex) || (i < prevActiveIndex && i > activeIndex)
|
const isThrough = (i > prevActiveIndex && i < activeIndex) || (i < prevActiveIndex && i > activeIndex);
|
||||||
));
|
toggleExtraClass(node, 'through', isThrough);
|
||||||
node.classList.toggle('to', i === activeIndex);
|
toggleExtraClass(node, 'to', i === activeIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent();
|
const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent();
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
container.classList.add('animating');
|
addExtraClass(container, 'animating');
|
||||||
|
|
||||||
onStart?.();
|
onStart?.();
|
||||||
|
|
||||||
@ -182,17 +186,20 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.classList.remove('animating', 'backwards');
|
removeExtraClass(container, 'animating');
|
||||||
|
removeExtraClass(container, 'backwards');
|
||||||
|
|
||||||
childNodes.forEach((node, i) => {
|
childNodes.forEach((node, i) => {
|
||||||
if (node instanceof HTMLElement) {
|
if (node instanceof HTMLElement) {
|
||||||
node.classList.remove('from', 'through', 'to');
|
removeExtraClass(node, 'from');
|
||||||
node.classList.toggle(classNames.active, i === activeIndex);
|
removeExtraClass(node, 'through');
|
||||||
|
removeExtraClass(node, 'to');
|
||||||
|
toggleExtraClass(node, CLASSES.active, i === activeIndex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (shouldRestoreHeight) {
|
if (shouldRestoreHeight) {
|
||||||
const activeElement = container.querySelector<HTMLDivElement>(`.${classNames.active}`);
|
const activeElement = container.querySelector<HTMLDivElement>(`.${CLASSES.active}`);
|
||||||
|
|
||||||
if (activeElement) {
|
if (activeElement) {
|
||||||
activeElement.style.height = 'auto';
|
activeElement.style.height = 'auto';
|
||||||
@ -237,7 +244,7 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (shouldRestoreHeight) {
|
if (shouldRestoreHeight) {
|
||||||
const container = containerRef.current!;
|
const container = containerRef.current!;
|
||||||
const activeElement = container.querySelector<HTMLDivElement>(`.${classNames.active}`)
|
const activeElement = container.querySelector<HTMLDivElement>(`.${CLASSES.active}`)
|
||||||
|| container.querySelector<HTMLDivElement>('.from');
|
|| container.querySelector<HTMLDivElement>('.from');
|
||||||
const clientHeight = activeElement?.clientHeight;
|
const clientHeight = activeElement?.clientHeight;
|
||||||
if (!clientHeight) {
|
if (!clientHeight) {
|
||||||
@ -250,6 +257,7 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
}
|
}
|
||||||
}, [shouldRestoreHeight, children]);
|
}, [shouldRestoreHeight, children]);
|
||||||
|
|
||||||
|
const asFastList = !renderCount;
|
||||||
const renders = rendersRef.current;
|
const renders = rendersRef.current;
|
||||||
const renderKeys = Object.keys(renderCount ? new Array(renderCount).fill(undefined) : renders).map(Number);
|
const renderKeys = Object.keys(renderCount ? new Array(renderCount).fill(undefined) : renders).map(Number);
|
||||||
const contents = renderKeys.map((key) => {
|
const contents = renderKeys.map((key) => {
|
||||||
@ -273,7 +281,7 @@ const Transition: FC<TransitionProps> = ({
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
id={id}
|
id={id}
|
||||||
className={buildClassName('Transition', className, name)}
|
className={buildClassName('Transition', className, name)}
|
||||||
teactFastList={!renderCount}
|
teactFastList={asFastList}
|
||||||
>
|
>
|
||||||
{contents}
|
{contents}
|
||||||
</div>
|
</div>
|
||||||
@ -303,11 +311,11 @@ function performSlideOptimized(
|
|||||||
if (animationLevel === ANIMATION_LEVEL_MIN) {
|
if (animationLevel === ANIMATION_LEVEL_MIN) {
|
||||||
fromSlide.style.transition = 'none';
|
fromSlide.style.transition = 'none';
|
||||||
fromSlide.style.transform = '';
|
fromSlide.style.transform = '';
|
||||||
fromSlide.classList.remove(classNames.active);
|
removeExtraClass(fromSlide, CLASSES.active);
|
||||||
|
|
||||||
toSlide.style.transition = 'none';
|
toSlide.style.transition = 'none';
|
||||||
toSlide.style.transform = 'translate3d(0, 0, 0)';
|
toSlide.style.transform = 'translate3d(0, 0, 0)';
|
||||||
toSlide.classList.add(classNames.active);
|
addExtraClass(toSlide, CLASSES.active);
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
@ -337,8 +345,8 @@ function performSlideOptimized(
|
|||||||
toSlide.style.transition = '';
|
toSlide.style.transition = '';
|
||||||
toSlide.style.transform = 'translate3d(0, 0, 0)';
|
toSlide.style.transform = 'translate3d(0, 0, 0)';
|
||||||
|
|
||||||
fromSlide.classList.remove(classNames.active);
|
removeExtraClass(fromSlide, CLASSES.active);
|
||||||
toSlide.classList.add(classNames.active);
|
addExtraClass(toSlide, CLASSES.active);
|
||||||
|
|
||||||
waitForTransitionEnd(fromSlide, () => {
|
waitForTransitionEnd(fromSlide, () => {
|
||||||
if (activeKey !== currentKeyRef.current) {
|
if (activeKey !== currentKeyRef.current) {
|
||||||
|
|||||||
@ -43,6 +43,8 @@ const MAPPED_ATTRIBUTES: { [k: string]: string } = {
|
|||||||
const INDEX_KEY_PREFIX = '__indexKey#';
|
const INDEX_KEY_PREFIX = '__indexKey#';
|
||||||
|
|
||||||
const headsByElement = new WeakMap<HTMLElement, VirtualDomHead>();
|
const headsByElement = new WeakMap<HTMLElement, VirtualDomHead>();
|
||||||
|
const extraClasses = new WeakMap<HTMLElement, Set<string>>();
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
let DEBUG_virtualTreeSize = 1;
|
let DEBUG_virtualTreeSize = 1;
|
||||||
|
|
||||||
@ -319,15 +321,18 @@ function remount(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unmountRealTree($element: VirtualElement) {
|
function unmountRealTree($element: VirtualElement) {
|
||||||
if (isComponentElement($element)) {
|
if (isComponentElement($element)) {
|
||||||
unmountComponent($element.componentInstance);
|
unmountComponent($element.componentInstance);
|
||||||
} else {
|
} else if (!isFragmentElement($element)) {
|
||||||
if (isTagElement($element)) {
|
if (isTagElement($element)) {
|
||||||
if ($element.target) {
|
const { target } = $element;
|
||||||
removeAllDelegatedListeners($element.target as HTMLElement);
|
|
||||||
|
|
||||||
if ($element.props.ref?.current === $element.target) {
|
if (target) {
|
||||||
|
extraClasses.delete(target);
|
||||||
|
removeAllDelegatedListeners(target);
|
||||||
|
|
||||||
|
if ($element.props.ref?.current === target) {
|
||||||
$element.props.ref.current = undefined;
|
$element.props.ref.current = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -629,10 +634,8 @@ function updateAttributes($current: VirtualElementTag, $new: VirtualElementTag,
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setAttribute(element: HTMLElement, key: string, value: any) {
|
function setAttribute(element: HTMLElement, key: string, value: any) {
|
||||||
// An optimization attempt
|
|
||||||
if (key === 'className') {
|
if (key === 'className') {
|
||||||
element.className = value;
|
updateClassName(element, value);
|
||||||
// An optimization attempt
|
|
||||||
} else if (key === 'value') {
|
} else if (key === 'value') {
|
||||||
const inputEl = element as HTMLInputElement;
|
const inputEl = element as HTMLInputElement;
|
||||||
|
|
||||||
@ -668,7 +671,7 @@ function setAttribute(element: HTMLElement, key: string, value: any) {
|
|||||||
|
|
||||||
function removeAttribute(element: HTMLElement, key: string, value: any) {
|
function removeAttribute(element: HTMLElement, key: string, value: any) {
|
||||||
if (key === 'className') {
|
if (key === 'className') {
|
||||||
element.className = '';
|
updateClassName(element, '');
|
||||||
} else if (key === 'value') {
|
} else if (key === 'value') {
|
||||||
(element as HTMLInputElement).value = '';
|
(element as HTMLInputElement).value = '';
|
||||||
} else if (key === 'style') {
|
} else if (key === 'style') {
|
||||||
@ -682,6 +685,55 @@ function removeAttribute(element: HTMLElement, key: string, value: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateClassName(element: HTMLElement, value: string) {
|
||||||
|
const extra = extraClasses.get(element);
|
||||||
|
if (!extra) {
|
||||||
|
element.className = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const extraArray = Array.from(extra);
|
||||||
|
if (value) {
|
||||||
|
extraArray.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
element.className = extraArray.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addExtraClass(element: HTMLElement, className: string) {
|
||||||
|
element.classList.add(className);
|
||||||
|
|
||||||
|
const classList = extraClasses.get(element);
|
||||||
|
if (classList) {
|
||||||
|
classList.add(className);
|
||||||
|
} else {
|
||||||
|
extraClasses.set(element, new Set([className]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeExtraClass(element: HTMLElement, className: string) {
|
||||||
|
element.classList.remove(className);
|
||||||
|
|
||||||
|
const classList = extraClasses.get(element);
|
||||||
|
if (classList) {
|
||||||
|
classList.delete(className);
|
||||||
|
|
||||||
|
if (!classList.size) {
|
||||||
|
extraClasses.delete(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleExtraClass(element: HTMLElement, className: string, force?: boolean) {
|
||||||
|
element.classList.toggle(className, force);
|
||||||
|
|
||||||
|
if (element.classList.contains(className)) {
|
||||||
|
addExtraClass(element, className);
|
||||||
|
} else {
|
||||||
|
removeExtraClass(element, className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
function DEBUG_addToVirtualTreeSize($current: VirtualElementParent | VirtualDomHead) {
|
function DEBUG_addToVirtualTreeSize($current: VirtualElementParent | VirtualDomHead) {
|
||||||
DEBUG_virtualTreeSize += $current.children.length;
|
DEBUG_virtualTreeSize += $current.children.length;
|
||||||
|
|||||||
@ -35,7 +35,7 @@ interface VirtualElementText {
|
|||||||
|
|
||||||
export interface VirtualElementTag {
|
export interface VirtualElementTag {
|
||||||
type: VirtualElementTypesEnum.Tag;
|
type: VirtualElementTypesEnum.Tag;
|
||||||
target?: Node;
|
target?: HTMLElement;
|
||||||
tag: string;
|
tag: string;
|
||||||
props: Props;
|
props: Props;
|
||||||
children: VirtualElementChildren;
|
children: VirtualElementChildren;
|
||||||
@ -50,7 +50,6 @@ export interface VirtualElementComponent {
|
|||||||
|
|
||||||
export interface VirtualElementFragment {
|
export interface VirtualElementFragment {
|
||||||
type: VirtualElementTypesEnum.Fragment;
|
type: VirtualElementTypesEnum.Fragment;
|
||||||
target?: Node;
|
|
||||||
children: VirtualElementChildren;
|
children: VirtualElementChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user