chore: upgrade lexical & make linting/formatting consistent with web codebase (#2144)
This commit is contained in:
@@ -6,11 +6,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import './Button.css';
|
||||
import './Button.css'
|
||||
|
||||
import {ReactNode} from 'react';
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import joinClasses from '../Utils/join-classes';
|
||||
import joinClasses from '../Utils/join-classes'
|
||||
|
||||
export default function Button({
|
||||
'data-test-id': dataTestId,
|
||||
@@ -21,28 +21,24 @@ export default function Button({
|
||||
small,
|
||||
title,
|
||||
}: {
|
||||
'data-test-id'?: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
small?: boolean;
|
||||
title?: string;
|
||||
'data-test-id'?: string
|
||||
children: ReactNode
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
onClick: () => void
|
||||
small?: boolean
|
||||
title?: string
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
className={joinClasses(
|
||||
'Button__root',
|
||||
disabled && 'Button__disabled',
|
||||
small && 'Button__small',
|
||||
className,
|
||||
)}
|
||||
className={joinClasses('Button__root', disabled && 'Button__disabled', small && 'Button__small', className)}
|
||||
onClick={onClick}
|
||||
title={title}
|
||||
aria-label={title}
|
||||
{...(dataTestId && {'data-test-id': dataTestId})}>
|
||||
{...(dataTestId && { 'data-test-id': dataTestId })}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,26 +6,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import './Dialog.css';
|
||||
import './Dialog.css'
|
||||
|
||||
import {ReactNode} from 'react';
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
type Props = Readonly<{
|
||||
'data-test-id'?: string;
|
||||
children: ReactNode;
|
||||
}>;
|
||||
'data-test-id'?: string
|
||||
children: ReactNode
|
||||
}>
|
||||
|
||||
export function DialogButtonsList({children}: Props): JSX.Element {
|
||||
return <div className="DialogButtonsList">{children}</div>;
|
||||
export function DialogButtonsList({ children }: Props): JSX.Element {
|
||||
return <div className="DialogButtonsList">{children}</div>
|
||||
}
|
||||
|
||||
export function DialogActions({
|
||||
'data-test-id': dataTestId,
|
||||
children,
|
||||
}: Props): JSX.Element {
|
||||
export function DialogActions({ 'data-test-id': dataTestId, children }: Props): JSX.Element {
|
||||
return (
|
||||
<div className="DialogActions" data-test-id={dataTestId}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,85 +6,73 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import './LinkPreview.css';
|
||||
import './LinkPreview.css'
|
||||
|
||||
import {CSSProperties, Suspense} from 'react';
|
||||
import { CSSProperties, Suspense } from 'react'
|
||||
|
||||
type Preview = {
|
||||
title: string;
|
||||
description: string;
|
||||
img: string;
|
||||
domain: string;
|
||||
} | null;
|
||||
title: string
|
||||
description: string
|
||||
img: string
|
||||
domain: string
|
||||
} | null
|
||||
|
||||
// Cached responses or running request promises
|
||||
const PREVIEW_CACHE: Record<string, Promise<Preview> | {preview: Preview}> = {};
|
||||
const PREVIEW_CACHE: Record<string, Promise<Preview> | { preview: Preview }> = {}
|
||||
|
||||
const URL_MATCHER =
|
||||
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
||||
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
|
||||
|
||||
function useSuspenseRequest(url: string) {
|
||||
let cached = PREVIEW_CACHE[url];
|
||||
let cached = PREVIEW_CACHE[url]
|
||||
|
||||
if (!url.match(URL_MATCHER)) {
|
||||
return {preview: null};
|
||||
return { preview: null }
|
||||
}
|
||||
|
||||
if (!cached) {
|
||||
cached = PREVIEW_CACHE[url] = fetch(
|
||||
`/api/link-preview?url=${encodeURI(url)}`,
|
||||
)
|
||||
cached = PREVIEW_CACHE[url] = fetch(`/api/link-preview?url=${encodeURI(url)}`)
|
||||
.then((response) => response.json())
|
||||
.then((preview) => {
|
||||
PREVIEW_CACHE[url] = preview;
|
||||
return preview;
|
||||
PREVIEW_CACHE[url] = preview
|
||||
return preview
|
||||
})
|
||||
.catch(() => {
|
||||
PREVIEW_CACHE[url] = {preview: null};
|
||||
});
|
||||
PREVIEW_CACHE[url] = { preview: null }
|
||||
})
|
||||
}
|
||||
|
||||
if (cached instanceof Promise) {
|
||||
throw cached;
|
||||
throw cached
|
||||
}
|
||||
|
||||
return cached;
|
||||
return cached
|
||||
}
|
||||
|
||||
function LinkPreviewContent({
|
||||
url,
|
||||
}: Readonly<{
|
||||
url: string;
|
||||
url: string
|
||||
}>): JSX.Element | null {
|
||||
const {preview} = useSuspenseRequest(url);
|
||||
const { preview } = useSuspenseRequest(url)
|
||||
if (preview === null) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className="LinkPreview__container">
|
||||
{preview.img && (
|
||||
<div className="LinkPreview__imageWrapper">
|
||||
<img
|
||||
src={preview.img}
|
||||
alt={preview.title}
|
||||
className="LinkPreview__image"
|
||||
/>
|
||||
<img src={preview.img} alt={preview.title} className="LinkPreview__image" />
|
||||
</div>
|
||||
)}
|
||||
{preview.domain && (
|
||||
<div className="LinkPreview__domain">{preview.domain}</div>
|
||||
)}
|
||||
{preview.title && (
|
||||
<div className="LinkPreview__title">{preview.title}</div>
|
||||
)}
|
||||
{preview.description && (
|
||||
<div className="LinkPreview__description">{preview.description}</div>
|
||||
)}
|
||||
{preview.domain && <div className="LinkPreview__domain">{preview.domain}</div>}
|
||||
{preview.title && <div className="LinkPreview__title">{preview.title}</div>}
|
||||
{preview.description && <div className="LinkPreview__description">{preview.description}</div>}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function Glimmer(props: {style: CSSProperties; index: number}): JSX.Element {
|
||||
function Glimmer(props: { style: CSSProperties; index: number }): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className="LinkPreview__glimmer"
|
||||
@@ -94,24 +82,25 @@ function Glimmer(props: {style: CSSProperties; index: number}): JSX.Element {
|
||||
...(props.style || {}),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default function LinkPreview({
|
||||
url,
|
||||
}: Readonly<{
|
||||
url: string;
|
||||
url: string
|
||||
}>): JSX.Element {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<>
|
||||
<Glimmer style={{height: '80px'}} index={0} />
|
||||
<Glimmer style={{width: '60%'}} index={1} />
|
||||
<Glimmer style={{width: '80%'}} index={2} />
|
||||
<Glimmer style={{ height: '80px' }} index={0} />
|
||||
<Glimmer style={{ width: '60%' }} index={1} />
|
||||
<Glimmer style={{ width: '80%' }} index={2} />
|
||||
</>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<LinkPreviewContent url={url} />
|
||||
</Suspense>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
border-radius: 0px;
|
||||
}
|
||||
.Modal__title {
|
||||
color:var(--sn-stylekit-foreground-color);
|
||||
color: var(--sn-stylekit-foreground-color);
|
||||
margin: 0px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid var(--sn-stylekit-border-color);
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import './Modal.css';
|
||||
import './Modal.css'
|
||||
|
||||
import {ReactNode, useEffect, useRef} from 'react';
|
||||
import {createPortal} from 'react-dom';
|
||||
import { ReactNode, useEffect, useRef } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
|
||||
function PortalImpl({
|
||||
onClose,
|
||||
@@ -17,68 +17,60 @@ function PortalImpl({
|
||||
title,
|
||||
closeOnClickOutside,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
closeOnClickOutside: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
children: ReactNode
|
||||
closeOnClickOutside: boolean
|
||||
onClose: () => void
|
||||
title: string
|
||||
}) {
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const modalRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (modalRef.current !== null) {
|
||||
modalRef.current.focus();
|
||||
modalRef.current.focus()
|
||||
}
|
||||
}, []);
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let modalOverlayElement: HTMLElement | null = null;
|
||||
let modalOverlayElement: HTMLElement | null = null
|
||||
const handler = (event: KeyboardEvent) => {
|
||||
if (event.keyCode === 27) {
|
||||
onClose();
|
||||
onClose()
|
||||
}
|
||||
};
|
||||
}
|
||||
const clickOutsideHandler = (event: MouseEvent) => {
|
||||
const target = event.target;
|
||||
if (
|
||||
modalRef.current !== null &&
|
||||
!modalRef.current.contains(target as Node) &&
|
||||
closeOnClickOutside
|
||||
) {
|
||||
onClose();
|
||||
const target = event.target
|
||||
if (modalRef.current !== null && !modalRef.current.contains(target as Node) && closeOnClickOutside) {
|
||||
onClose()
|
||||
}
|
||||
};
|
||||
}
|
||||
if (modalRef.current !== null) {
|
||||
modalOverlayElement = modalRef.current?.parentElement;
|
||||
modalOverlayElement = modalRef.current?.parentElement
|
||||
if (modalOverlayElement !== null) {
|
||||
modalOverlayElement?.addEventListener('click', clickOutsideHandler);
|
||||
modalOverlayElement?.addEventListener('click', clickOutsideHandler)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handler);
|
||||
window.addEventListener('keydown', handler)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handler);
|
||||
window.removeEventListener('keydown', handler)
|
||||
if (modalOverlayElement !== null) {
|
||||
modalOverlayElement?.removeEventListener('click', clickOutsideHandler);
|
||||
modalOverlayElement?.removeEventListener('click', clickOutsideHandler)
|
||||
}
|
||||
};
|
||||
}, [closeOnClickOutside, onClose]);
|
||||
}
|
||||
}, [closeOnClickOutside, onClose])
|
||||
|
||||
return (
|
||||
<div className="Modal__overlay" role="dialog">
|
||||
<div className="Modal__modal" tabIndex={-1} ref={modalRef}>
|
||||
<h2 className="Modal__title">{title}</h2>
|
||||
<button
|
||||
className="Modal__closeButton"
|
||||
aria-label="Close modal"
|
||||
type="button"
|
||||
onClick={onClose}>
|
||||
<button className="Modal__closeButton" aria-label="Close modal" type="button" onClick={onClose}>
|
||||
✕
|
||||
</button>
|
||||
<div className="Modal__content">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default function Modal({
|
||||
@@ -87,18 +79,15 @@ export default function Modal({
|
||||
title,
|
||||
closeOnClickOutside = false,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
closeOnClickOutside?: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
children: ReactNode
|
||||
closeOnClickOutside?: boolean
|
||||
onClose: () => void
|
||||
title: string
|
||||
}): JSX.Element {
|
||||
return createPortal(
|
||||
<PortalImpl
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
closeOnClickOutside={closeOnClickOutside}>
|
||||
<PortalImpl onClose={onClose} title={title} closeOnClickOutside={closeOnClickOutside}>
|
||||
{children}
|
||||
</PortalImpl>,
|
||||
document.body,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import './Input.css';
|
||||
import './Input.css'
|
||||
|
||||
type Props = Readonly<{
|
||||
'data-test-id'?: string;
|
||||
label: string;
|
||||
onChange: (val: string) => void;
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
}>;
|
||||
'data-test-id'?: string
|
||||
label: string
|
||||
onChange: (val: string) => void
|
||||
placeholder?: string
|
||||
value: string
|
||||
}>
|
||||
|
||||
export default function TextInput({
|
||||
label,
|
||||
@@ -32,10 +32,10 @@ export default function TextInput({
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
onChange(e.target.value)
|
||||
}}
|
||||
data-test-id={dataTestId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user