Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d95baa70f | ||
|
|
050bce163a | ||
|
|
68b84683d0 |
@@ -0,0 +1,11 @@
|
|||||||
|
global.ResizeObserver = class ResizeObserver {
|
||||||
|
observe() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
unobserve() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
disconnect() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -76,11 +76,11 @@ export function useDropdown(disableClosing?: boolean) {
|
|||||||
|
|
||||||
handleChangeIframeSize(dropdownElement);
|
handleChangeIframeSize(dropdownElement);
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new ResizeObserver(() => {
|
||||||
handleChangeIframeSize(dropdownElement);
|
handleChangeIframeSize(dropdownElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
observer.observe(dropdownElement, { attributes: true, childList: true, subtree: true });
|
observer.observe(dropdownElement);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.body.style.removeProperty('min-height');
|
document.body.style.removeProperty('min-height');
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
border: 0;
|
border: 0;
|
||||||
resize: none;
|
resize: none;
|
||||||
|
overflow: hidden; /* prevent scrollbar from appearing */
|
||||||
backface-visibility: hidden; /* let's try to fix blinking in Safari */
|
backface-visibility: hidden; /* let's try to fix blinking in Safari */
|
||||||
transform: translateZ(0); /* let's try to fix blinking in Safari, again */
|
transform: translateZ(0); /* let's try to fix blinking in Safari, again */
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { h, Component, Fragment } from 'preact';
|
import { h, Component, Fragment } from 'preact';
|
||||||
import { useEffect, useRef } from 'preact/hooks';
|
import { useEffect } from 'preact/hooks';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import b from 'bem-react-helper';
|
import b from 'bem-react-helper';
|
||||||
import { IntlShape, useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { IntlShape, useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||||
@@ -42,6 +42,13 @@ import { setCollapse } from 'store/thread/actions';
|
|||||||
|
|
||||||
import styles from './root.module.css';
|
import styles from './root.module.css';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends size of the iframe to parent window
|
||||||
|
*/
|
||||||
|
export function updateIframeHeight() {
|
||||||
|
postMessageToParent({ height: document.body.offsetHeight });
|
||||||
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: StoreState) => ({
|
const mapStateToProps = (state: StoreState) => ({
|
||||||
sort: state.comments.sort,
|
sort: state.comments.sort,
|
||||||
isCommentsLoading: state.comments.isFetching,
|
isCommentsLoading: state.comments.isFetching,
|
||||||
@@ -287,10 +294,6 @@ export class Root extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateIframeHeight() {
|
|
||||||
postMessageToParent({ height: document.body.offsetHeight });
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CommentsProps {
|
interface CommentsProps {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
topComments: string[];
|
topComments: string[];
|
||||||
@@ -298,33 +301,12 @@ interface CommentsProps {
|
|||||||
showMore(): void;
|
showMore(): void;
|
||||||
}
|
}
|
||||||
function Comments({ isLoading, topComments, commentsShown, showMore }: CommentsProps) {
|
function Comments({ isLoading, topComments, commentsShown, showMore }: CommentsProps) {
|
||||||
const rootRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!rootRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateIframeHeight();
|
|
||||||
// TODO: throttle updates
|
|
||||||
const observer = new MutationObserver(() => {
|
|
||||||
// a hacky way to force iframe height update when new image is rendered and loaded
|
|
||||||
rootRef.current?.querySelectorAll('img').forEach((img) => {
|
|
||||||
img.addEventListener('load', updateIframeHeight);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(rootRef.current, { attributes: true, childList: true, subtree: true });
|
|
||||||
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const renderComments =
|
const renderComments =
|
||||||
IS_MOBILE && commentsShown < topComments.length ? topComments.slice(0, commentsShown) : topComments;
|
IS_MOBILE && commentsShown < topComments.length ? topComments.slice(0, commentsShown) : topComments;
|
||||||
const isShowMoreButtonVisible = IS_MOBILE && commentsShown < topComments.length;
|
const isShowMoreButtonVisible = IS_MOBILE && commentsShown < topComments.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="root__threads" role="list" ref={rootRef}>
|
<div className="root__threads" role="list">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Preloader className="root__preloader" />
|
<Preloader className="root__preloader" />
|
||||||
) : (
|
) : (
|
||||||
@@ -362,6 +344,14 @@ export function ConnectedRoot() {
|
|||||||
const props = useSelector(mapStateToProps);
|
const props = useSelector(mapStateToProps);
|
||||||
const actions = useActions(boundActions);
|
const actions = useActions(boundActions);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new ResizeObserver(updateIframeHeight);
|
||||||
|
|
||||||
|
updateIframeHeight();
|
||||||
|
observer.observe(document.body);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!window.remark_config) {
|
if (!window.remark_config) {
|
||||||
throw new Error('Remark42: Config object is undefined');
|
throw new Error('Remark42: Config object is undefined');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { h, JSX } from 'preact';
|
|||||||
import { forwardRef } from 'preact/compat';
|
import { forwardRef } from 'preact/compat';
|
||||||
import { useEffect, useRef } from 'preact/hooks';
|
import { useEffect, useRef } from 'preact/hooks';
|
||||||
|
|
||||||
function autoResize(textarea: HTMLTextAreaElement) {
|
function autoResize(textarea: HTMLTextAreaElement, onResize?: () => void) {
|
||||||
textarea.style.height = '';
|
textarea.style.height = '';
|
||||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = Omit<JSX.HTMLAttributes<HTMLTextAreaElement>, 'onInput'> & {
|
type Props = Omit<JSX.HTMLAttributes<HTMLTextAreaElement>, 'onInput'> & {
|
||||||
onInput?: (evt: JSX.TargetedEvent<HTMLTextAreaElement, Event>) => void;
|
onInput?(evt: JSX.TargetedEvent<HTMLTextAreaElement, Event>): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TextareaAutosize = forwardRef<HTMLTextAreaElement, Props>(({ onInput, value, ...props }, externalRef) => {
|
export const TextareaAutosize = forwardRef<HTMLTextAreaElement, Props>(({ onInput, value, ...props }, externalRef) => {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const config: Config = {
|
|||||||
setupFilesAfterEnv: [
|
setupFilesAfterEnv: [
|
||||||
'<rootDir>/app/__mocks__/fetch.ts',
|
'<rootDir>/app/__mocks__/fetch.ts',
|
||||||
'<rootDir>/app/__mocks__/localstorage.ts',
|
'<rootDir>/app/__mocks__/localstorage.ts',
|
||||||
|
'<rootDir>/app/__mocks__/resize-observer.ts',
|
||||||
'<rootDir>/app/__stubs__/remark-config.ts',
|
'<rootDir>/app/__stubs__/remark-config.ts',
|
||||||
'<rootDir>/app/__stubs__/static-config.ts',
|
'<rootDir>/app/__stubs__/static-config.ts',
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user