Skip to content

Ontology nav fix #1083 #1089

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion browser/data-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"@types/react-pdf": "^7.0.0",
"@types/react-window": "^1.8.8",
"@vitejs/plugin-react": "^4.3.4",
"babel-plugin-react-compiler": "19.0.0-beta-21e868a-20250216",
"babel-plugin-react-compiler": "19.1.0-rc.2",
"babel-plugin-styled-components": "^2.1.4",
"csstype": "^3.1.3",
"gh-pages": "^5.0.0",
Expand Down
7 changes: 4 additions & 3 deletions browser/data-browser/src/routes/RootRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ export const rootRoute = createRootRoute({
});

const TopRouteComponent: React.FC = () => {
const { href } = useLocation();
const { pathname, searchStr } = useLocation();

// We need to combine origin with tanstack's href because tanstack does not include the origin in the href but the normal window.location.href is not reactive.
const subject = window.location.origin + href;
// We want the origin together with the path and search string but not the hash.
// We use the useLocation hook to get the pathname and searchStr because the window.location is not reactive.
const subject = window.location.origin + pathname + searchStr;

// Remove trailing slash from subject
const cleanedSubject = subject.endsWith('/') ? subject.slice(0, -1) : subject;
Expand Down
9 changes: 5 additions & 4 deletions browser/data-browser/src/views/Card/ResourceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ interface ResourceCardProps extends CardViewPropsBase {
* (shortname) is rendered prominently at the top.
*/
function ResourceCard(
props: ResourceCardProps & JSX.IntrinsicElements['div'],
props: ResourceCardProps &
JSX.IntrinsicElements['div'] & { className?: string },
): JSX.Element {
const { subject, initialInView } = props;
const { initialInView, className, ...rest } = props;
const [isShown, setIsShown] = useState(false);
// The (more expensive) ResourceCardInner is only rendered when the component has been in View
const { ref, inView } = useInView({
Expand All @@ -56,13 +57,13 @@ function ResourceCard(

return (
<Suspense>
<Card ref={ref} {...props} about={subject}>
<Card {...rest} ref={ref} about={props.subject} className={className}>
{isShown ? (
<ResourceCardInner {...props} />
) : (
<>
<h2>
<AtomicLink subject={subject}>{subject}</AtomicLink>
<AtomicLink subject={props.subject}>{props.subject}</AtomicLink>
</h2>
<p>Resource is loading...</p>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { core, useArray, useResource, useString } from '@tomic/react';

import { Card } from '../../../components/Card';
import { PropertyLineRead } from '../Property/PropertyLineRead';
import { styled } from 'styled-components';
import { FaCube } from 'react-icons/fa';
import { Column, Row } from '../../../components/Row';
import Markdown from '../../../components/datatypes/Markdown';
import { AtomicLink } from '../../../components/AtomicLink';
import { toAnchorId } from '../../../helpers/toAnchorId';
import { ViewTransitionProps } from '../../../helpers/ViewTransitionProps';
import {
RESOURCE_PAGE_TRANSITION_TAG,
transitionName,
} from '../../../helpers/transitionName';
import { NewClassInstanceButton } from './NewClassInstanceButton';

import type { JSX } from 'react';
import { TargetableCard } from '../TargetableCard';

interface ClassCardReadProps {
subject: string;
Expand All @@ -28,10 +22,10 @@ export function ClassCardRead({ subject }: ClassCardReadProps): JSX.Element {
const [recommends] = useArray(resource, core.properties.recommends);

return (
<StyledCard subject={subject}>
<TargetableCard subject={subject}>
<Column>
<Row center justify='space-between'>
<StyledH3 id={toAnchorId(subject)}>
<StyledH3>
<FaCube />
<AtomicLink subject={subject}>{resource.title}</AtomicLink>
</StyledH3>
Expand All @@ -55,15 +49,10 @@ export function ClassCardRead({ subject }: ClassCardReadProps): JSX.Element {
)}
</StyledTable>
</Column>
</StyledCard>
</TargetableCard>
);
}

const StyledCard = styled(Card)<ViewTransitionProps>`
padding-bottom: ${p => p.theme.size()};
${props => transitionName(RESOURCE_PAGE_TRANSITION_TAG, props.subject)};
`;

const StyledH3 = styled.h3`
display: flex;
align-items: center;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import {
} from '../../../components/ResourceContextMenu';
import { urls, useArray, useProperty, useResource } from '@tomic/react';
import { useCallback, type JSX } from 'react';
import { Card } from '../../../components/Card';
import { styled } from 'styled-components';
import { FaCube } from 'react-icons/fa';
import { Column, Row } from '../../../components/Row';
import { OntologyDescription } from '../OntologyDescription';
import { PropertyLineWrite } from '../Property/PropertyLineWrite';
import InputSwitcher from '../../../components/forms/InputSwitcher';
import { toAnchorId } from '../../../helpers/toAnchorId';
import { AddPropertyButton } from './AddPropertyButton';
import { ErrorChipInput } from '../../../components/forms/ErrorChip';
import { useOntologyContext } from '../OntologyContext';
import { TargetableCard } from '../TargetableCard';

interface ClassCardWriteProps {
subject: string;
Expand Down Expand Up @@ -52,8 +51,11 @@ export function ClassCardWrite({ subject }: ClassCardWriteProps): JSX.Element {
};

return (
<StyledCard data-testid={`class-card-write-${resource.title}`}>
<Column id={toAnchorId(subject)}>
<StyledCard
subject={subject}
data-testid={`class-card-write-${resource.title}`}
>
<Column>
<Row center justify='space-between'>
<TitleWrapper>
<FaCube />
Expand Down Expand Up @@ -103,8 +105,8 @@ export function ClassCardWrite({ subject }: ClassCardWriteProps): JSX.Element {
);
}

const StyledCard = styled(Card)`
padding-bottom: ${p => p.theme.margin}rem;
const StyledCard = styled(TargetableCard)`
padding-bottom: ${p => p.theme.size()}rem;
max-width: 100rem;

border: ${p =>
Expand Down
5 changes: 2 additions & 3 deletions browser/data-browser/src/views/OntologyPage/OntologyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,20 @@ import { OntologySidebar } from './OntologySidebar';
import { styled } from 'styled-components';
import { ClassCardRead } from './Class/ClassCardRead';
import { PropertyCardRead } from './Property/PropertyCardRead';
import ResourceCard from '../Card/ResourceCard';
import { Button } from '../../components/Button';
import { Column, Row } from '../../components/Row';
import { FaEdit, FaEye } from 'react-icons/fa';
import { OntologyDescription } from './OntologyDescription';
import { ClassCardWrite } from './Class/ClassCardWrite';
import { NewClassButton } from './NewClassButton';
import { toAnchorId } from '../../helpers/toAnchorId';
import { OntologyContextProvider } from './OntologyContext';
import { PropertyCardWrite } from './Property/PropertyCardWrite';
import { Graph } from './Graph';
import { CreateInstanceButton } from './CreateInstanceButton';
import { useState } from 'react';
import { NewPropertyButton } from './NewPropertyButton';
import { InfoTitle } from './InfoTitle';
import { TargetableResourceCard } from './TargetableCard';

const isEmpty = (arr: Array<unknown>) => arr.length === 0;

Expand Down Expand Up @@ -124,7 +123,7 @@ export function OntologyPage({ resource }: ResourcePageProps) {
{editMode && <CreateInstanceButton ontology={resource} />}
{instances.map(c => (
<li key={c}>
<ResourceCard subject={c} id={toAnchorId(c)} />
<TargetableResourceCard subject={c} />
</li>
))}
{!editMode && instances.length === 0 && <span>No instances</span>}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { Card } from '../../../components/Card';
import { urls, useArray, useResource, useString } from '@tomic/react';
import { core, useArray, useResource, useString } from '@tomic/react';
import { FaHashtag } from 'react-icons/fa';
import { styled } from 'styled-components';
import Markdown from '../../../components/datatypes/Markdown';
import { Column, Row } from '../../../components/Row';
import { InlineFormattedResourceList } from '../../../components/InlineFormattedResourceList';
import { InlineDatatype } from '../InlineDatatype';
import { AtomicLink } from '../../../components/AtomicLink';
import { toAnchorId } from '../../../helpers/toAnchorId';

import type { JSX } from 'react';
import { TargetableCard } from '../TargetableCard';

interface PropertyCardReadProps {
subject: string;
Expand All @@ -19,11 +18,11 @@ export function PropertyCardRead({
subject,
}: PropertyCardReadProps): JSX.Element {
const resource = useResource(subject);
const [description] = useString(resource, urls.properties.description);
const [allowsOnly] = useArray(resource, urls.properties.allowsOnly);
const [description] = useString(resource, core.properties.description);
const [allowsOnly] = useArray(resource, core.properties.allowsOnly);

return (
<StyledCard id={toAnchorId(subject)}>
<TargetableCard subject={subject}>
<Column>
<Row center justify='space-between'>
<Heading>
Expand All @@ -42,7 +41,7 @@ export function PropertyCardRead({
</>
)}
</Column>
</StyledCard>
</TargetableCard>
);
}

Expand All @@ -57,7 +56,3 @@ const Heading = styled.h3`
const SubHeading = styled.h4`
margin-bottom: 0px;
`;

const StyledCard = styled(Card)`
padding-bottom: ${p => p.theme.margin}rem;
`;
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { useCallback, type JSX } from 'react';
import { Card } from '../../../components/Card';
import { urls, useCanWrite, useProperty, useResource } from '@tomic/react';
import { FaHashtag } from 'react-icons/fa';
import { styled } from 'styled-components';
import { Column, Row } from '../../../components/Row';
import { toAnchorId } from '../../../helpers/toAnchorId';
import InputSwitcher from '../../../components/forms/InputSwitcher';
import {
ContextMenuOptions,
ResourceContextMenu,
} from '../../../components/ResourceContextMenu';
import { useOntologyContext } from '../OntologyContext';
import { PropertyFormCommon } from './PropertyFormCommon';
import { TargetableCard } from '../TargetableCard';

interface PropertyCardWriteProps {
subject: string;
Expand All @@ -36,7 +35,7 @@ export function PropertyCardWrite({
}, [removeProperty, subject]);

return (
<StyledCard id={toAnchorId(subject)}>
<StyledCard subject={subject}>
<Column>
<Row center justify='space-between'>
<TitleWrapper>
Expand Down Expand Up @@ -71,7 +70,7 @@ const TitleWrapper = styled.div`
}
`;

const StyledCard = styled(Card)`
const StyledCard = styled(TargetableCard)`
border: ${p =>
p.theme.darkMode ? `1px solid ${p.theme.colors.bg2}` : 'none'};
padding-bottom: ${p => p.theme.margin}rem;
Expand Down
60 changes: 60 additions & 0 deletions browser/data-browser/src/views/OntologyPage/TargetableCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { css, keyframes, styled } from 'styled-components';
import { toAnchorId } from '../../helpers/toAnchorId';
import { Card } from '../../components/Card';

import ResourceCard from '../Card/ResourceCard';

type TargetableCardProps = {
subject: string;
className?: string;
};

export const TargetableCard = ({
subject,
className,
children,
}: React.PropsWithChildren<TargetableCardProps>) => {
return (
<StyledCard
id={toAnchorId(subject ?? '')}
about={subject}
className={className}
>
{children}
</StyledCard>
);
};

export const TargetableResourceCard = ({ subject }: TargetableCardProps) => {
return (
<StyledResourceCard subject={subject} id={toAnchorId(subject ?? '')} />
);
};

const targetHighlight = keyframes`
from {
box-shadow: var(--target-animation-base-shadow), 0 0 0 2px var(--target-animation-color);
}
to {
box-shadow: var(--target-animation-base-shadow);
}
`;

const styles = css`
--target-animation-color: ${p => p.theme.colors.main};
--target-animation-base-shadow: ${p => p.theme.boxShadow};
padding-bottom: ${p => p.theme.size()};

&:target {
box-shadow:
var(--target-animation-base-shadow),
0 0 0 2px var(--target-animation-color);
animation: 500ms ease-out 1.5s forwards ${targetHighlight};
}
`;
const StyledCard = styled(Card)`
${styles}
`;
const StyledResourceCard = styled(ResourceCard)`
${styles}
`;
Loading
Loading