Replace reakit/Composite with react-roving-tabindex (#6985)

* fix: replace reakit composite with react-roving-tabindex

* fix: touch points

* fix: focus stuck at first list item

* fix: document history navigation

* fix: remove ununsed ListItem components

* fix: keyboard navigation in recent search list

* fix: updated lib
This commit is contained in:
Apoorv Mishra
2024-06-13 18:45:44 +05:30
committed by GitHub
parent 20b1766e8d
commit 23c8adc5d1
19 changed files with 219 additions and 329 deletions

View File

@@ -1,13 +1,9 @@
import { RovingTabIndexProvider } from "@getoutline/react-roving-tabindex";
import { observer } from "mobx-react";
import * as React from "react";
import {
useCompositeState,
Composite,
CompositeStateReturn,
} from "reakit/Composite";
type Props = React.HTMLAttributes<HTMLDivElement> & {
children: (composite: CompositeStateReturn) => React.ReactNode;
children: () => React.ReactNode;
onEscape?: (ev: React.KeyboardEvent<HTMLDivElement>) => void;
};
@@ -15,40 +11,36 @@ function ArrowKeyNavigation(
{ children, onEscape, ...rest }: Props,
ref: React.RefObject<HTMLDivElement>
) {
const composite = useCompositeState();
const handleKeyDown = React.useCallback(
(ev) => {
(ev: React.KeyboardEvent<HTMLDivElement>) => {
if (onEscape) {
if (ev.nativeEvent.isComposing) {
return;
}
if (ev.key === "Escape") {
ev.preventDefault();
onEscape(ev);
}
if (
ev.key === "ArrowUp" &&
composite.currentId === composite.items[0].id
// If the first item is focused and the user presses ArrowUp
ev.currentTarget.firstElementChild === document.activeElement
) {
onEscape(ev);
}
}
},
[composite.currentId, composite.items, onEscape]
[onEscape]
);
return (
<Composite
{...rest}
{...composite}
onKeyDown={handleKeyDown}
role="menu"
ref={ref}
>
{children(composite)}
</Composite>
<RovingTabIndexProvider options={{ focusOnClick: true, direction: "both" }}>
<div {...rest} onKeyDown={handleKeyDown} ref={ref}>
{children()}
</div>
</RovingTabIndexProvider>
);
}

View File

@@ -1,8 +1,11 @@
import {
useFocusEffect,
useRovingTabIndex,
} from "@getoutline/react-roving-tabindex";
import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { CompositeStateReturn, CompositeItem } from "reakit/Composite";
import styled, { css } from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { s } from "@shared/styles";
@@ -32,7 +35,7 @@ type Props = {
showPin?: boolean;
showDraft?: boolean;
showTemplate?: boolean;
} & CompositeStateReturn;
};
const SEARCH_RESULT_REGEX = /<b\b[^>]*>(.*?)<\/b>/gi;
@@ -49,6 +52,15 @@ function DocumentListItem(
const user = useCurrentUser();
const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
let itemRef: React.Ref<HTMLAnchorElement> =
React.useRef<HTMLAnchorElement>(null);
if (ref) {
itemRef = ref;
}
const { focused, ...rovingTabIndex } = useRovingTabIndex(itemRef, false);
useFocusEffect(focused, itemRef);
const {
document,
showParentDocuments,
@@ -68,9 +80,8 @@ function DocumentListItem(
!document.isDraft && !document.isArchived && !document.isTemplate;
return (
<CompositeItem
as={DocumentLink}
ref={ref}
<DocumentLink
ref={itemRef}
dir={document.dir}
role="menuitem"
$isStarred={document.isStarred}
@@ -82,6 +93,7 @@ function DocumentListItem(
},
}}
{...rest}
{...rovingTabIndex}
>
<Content>
<Heading dir={document.dir}>
@@ -142,7 +154,7 @@ function DocumentListItem(
modal={false}
/>
</Actions>
</CompositeItem>
</DocumentLink>
);
}

View File

@@ -11,16 +11,12 @@ import {
import * as React from "react";
import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom";
import { CompositeStateReturn } from "reakit/Composite";
import styled, { css } from "styled-components";
import { s } from "@shared/styles";
import Document from "~/models/Document";
import Event from "~/models/Event";
import Avatar from "~/components/Avatar";
import CompositeItem, {
Props as ItemProps,
} from "~/components/List/CompositeItem";
import Item, { Actions } from "~/components/List/Item";
import Item, { Actions, Props as ItemProps } from "~/components/List/Item";
import Time from "~/components/Time";
import useStores from "~/hooks/useStores";
import RevisionMenu from "~/menus/RevisionMenu";
@@ -32,7 +28,7 @@ type Props = {
document: Document;
event: Event;
latest?: boolean;
} & CompositeStateReturn;
};
const EventListItem = ({ event, latest, document, ...rest }: Props) => {
const { t } = useTranslation();
@@ -176,11 +172,7 @@ const BaseItem = React.forwardRef(function _BaseItem(
{ to, ...rest }: ItemProps,
ref?: React.Ref<HTMLAnchorElement>
) {
if (to) {
return <CompositeListItem to={to} ref={ref} {...rest} />;
}
return <ListItem ref={ref} {...rest} />;
return <ListItem to={to} ref={ref} {...rest} />;
});
const Subtitle = styled.span`
@@ -240,8 +232,4 @@ const ListItem = styled(Item)`
${ItemStyle}
`;
const CompositeListItem = styled(CompositeItem)`
${ItemStyle}
`;
export default observer(EventListItem);

View File

@@ -1,17 +0,0 @@
import * as React from "react";
import {
CompositeStateReturn,
CompositeItem as BaseCompositeItem,
} from "reakit/Composite";
import Item, { Props as ItemProps } from "./Item";
export type Props = ItemProps & CompositeStateReturn;
function CompositeItem(
{ to, ...rest }: Props,
ref?: React.Ref<HTMLAnchorElement>
) {
return <BaseCompositeItem as={Item} to={to} {...rest} ref={ref} />;
}
export default React.forwardRef(CompositeItem);

View File

@@ -1,3 +1,7 @@
import {
useFocusEffect,
useRovingTabIndex,
} from "@getoutline/react-roving-tabindex";
import { LocationDescriptor } from "history";
import * as React from "react";
import styled, { useTheme } from "styled-components";
@@ -33,6 +37,18 @@ const ListItem = (
const theme = useTheme();
const compact = !subtitle;
let itemRef: React.Ref<HTMLAnchorElement> =
React.useRef<HTMLAnchorElement>(null);
if (ref) {
itemRef = ref;
}
const { focused, ...rovingTabIndex } = useRovingTabIndex(
itemRef as React.RefObject<HTMLAnchorElement>,
to ? false : true
);
useFocusEffect(focused, itemRef as React.RefObject<HTMLAnchorElement>);
const content = (selected: boolean) => (
<>
{image && <Image>{image}</Image>}
@@ -59,13 +75,20 @@ const ListItem = (
if (to) {
return (
<Wrapper
ref={ref}
ref={itemRef}
$border={border}
$small={small}
activeStyle={{
background: theme.accent,
}}
{...rest}
{...rovingTabIndex}
onClick={(ev) => {
if (rest.onClick) {
rest.onClick(ev);
}
rovingTabIndex.onClick(ev);
}}
as={NavLink}
to={to}
>
@@ -75,7 +98,7 @@ const ListItem = (
}
return (
<Wrapper ref={ref} $border={border} $small={small} {...rest}>
<Wrapper ref={itemRef} $border={border} $small={small} {...rest}>
{content(false)}
</Wrapper>
);

View File

@@ -42,7 +42,7 @@ const PaginatedDocumentList = React.memo<Props>(function PaginatedDocumentList({
fetch={fetch}
options={options}
renderError={(props) => <Error {...props} />}
renderItem={(item: Document, _index, compositeProps) => (
renderItem={(item: Document, _index) => (
<DocumentListItem
key={item.id}
document={item}
@@ -52,7 +52,6 @@ const PaginatedDocumentList = React.memo<Props>(function PaginatedDocumentList({
showPublished={showPublished}
showTemplate={showTemplate}
showDraft={showDraft}
{...compositeProps}
/>
)}
{...rest}

View File

@@ -30,13 +30,12 @@ const PaginatedEventList = React.memo<Props>(function PaginatedEventList({
heading={heading}
fetch={fetch}
options={options}
renderItem={(item: Event, index, compositeProps) => (
renderItem={(item: Event, index) => (
<EventListItem
key={item.id}
event={item}
document={document}
latest={index === 0}
{...compositeProps}
/>
)}
renderHeading={(name) => <Heading>{name}</Heading>}

View File

@@ -4,7 +4,6 @@ import { observer } from "mobx-react";
import * as React from "react";
import { withTranslation, WithTranslation } from "react-i18next";
import { Waypoint } from "react-waypoint";
import { CompositeStateReturn } from "reakit/Composite";
import { Pagination } from "@shared/constants";
import RootStore from "~/stores/RootStore";
import ArrowKeyNavigation from "~/components/ArrowKeyNavigation";
@@ -30,11 +29,7 @@ type Props<T> = WithTranslation &
loading?: React.ReactElement;
items?: T[];
className?: string;
renderItem: (
item: T,
index: number,
compositeProps: CompositeStateReturn
) => React.ReactNode;
renderItem: (item: T, index: number) => React.ReactNode;
renderError?: (options: {
error: Error;
retry: () => void;
@@ -194,10 +189,10 @@ class PaginatedList<T extends PaginatedItem> extends React.Component<Props<T>> {
onEscape={onEscape}
className={this.props.className}
>
{(composite: CompositeStateReturn) => {
{() => {
let previousHeading = "";
return items.slice(0, this.renderCount).map((item, index) => {
const children = this.props.renderItem(item, index, composite);
const children = this.props.renderItem(item, index);
// If there is no renderHeading method passed then no date
// headings are rendered

View File

@@ -1,7 +1,10 @@
import {
useFocusEffect,
useRovingTabIndex,
} from "@getoutline/react-roving-tabindex";
import { observer } from "mobx-react";
import * as React from "react";
import { Link } from "react-router-dom";
import { CompositeItem } from "reakit/Composite";
import styled, { css } from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { s, ellipsis } from "@shared/styles";
@@ -34,10 +37,18 @@ function DocumentListItem(
) {
const { document, highlight, context, shareId, ...rest } = props;
let itemRef: React.Ref<HTMLAnchorElement> =
React.useRef<HTMLAnchorElement>(null);
if (ref) {
itemRef = ref;
}
const { focused, ...rovingTabIndex } = useRovingTabIndex(itemRef, false);
useFocusEffect(focused, itemRef);
return (
<CompositeItem
as={DocumentLink}
ref={ref}
<DocumentLink
ref={itemRef}
dir={document.dir}
to={{
pathname: shareId
@@ -48,6 +59,13 @@ function DocumentListItem(
},
}}
{...rest}
{...rovingTabIndex}
onClick={(ev) => {
if (rest.onClick) {
rest.onClick(ev);
}
rovingTabIndex.onClick(ev);
}}
>
<Content>
<Heading dir={document.dir}>
@@ -66,7 +84,7 @@ function DocumentListItem(
/>
}
</Content>
</CompositeItem>
</DocumentLink>
);
}

View File

@@ -206,7 +206,7 @@ function SearchPopover({ shareId }: Props) {
<NoResults>{t("No results for {{query}}", { query })}</NoResults>
}
loading={<PlaceholderList count={3} header={{ height: 20 }} />}
renderItem={(item: SearchResult, index, compositeProps) => (
renderItem={(item: SearchResult, index) => (
<SearchListItem
key={item.document.id}
shareId={shareId}
@@ -215,7 +215,6 @@ function SearchPopover({ shareId }: Props) {
context={item.context}
highlight={cachedQuery}
onClick={handleSearchItemClick}
{...compositeProps}
/>
)}
/>

View File

@@ -1,49 +0,0 @@
import { observer } from "mobx-react";
import { PlusIcon } from "outline-icons";
import * as React from "react";
import { Trans, useTranslation } from "react-i18next";
import User from "~/models/User";
import Avatar from "~/components/Avatar";
import Badge from "~/components/Badge";
import Button from "~/components/Button";
import ListItem from "~/components/List/Item";
import Time from "~/components/Time";
type Props = {
user: User;
canEdit: boolean;
onAdd: () => void;
};
const UserListItem = ({ user, onAdd, canEdit }: Props) => {
const { t } = useTranslation();
return (
<ListItem
title={user.name}
image={<Avatar model={user} size={32} />}
subtitle={
<>
{user.lastActiveAt ? (
<Trans>
Active <Time dateTime={user.lastActiveAt} /> ago
</Trans>
) : (
t("Never signed in")
)}
{user.isInvited && <Badge>{t("Invited")}</Badge>}
{user.isAdmin && <Badge primary={user.isAdmin}>{t("Admin")}</Badge>}
</>
}
actions={
canEdit ? (
<Button type="button" onClick={onAdd} icon={<PlusIcon />} neutral>
{t("Add")}
</Button>
) : undefined
}
/>
);
};
export default observer(UserListItem);

View File

@@ -53,8 +53,8 @@ function Search(props: Props) {
// refs
const searchInputRef = React.useRef<HTMLInputElement | null>(null);
const resultListCompositeRef = React.useRef<HTMLDivElement | null>(null);
const recentSearchesCompositeRef = React.useRef<HTMLDivElement | null>(null);
const resultListRef = React.useRef<HTMLDivElement | null>(null);
const recentSearchesRef = React.useRef<HTMLDivElement | null>(null);
// filters
const query = decodeURIComponentSafe(routeMatch.params.term ?? "");
@@ -178,19 +178,9 @@ function Search(props: Props) {
}
}
const firstResultItem = (
resultListCompositeRef.current?.querySelectorAll(
"[href]"
) as NodeListOf<HTMLAnchorElement>
)?.[0];
const firstItem = (resultListRef.current?.firstElementChild ??
recentSearchesRef.current?.firstElementChild) as HTMLAnchorElement;
const firstRecentSearchItem = (
recentSearchesCompositeRef.current?.querySelectorAll(
"li > [href]"
) as NodeListOf<HTMLAnchorElement>
)?.[0];
const firstItem = firstResultItem ?? firstRecentSearchItem;
firstItem?.focus();
}
};
@@ -277,11 +267,11 @@ function Search(props: Props) {
)}
<ResultList column>
<StyledArrowKeyNavigation
ref={resultListCompositeRef}
ref={resultListRef}
onEscape={handleEscape}
aria-label={t("Search Results")}
>
{(compositeProps) =>
{() =>
data?.length
? data.map((result) => (
<DocumentListItem
@@ -291,7 +281,6 @@ function Search(props: Props) {
context={result.context}
showCollection
showTemplate
{...compositeProps}
/>
))
: null
@@ -305,10 +294,7 @@ function Search(props: Props) {
</ResultList>
</>
) : documentId || collectionId ? null : (
<RecentSearches
ref={recentSearchesCompositeRef}
onEscape={handleEscape}
/>
<RecentSearches ref={recentSearchesRef} onEscape={handleEscape} />
)}
</ResultsWrapper>
</Scene>

View File

@@ -0,0 +1,92 @@
import {
useFocusEffect,
useRovingTabIndex,
} from "@getoutline/react-roving-tabindex";
import { CloseIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import styled from "styled-components";
import { s } from "@shared/styles";
import type SearchQuery from "~/models/SearchQuery";
import NudeButton from "~/components/NudeButton";
import Tooltip from "~/components/Tooltip";
import { hover } from "~/styles";
import { searchPath } from "~/utils/routeHelpers";
type Props = {
searchQuery: SearchQuery;
};
function RecentSearchListItem({ searchQuery }: Props) {
const { t } = useTranslation();
const ref = React.useRef<HTMLAnchorElement>(null);
const { focused, ...rovingTabIndex } = useRovingTabIndex(ref, false);
useFocusEffect(focused, ref);
return (
<RecentSearch
to={searchPath(searchQuery.query)}
ref={ref}
{...rovingTabIndex}
>
{searchQuery.query}
<Tooltip content={t("Remove search")} delay={150}>
<RemoveButton
aria-label={t("Remove search")}
onClick={async (ev) => {
ev.preventDefault();
await searchQuery.delete();
}}
>
<CloseIcon />
</RemoveButton>
</Tooltip>
</RecentSearch>
);
}
const RemoveButton = styled(NudeButton)`
opacity: 0;
color: ${s("textTertiary")};
&:hover {
color: ${s("text")};
}
`;
const RecentSearch = styled(Link)`
display: flex;
justify-content: space-between;
color: ${s("textSecondary")};
cursor: var(--pointer);
padding: 1px 4px;
border-radius: 4px;
position: relative;
font-size: 14px;
&:before {
content: "·";
color: ${s("textTertiary")};
position: absolute;
left: -8px;
}
&:focus-visible {
outline: none;
}
&:focus,
&:${hover} {
color: ${s("text")};
background: ${s("secondaryBackground")};
${RemoveButton} {
opacity: 1;
}
}
`;
export default RecentSearchListItem;

View File

@@ -1,18 +1,12 @@
import { observer } from "mobx-react";
import { CloseIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { CompositeItem } from "reakit/Composite";
import styled from "styled-components";
import { s } from "@shared/styles";
import ArrowKeyNavigation from "~/components/ArrowKeyNavigation";
import Fade from "~/components/Fade";
import NudeButton from "~/components/NudeButton";
import Tooltip from "~/components/Tooltip";
import useStores from "~/hooks/useStores";
import { hover } from "~/styles";
import { searchPath } from "~/utils/routeHelpers";
import RecentSearchListItem from "./RecentSearchListItem";
type Props = {
/** Callback when the Escape key is pressed while navigating the list */
@@ -36,39 +30,20 @@ function RecentSearches(
const content = searches.recent.length ? (
<>
<Heading>{t("Recent searches")}</Heading>
<List>
<ArrowKeyNavigation
ref={ref}
onEscape={onEscape}
aria-label={t("Search Results")}
>
{(compositeProps) =>
searches.recent.map((searchQuery) => (
<ListItem key={searchQuery.id}>
<CompositeItem
as={RecentSearch}
to={searchPath(searchQuery.query)}
role="menuitem"
{...compositeProps}
>
{searchQuery.query}
<Tooltip content={t("Remove search")} delay={150}>
<RemoveButton
aria-label={t("Remove search")}
onClick={async (ev) => {
ev.preventDefault();
await searchQuery.delete();
}}
>
<CloseIcon />
</RemoveButton>
</Tooltip>
</CompositeItem>
</ListItem>
))
}
</ArrowKeyNavigation>
</List>
<StyledArrowKeyNavigation
ref={ref}
onEscape={onEscape}
aria-label={t("Recent searches")}
>
{() =>
searches.recent.map((searchQuery) => (
<RecentSearchListItem
key={searchQuery.id}
searchQuery={searchQuery}
/>
))
}
</StyledArrowKeyNavigation>
</>
) : null;
@@ -83,55 +58,9 @@ const Heading = styled.h2`
margin-bottom: 0;
`;
const List = styled.ol`
const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)`
padding: 0;
margin-top: 8px;
`;
const ListItem = styled.li`
font-size: 14px;
padding: 0;
list-style: none;
position: relative;
&:before {
content: "·";
color: ${s("textTertiary")};
position: absolute;
left: -8px;
}
`;
const RemoveButton = styled(NudeButton)`
opacity: 0;
color: ${s("textTertiary")};
&:hover {
color: ${s("text")};
}
`;
const RecentSearch = styled(Link)`
display: flex;
justify-content: space-between;
color: ${s("textSecondary")};
cursor: var(--pointer);
padding: 1px 4px;
border-radius: 4px;
&:focus-visible {
outline: none;
}
&:focus,
&: ${hover} {
color: ${s("text")};
background: ${s("secondaryBackground")};
${RemoveButton} {
opacity: 1;
}
}
`;
export default observer(React.forwardRef(RecentSearches));

View File

@@ -1,39 +0,0 @@
import * as React from "react";
import { useTranslation } from "react-i18next";
import Share from "~/models/Share";
import ListItem from "~/components/List/Item";
import Time from "~/components/Time";
import ShareMenu from "~/menus/ShareMenu";
type Props = {
share: Share;
};
const ShareListItem = ({ share }: Props) => {
const { t } = useTranslation();
const { lastAccessedAt } = share;
return (
<ListItem
title={share.documentTitle}
subtitle={
<>
{t("Shared")} <Time dateTime={share.createdAt} addSuffix />{" "}
{t("by {{ name }}", {
name: share.createdBy.name,
})}{" "}
{lastAccessedAt && (
<>
{" "}
&middot; {t("Last accessed")}{" "}
<Time dateTime={lastAccessedAt} addSuffix />
</>
)}
</>
}
actions={<ShareMenu share={share} />}
/>
);
};
export default ShareListItem;

View File

@@ -1,50 +0,0 @@
import { observer } from "mobx-react";
import * as React from "react";
import { Trans, useTranslation } from "react-i18next";
import styled from "styled-components";
import User from "~/models/User";
import Avatar from "~/components/Avatar";
import Badge from "~/components/Badge";
import ListItem from "~/components/List/Item";
import Time from "~/components/Time";
import UserMenu from "~/menus/UserMenu";
type Props = {
user: User;
showMenu: boolean;
};
const UserListItem = ({ user, showMenu }: Props) => {
const { t } = useTranslation();
return (
<ListItem
title={<Title>{user.name}</Title>}
image={<Avatar model={user} size={32} />}
subtitle={
<>
{user.email ? `${user.email} · ` : undefined}
{user.lastActiveAt ? (
<Trans>
Active <Time dateTime={user.lastActiveAt} /> ago
</Trans>
) : (
t("Invited")
)}
{user.isAdmin && <Badge primary={user.isAdmin}>{t("Admin")}</Badge>}
{user.isSuspended && <Badge>{t("Suspended")}</Badge>}
</>
}
actions={showMenu ? <UserMenu user={user} /> : undefined}
/>
);
};
const Title = styled.span`
&:hover {
text-decoration: underline;
cursor: var(--pointer);
}
`;
export default observer(UserListItem);

View File

@@ -70,6 +70,7 @@
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@getoutline/react-roving-tabindex": "^3.2.2",
"@getoutline/y-prosemirror": "^1.0.18",
"@hocuspocus/extension-throttle": "1.1.2",
"@hocuspocus/provider": "1.1.2",

View File

@@ -749,13 +749,13 @@
"Past year": "Past year",
"Remove document filter": "Remove document filter",
"Any status": "Any status",
"Search Results": "Search Results",
"Remove search": "Remove search",
"Any author": "Any author",
"Author": "Author",
"We were unable to find the page youre looking for.": "We were unable to find the page youre looking for.",
"Search titles only": "Search titles only",
"No documents found for your search filters.": "No documents found for your search filters.",
"Search Results": "Search Results",
"API Keys": "API Keys",
"Create personal API keys to authenticate with the API and programatically control\n your workspace's data. API keys have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.": "Create personal API keys to authenticate with the API and programatically control\n your workspace's data. API keys have the same permissions as your user account.\n For more details see the <em>developer documentation</em>.",
"Active": "Active",
@@ -799,11 +799,9 @@
"Drag and drop the zip file from Notion's HTML export option, or click to upload": "Drag and drop the zip file from Notion's HTML export option, or click to upload",
"Last active": "Last active",
"Guest": "Guest",
"Shared": "Shared",
"by {{ name }}": "by {{ name }}",
"Last accessed": "Last accessed",
"Shared by": "Shared by",
"Date shared": "Date shared",
"Last accessed": "Last accessed",
"Domain": "Domain",
"Views": "Views",
"All roles": "All roles",

View File

@@ -2776,6 +2776,13 @@
dependencies:
prop-types "^15.8.1"
"@getoutline/react-roving-tabindex@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@getoutline/react-roving-tabindex/-/react-roving-tabindex-3.2.2.tgz#a3d957b0aa298d8c601a02a575cbfb263dcd940e"
integrity sha512-K8uk2BQpngOTrJUMvw/Ai3zOmKNkEo2wmjnkfgHrpW6eOgzTh3VCDOLdqCMNBNqFlNwJudpVRD6EuXNO77cQ0w==
dependencies:
warning "^4.0.3"
"@getoutline/y-prosemirror@^1.0.18":
version "1.0.18"
resolved "https://registry.yarnpkg.com/@getoutline/y-prosemirror/-/y-prosemirror-1.0.18.tgz#17245c0362d30adb85131c86fb9a59358884b234"
@@ -15380,6 +15387,13 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
dependencies:
loose-envify "^1.0.0"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"