Skip to content

Commit f8ccaed

Browse files
authored
add type optional (#382)
1 parent a218bde commit f8ccaed

File tree

19 files changed

+880
-295
lines changed

19 files changed

+880
-295
lines changed

.changeset/pretty-showers-tap.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@graphprotocol/hypergraph-react": patch
3+
"@graphprotocol/hypergraph": patch
4+
---
5+
6+
add Type.optional
7+

apps/events/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"@noble/hashes": "^1.8.0",
1515
"@radix-ui/react-avatar": "^1.1.9",
1616
"@radix-ui/react-icons": "^1.3.2",
17+
"@radix-ui/react-label": "^2.1.7",
1718
"@radix-ui/react-slot": "^1.2.2",
1819
"@tanstack/react-query": "^5.75.5",
1920
"@tanstack/react-router": "^1.120.2",

apps/events/src/components/events/events.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useState } from 'react';
1010
import { Event } from '../../schema';
1111
import { Button } from '../ui/button';
1212
import { Input } from '../ui/input';
13+
import { Label } from '../ui/label';
1314

1415
export const Events = () => {
1516
const { data: eventsLocalData } = useQuery(Event, { mode: 'private' });
@@ -43,6 +44,7 @@ export const Events = () => {
4344
{eventsLocalData.map((event) => (
4445
<div key={event.id} className="flex flex-row items-center gap-2">
4546
<h2>{event.name}</h2>
47+
<p>{event.description}</p>
4648
<div className="text-xs">{event.id}</div>
4749
<select
4850
value={selectedSpace}
@@ -66,10 +68,22 @@ export const Events = () => {
6668
e.preventDefault();
6769
const formData = new FormData(e.target as HTMLFormElement);
6870
const name = formData.get('name') as string;
69-
createEvent({ name });
71+
const description = formData.get('description') as string;
72+
if (!name) {
73+
alert('Name is required');
74+
return;
75+
}
76+
if (description) {
77+
createEvent({ name, description });
78+
} else {
79+
createEvent({ name });
80+
}
7081
}}
7182
>
72-
<Input type="text" name="name" />
83+
<Label htmlFor="name">Name</Label>
84+
<Input type="text" name="name" required />
85+
<Label htmlFor="description">Description</Label>
86+
<Input type="text" name="description" />
7387
<Button type="submit">Create Event</Button>
7488
</form>
7589
</>

apps/events/src/components/playground.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const Playground = ({ spaceId }: { spaceId: string }) => {
1717
jobOffers: {},
1818
},
1919
},
20-
first: 2,
20+
first: 10,
2121
space: spaceId,
2222
});
2323
const [isDeleting, setIsDeleting] = useState(false);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as LabelPrimitive from '@radix-ui/react-label';
2+
import type * as React from 'react';
3+
4+
import { cn } from '@/lib/utils';
5+
6+
function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
7+
return (
8+
<LabelPrimitive.Root
9+
data-slot="label"
10+
className={cn(
11+
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
12+
className,
13+
)}
14+
{...props}
15+
/>
16+
);
17+
}
18+
19+
export { Label };

apps/events/src/mapping.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const mapping: Mapping.Mapping = {
66
typeIds: [Id.Id('7f9562d4-034d-4385-bf5c-f02cdebba47a')],
77
properties: {
88
name: Id.Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
9+
description: Id.Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'),
910
},
1011
relations: {
1112
sponsors: Id.Id('6860bfac-f703-4289-b789-972d0aaf3abe'),

apps/events/src/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ export class Company extends Entity.Class<Company>('Company')({
3333

3434
export class Event extends Entity.Class<Event>('Event')({
3535
name: Type.Text,
36-
// description: Type.Text,
36+
description: Type.optional(Type.Text),
3737
sponsors: Type.Relation(Company),
3838
}) {}

docs/docs/schema.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ export class Company extends Entity.Class<Company>('Company')({
5656
}) {}
5757
```
5858

59+
## Optional Fields
60+
61+
You can make a field optional by wrapping it in `Type.optional`.
62+
63+
```ts
64+
import { Entity, Type } from '@graphprotocol/hypergraph';
65+
66+
export class Company extends Entity.Class<Company>('Company')({
67+
name: Type.Text,
68+
description: Type.optional(Type.Text),
69+
founded: Type.optional(Type.Date),
70+
}) {}
71+
```
72+
5973
## Schema Examples
6074

6175
You can search for dozens of schema/mapping examples on the [Hypergraph Schema Browser](https://schema-browser.vercel.app/).

packages/hypergraph-react/src/hooks/use-spaces.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type PublicSpacesQueryResult = {
2424
spaceAddress: string;
2525
page: {
2626
name: string;
27-
};
27+
} | null;
2828
}[];
2929
};
3030

@@ -43,7 +43,7 @@ export const useSpaces = (params: { mode: 'public' | 'private' }) => {
4343
return result?.spaces
4444
? result.spaces.map((space) => ({
4545
id: space.id,
46-
name: space.page.name,
46+
name: space.page?.name,
4747
spaceAddress: space.spaceAddress,
4848
}))
4949
: [];

packages/hypergraph-react/src/internal/use-create-entity-public.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Graph, Id, type PropertiesParam, type RelationsParam } from '@graphprotocol/grc-20';
22
import type { Connect, Entity } from '@graphprotocol/hypergraph';
3-
import { store, Type } from '@graphprotocol/hypergraph';
3+
import { store, TypeUtils } from '@graphprotocol/hypergraph';
44
import { useQueryClient } from '@tanstack/react-query';
55
import { useSelector } from '@xstate/store/react';
66
import type * as Schema from 'effect/Schema';
@@ -33,14 +33,20 @@ export function useCreateEntityPublic<const S extends Entity.AnyNoContext>(
3333
const fields = type.fields;
3434
const values: PropertiesParam = [];
3535
for (const [key, value] of Object.entries(mappingEntry.properties || {})) {
36+
if (data[key] === undefined) {
37+
if (TypeUtils.isOptional(fields[key])) {
38+
continue;
39+
}
40+
throw new Error(`Value for ${key} is undefined`);
41+
}
3642
let serializedValue: string = data[key];
37-
if (fields[key] === Type.Checkbox) {
43+
if (TypeUtils.isCheckboxOrOptionalCheckboxType(fields[key])) {
3844
serializedValue = Graph.serializeCheckbox(data[key]);
39-
} else if (fields[key] === Type.Date) {
45+
} else if (TypeUtils.isDateOrOptionalDateType(fields[key])) {
4046
serializedValue = Graph.serializeDate(data[key]);
41-
} else if (fields[key] === Type.Point) {
47+
} else if (TypeUtils.isPointOrOptionalPointType(fields[key])) {
4248
serializedValue = Graph.serializePoint(data[key]);
43-
} else if (fields[key] === Type.Number) {
49+
} else if (TypeUtils.isNumberOrOptionalNumberType(fields[key])) {
4450
serializedValue = Graph.serializeNumber(data[key]);
4551
}
4652

0 commit comments

Comments
 (0)