Skip to content

Commit

Permalink
#845 Default Ontology
Browse files Browse the repository at this point in the history
  • Loading branch information
Polleps authored and joepio committed Apr 24, 2024
1 parent 878becd commit 12e77a4
Show file tree
Hide file tree
Showing 19 changed files with 454 additions and 193 deletions.
12 changes: 10 additions & 2 deletions browser/data-browser/src/components/Dropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -412,19 +412,27 @@ const MenuItemStyled = styled(Button)<MenuItemStyledProps>`
p.selected ? p.theme.colors.bg1 : p.theme.colors.bg};
text-decoration: ${p => (p.selected ? 'underline' : 'none')};
& svg {
color: ${p => p.theme.colors.textLight};
}
&:hover {
background-color: ${p => p.theme.colors.bg1};
}
&:active {
background-color: ${p => p.theme.colors.bg2};
}
&:disabled {
color: ${p => p.theme.colors.textLight};
color: ${p => p.theme.colors.textLight2};
cursor: default;
background-color: ${p => p.theme.colors.bg};
&:hover {
cursor: 'default';
}
background-color: ${p => p.theme.colors.bg};
& svg {
color: ${p => p.theme.colors.textLight2};
}
}
svg {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
import { styled } from 'styled-components';
import {
Collection,
unknownSubject,
urls,
useCollection,
useMemberFromCollection,
} from '@tomic/react';
import { core, unknownSubject, useResource, useStore } from '@tomic/react';
import { SideBarItem } from '../SideBarItem';
import { Row } from '../../Row';
import { AtomicLink } from '../../AtomicLink';
import { getIconForClass } from '../../../views/FolderPage/iconMap';
import { ScrollArea } from '../../ScrollArea';
import { ErrorLook } from '../../ErrorLook';
import { useEffect, useState } from 'react';
import { useSettings } from '../../../helpers/AppSettings';

export function OntologiesPanel(): JSX.Element | null {
const { collection } = useCollection({
property: urls.properties.isA,
value: urls.classes.ontology,
});
const store = useStore();
const [ontologies, setOntologies] = useState<string[]>([]);
const { drive } = useSettings();

useEffect(() => {
store
.search('', {
filters: {
[core.properties.isA]: core.classes.ontology,
},
parents: drive,
})
.then(setOntologies);
}, [store]);

return (
<Wrapper>
<StyledScrollArea>
{[...Array(collection.totalMembers).keys()].map(index => (
<Item key={index} collection={collection} index={index} />
{ontologies.map(subject => (
<Item key={subject} subject={subject} />
))}
</StyledScrollArea>
</Wrapper>
Expand All @@ -37,25 +43,24 @@ const Wrapper = styled.div`
`;

const StyledScrollArea = styled(ScrollArea)`
height: 10rem;
max-height: 10rem;
overflow-x: hidden;
`;

interface ItemProps {
index: number;
collection: Collection;
subject: string;
}

function Item({ index, collection }: ItemProps): JSX.Element {
const resource = useMemberFromCollection(collection, index);
function Item({ subject }: ItemProps): JSX.Element {
const resource = useResource(subject);

const Icon = getIconForClass(urls.classes.ontology);
const Icon = getIconForClass(core.classes.ontology);

if (resource.loading) {
return <div>loading</div>;
}

if (resource.error || resource.getSubject() === unknownSubject) {
if (resource.error || resource.subject === unknownSubject) {
return (
<SideBarItem>
<ErrorLook>Invalid Resource</ErrorLook>
Expand All @@ -64,7 +69,7 @@ function Item({ index, collection }: ItemProps): JSX.Element {
}

return (
<StyledLink subject={resource.getSubject()} clean>
<StyledLink subject={subject} clean>
<SideBarItem>
<Row gap='1ch' center>
<Icon />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { dataBrowser, core, classes, server } from '@tomic/react';
import { dataBrowser, core, classes } from '@tomic/react';
import { registerBasicInstanceHandler } from '../useNewResourceUI';

/**
Expand All @@ -12,7 +12,7 @@ export const registerBasicInstanceHandlers = () => {
await createAndNavigate(
dataBrowser.classes.folder,
{
[core.properties.name]: 'Untitled Folder',
[core.properties.name]: 'untitled-folder',
[dataBrowser.properties.displayStyle]: classes.displayStyles.list,
},
parent,
Expand Down Expand Up @@ -45,30 +45,4 @@ export const registerBasicInstanceHandlers = () => {
);
},
);

registerBasicInstanceHandler(
server.classes.drive,
async (_parent, createAndNavigate, { store, settings }) => {
const agent = store.getAgent();

if (!agent || agent.subject === undefined) {
throw new Error(
'No agent set in the Store, required when creating a Drive',
);
}

const newResource = await createAndNavigate(server.classes.drive, {
[core.properties.write]: [agent.subject],
[core.properties.read]: [agent.subject],
});

// resources created with createAndNavigate have a parent by default which we don't want for drives.
newResource.remove(core.properties.parent);

const agentResource = await store.getResource(agent.subject);
agentResource.push(server.properties.drives, [newResource.subject]);
agentResource.save();
settings.setDrive(newResource.subject);
},
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { core, useStore, server, dataBrowser, Resource } from '@tomic/react';
import { useState, useCallback, FormEvent, FC, useEffect } from 'react';
import { styled } from 'styled-components';
import { stringToSlug } from '../../../../../helpers/stringToSlug';
import { Button } from '../../../../Button';
import {
useDialog,
Dialog,
DialogContent,
DialogActions,
} from '../../../../Dialog';
import Field from '../../../Field';
import { InputWrapper, InputStyled } from '../../../InputStyles';
import { CustomResourceDialogProps } from '../../useNewResourceUI';
import { useCreateAndNavigate } from '../../../../../hooks/useCreateAndNavigate';
import { useSettings } from '../../../../../helpers/AppSettings';

export const NewDriveDialog: FC<CustomResourceDialogProps> = ({
parent,
onClose,
}) => {
const store = useStore();
const { setDrive } = useSettings();
const [name, setName] = useState('');

const createAndNavigate = useCreateAndNavigate();

const onSuccess = useCallback(async () => {
if (!name.trim()) return;

const agent = store.getAgent();

if (!agent || agent.subject === undefined) {
throw new Error(
'No agent set in the Store, required when creating a Drive',
);
}

const newDrive = await createAndNavigate(
server.classes.drive,
{
[core.properties.name]: name,
[core.properties.write]: [agent.subject],
[core.properties.read]: [agent.subject],
},
undefined,
undefined,
async resource => {
// resources created with createAndNavigate have a parent by default which we don't want for drives.
resource.remove(core.properties.parent);

// Add drive to the agents drive list.
const agentResource = await store.getResource(agent.subject!);
agentResource.push(server.properties.drives, [resource.subject]);
await agentResource.save();

// Create a default ontology.
const ontologyName = stringToSlug(name);
const ontology = await store.newResource({
subject: await store.buildUniqueSubjectFromParts(
['defaultOntology'],
resource.subject,
),
isA: core.classes.ontology,
parent: resource.subject,
propVals: {
[core.properties.shortname]: ontologyName,
[core.properties
.description]: `Default ontology for the ${name} drive`,
[core.properties.classes]: [],
[core.properties.properties]: [],
[core.properties.instances]: [],
},
});

await ontology.save();

await resource.set(server.properties.defaultOntology, ontology.subject);
await resource.set(dataBrowser.properties.subResources, [
ontology.subject,
]);
await resource.save();
},
);

// Change current drive to new drive
setDrive(newDrive.subject);

onClose();
}, [name, createAndNavigate, onClose, parent, setDrive, store]);

const [dialogProps, show, hide] = useDialog({ onSuccess, onCancel: onClose });

useEffect(() => {
show();
}, []);

return (
<Dialog {...dialogProps}>
<H1>New Drive</H1>
<DialogContent>
<form
onSubmit={(e: FormEvent) => {
e.preventDefault();
hide(true);
}}
>
<Field required label='Name'>
<InputWrapper>
<InputStyled
placeholder='My Drive'
value={name}
autoFocus={true}
onChange={e => setName(e.target.value)}
/>
</InputWrapper>
</Field>
</form>
</DialogContent>
<DialogActions>
<Button onClick={() => hide(false)} subtle>
Cancel
</Button>
<Button onClick={() => hide(true)} disabled={!name.trim()}>
Create
</Button>
</DialogActions>
</Dialog>
);
};

const H1 = styled.h1`
margin: 0;
`;
Loading

0 comments on commit 12e77a4

Please sign in to comment.