Skip to content

Commit 0f224a6

Browse files
Pollepsjoepio
authored andcommitted
#780 Enums in ontology editor
1 parent 3b3f862 commit 0f224a6

24 files changed

+629
-233
lines changed

browser/CHANGELOG.md

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,15 @@ This changelog covers all three packages, as they are (for now) updated as a who
1010
- [#770](https://github.com/atomicdata-dev/atomic-server/issues/770) Display more info on search result page.
1111
- [#771](https://github.com/atomicdata-dev/atomic-server/issues/771) Tables: Don't paste in multiple rows when focussed on an input
1212
- [#758](https://github.com/atomicdata-dev/atomic-server/issues/758) Fix Relation column forms to close when clicking on the searchbox
13+
- [#780](https://github.com/atomicdata-dev/atomic-server/issues/780) Use tags in ontology editor to create enum properties.
1314
- Fix server not rebuilding client when files changed.
14-
15-
### @tomic/lib
16-
17-
- [#798](https://github.com/atomicdata-dev/atomic-server/issues/798) Add `store.newResource()` to make creating new resources more easy.
18-
19-
## v0.36.2
20-
21-
### Atomic Browser
22-
2315
- Added persistent scrollbar to table
2416
- Improved table header UX
2517
- Numbers in tables now respect user locale
2618

2719
### @tomic/lib
2820

21+
- [#798](https://github.com/atomicdata-dev/atomic-server/issues/798) Add `store.newResource()` to make creating new resources more easy.
2922
- Always fetch all resources after setting + authenticating new agent with websockets #686
3023
- Add progress callback to `resource.getHistory()` And increased its performance for resources with a large number of commits [#745](https://github.com/atomicdata-dev/atomic-server/issues/745)
3124
- Fix websocket bug on port localhost with port 80

browser/data-browser/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@emotion/is-prop-valid": "^1.2.1",
1717
"@radix-ui/react-popover": "^1.0.6",
1818
"@radix-ui/react-scroll-area": "^1.0.1",
19+
"@radix-ui/react-tabs": "^1.0.4",
1920
"@tomic/react": "workspace:*",
2021
"emoji-mart": "^5.5.2",
2122
"polished": "^4.1.0",

browser/data-browser/src/components/InlineFormattedResourceList.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ const formatter = new Intl.ListFormat('en-GB', {
1212
export function InlineFormattedResourceList({
1313
subjects,
1414
}: InlineFormattedResourceListProps): JSX.Element {
15+
// There are rare cases where a resource array can locally have an undefined value, we filter these out to prevent the formatter from throwing an error.
16+
const filteredSubjects = subjects.filter(subject => subject !== undefined);
17+
1518
return (
1619
<>
17-
{formatter.formatToParts(subjects).map(({ type, value }) => {
20+
{formatter.formatToParts(filteredSubjects).map(({ type, value }) => {
1821
if (type === 'literal') {
1922
return value;
2023
}

browser/data-browser/src/components/Row.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,8 @@ const Flex = styled.div<FlexProps>`
6565
& ${ButtonDefault} {
6666
align-self: flex-start;
6767
}
68+
69+
& > p {
70+
margin: 0;
71+
}
6872
`;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { FC, PropsWithChildren } from 'react';
2+
import * as RadixTabs from '@radix-ui/react-tabs';
3+
import { styled } from 'styled-components';
4+
import { transition } from '../helpers/transition';
5+
6+
type TabItem = {
7+
label: string;
8+
value: string;
9+
};
10+
11+
interface TabsProps {
12+
tabs: TabItem[];
13+
className?: string;
14+
label: string;
15+
}
16+
17+
export const Tabs: FC<PropsWithChildren<TabsProps>> = ({
18+
children,
19+
tabs,
20+
label,
21+
className,
22+
}) => {
23+
return (
24+
<RadixTabs.Root defaultValue={tabs[0].value} className={className}>
25+
<TabList aria-label={label}>
26+
{tabs.map(tab => (
27+
<TabButton key={tab.value} value={tab.value}>
28+
{tab.label}
29+
</TabButton>
30+
))}
31+
</TabList>
32+
{children}
33+
</RadixTabs.Root>
34+
);
35+
};
36+
37+
interface TabPanelProps {
38+
value: string;
39+
}
40+
41+
export const TabPanel: FC<PropsWithChildren<TabPanelProps>> = ({
42+
value,
43+
children,
44+
}) => {
45+
return <RadixTabs.Content value={value}>{children}</RadixTabs.Content>;
46+
};
47+
48+
const TabList = styled(RadixTabs.List)`
49+
display: flex;
50+
justify-content: space-evenly;
51+
margin-bottom: ${p => p.theme.margin}rem;
52+
`;
53+
54+
const TabButton = styled(RadixTabs.Trigger)`
55+
background: none;
56+
border: none;
57+
color: ${p => p.theme.colors.text};
58+
border-bottom: 1px solid ${p => p.theme.colors.bg2};
59+
padding: 1rem;
60+
flex: 1;
61+
${transition('background', 'border-bottom')}
62+
cursor: pointer;
63+
&:hover,
64+
&:focus-visible {
65+
outline: none;
66+
background: ${p => p.theme.colors.bg1};
67+
}
68+
69+
&[data-state='active'] {
70+
border-bottom: 2px solid ${p => p.theme.colors.main};
71+
}
72+
`;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { Resource, core, dataBrowser, useStore } from '@tomic/react';
2+
import { useState, useCallback, Suspense, lazy } from 'react';
3+
import { FaPlus } from 'react-icons/fa';
4+
import { randomItem } from '../../helpers/randomItem';
5+
import { randomString } from '../../helpers/randomString';
6+
import { stringToSlug } from '../../helpers/stringToSlug';
7+
import { Button } from '../Button';
8+
import { Row } from '../Row';
9+
import { InputWrapper, InputStyled } from '../forms/InputStyles';
10+
import { tagColours } from './tagColours';
11+
12+
const EmojiInput = lazy(() => import('../../chunks/EmojiInput/EmojiInput'));
13+
14+
interface CreateTagRowProps {
15+
parent: string;
16+
onNewTag: (tag: Resource) => void;
17+
}
18+
19+
export function CreateTagRow({ parent, onNewTag }: CreateTagRowProps) {
20+
const store = useStore();
21+
const [tagName, setTagName] = useState<string>('');
22+
const [emoji, setEmoji] = useState<string | undefined>();
23+
const [resetKey, setResetKey] = useState<number>(0);
24+
25+
const createNewTag = useCallback(async () => {
26+
const tag = await store.newResource({
27+
subject: `${parent}/${randomString()}`,
28+
parent,
29+
isA: dataBrowser.classes.tag,
30+
propVals: {
31+
[core.properties.shortname]: tagName,
32+
[dataBrowser.properties.color]: randomItem(tagColours),
33+
},
34+
});
35+
36+
if (emoji) {
37+
await tag.set(dataBrowser.properties.emoji, emoji, store);
38+
}
39+
40+
onNewTag(tag);
41+
setTagName('');
42+
setEmoji(undefined);
43+
setResetKey(prev => prev + 1);
44+
}, [parent, store, tagName, emoji, onNewTag]);
45+
46+
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
47+
setTagName(stringToSlug(e.target.value));
48+
}, []);
49+
50+
const handleKeyDown = useCallback(
51+
(e: React.KeyboardEvent<HTMLInputElement>) => {
52+
if (e.key === 'Enter') {
53+
createNewTag();
54+
}
55+
},
56+
[createNewTag],
57+
);
58+
59+
return (
60+
<Suspense fallback={<div>Loading...</div>}>
61+
<Row>
62+
<InputWrapper>
63+
<EmojiInput onChange={setEmoji} key={resetKey} />
64+
<InputStyled
65+
placeholder='New tag'
66+
value={tagName}
67+
onChange={handleChange}
68+
onKeyDown={handleKeyDown}
69+
/>
70+
</InputWrapper>
71+
<Button title='Add tag' onClick={createNewTag} disabled={!tagName}>
72+
<FaPlus />
73+
</Button>
74+
</Row>
75+
</Suspense>
76+
);
77+
}

browser/data-browser/src/views/TablePage/PropertyForm/Tag.tsx renamed to browser/data-browser/src/components/Tag/Tag.tsx

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,19 @@ import { lighten, setLightness, setSaturation } from 'polished';
33
import * as RadixPopover from '@radix-ui/react-popover';
44
import { useCallback, useMemo, useState } from 'react';
55
import { styled } from 'styled-components';
6-
import { transition } from '../../../helpers/transition';
7-
import { Popover } from '../../../components/Popover';
8-
import { PalettePicker } from '../../../components/PalettePicker';
9-
import { Button } from '../../../components/Button';
10-
import { Column, Row } from '../../../components/Row';
6+
import { transition } from '../../helpers/transition';
7+
import { Popover } from '../Popover';
8+
import { PalettePicker } from '../PalettePicker';
9+
import { Button } from '../Button';
10+
import { Column, Row } from '../Row';
1111
import { FaTrash } from 'react-icons/fa';
12-
import { fadeIn } from '../../../helpers/commonAnimations';
12+
import { fadeIn } from '../../helpers/commonAnimations';
13+
import { tagColours } from './tagColours';
1314

1415
interface TagProps {
1516
subject: string;
1617
}
1718

18-
export const tagColors = [
19-
'#FFBE0B',
20-
'#FB5607',
21-
'#FF006E',
22-
'#8338EC',
23-
'#3A86FF',
24-
'#5FF56E',
25-
];
26-
2719
const useTagData = (subject: string) => {
2820
const resource = useResource(subject);
2921
const [title] = useTitle(resource);
@@ -74,11 +66,13 @@ const TagWrapper = styled.span<TagWrapperProps>`
7466
padding-block: 0.4rem;
7567
border-radius: 1em;
7668
border: 1px solid var(--tag-mid-color);
77-
color: var(--tag-dark-color);
69+
color: ${p =>
70+
p.theme.darkMode ? 'var(--tag-light-color)' : 'var(--tag-dark-color)'};
7871
line-height: 1;
7972
text-align: center;
8073
min-width: 3rem;
81-
background-color: var(--tag-light-color);
74+
background-color: ${p =>
75+
p.theme.darkMode ? 'var(--tag-dark-color)' : 'var(--tag-light-color)'};
8276
8377
&.selected-tag {
8478
text-decoration: underline;
@@ -152,7 +146,7 @@ export function EditableTag({
152146
>
153147
<PopoverContent>
154148
<Column>
155-
<PalettePicker palette={tagColors} onChange={handleColorChange} />
149+
<PalettePicker palette={tagColours} onChange={handleColorChange} />
156150
<DeleteButton onClick={() => onDelete(subject)}>
157151
<Row gap='0.5rem'>
158152
<FaTrash />
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './Tag';
2+
export * from './CreateTagRow';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const tagColours = [
2+
'#FFBE0B',
3+
'#FB5607',
4+
'#FF006E',
5+
'#8338EC',
6+
'#3A86FF',
7+
'#5FF56E',
8+
];

browser/data-browser/src/components/forms/AtomicSelectInput.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Resource, useValue } from '@tomic/react';
22

33
import { InputWrapper } from './InputStyles';
4-
import { styled, css } from 'styled-components';
4+
import { styled } from 'styled-components';
55

66
interface AtomicSelectInputProps {
77
resource: Resource;
@@ -11,6 +11,7 @@ interface AtomicSelectInputProps {
1111
label: string;
1212
}[];
1313
commit?: boolean;
14+
onChange?: (value: string) => void;
1415
}
1516

1617
type Props = AtomicSelectInputProps &
@@ -21,12 +22,14 @@ export function AtomicSelectInput({
2122
property,
2223
options,
2324
commit = false,
25+
onChange,
2426
...props
2527
}: Props): JSX.Element {
2628
const [value, setValue] = useValue(resource, property, { commit });
2729

2830
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
2931
setValue(e.target.value);
32+
onChange?.(e.target.value);
3033
};
3134

3235
return (
@@ -51,20 +54,18 @@ const StyledInputWrapper = styled(InputWrapper)`
5154
const SelectWrapper = styled.span<{ disabled: boolean }>`
5255
width: 100%;
5356
padding-inline: 0.2rem;
54-
55-
${p =>
56-
p.disabled &&
57-
css`
58-
background-color: ${props => props.theme.colors.bg1};
59-
`}
57+
background-color: ${p =>
58+
p.disabled ? p.theme.colors.bg1 : p.theme.colors.bg};
6059
`;
6160

6261
const Select = styled.select`
62+
cursor: pointer;
6363
width: 100%;
6464
border: none;
6565
outline: none;
6666
height: 2rem;
67-
67+
background-color: transparent;
68+
color: ${p => p.theme.colors.text};
6869
&:disabled {
6970
color: ${props => props.theme.colors.textLight};
7071
background-color: transparent;

0 commit comments

Comments
 (0)