Skip to content

Commit 60dcc6d

Browse files
authored
FormKit (#35)
1 parent fe197d1 commit 60dcc6d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1058
-316
lines changed

.eslintrc

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
{
2+
"root": true,
23
"env": {
34
"browser": true,
5+
"es2022": true,
46
"node": true
57
},
68
"extends": ["@nuxtjs/eslint-config-typescript", "plugin:nuxt/recommended"],
9+
"parserOptions": {
10+
"ecmaVersion": "latest",
11+
"parser": "@typescript-eslint/parser",
12+
"sourceType": "module"
13+
},
14+
"plugins": ["@typescript-eslint", "vue", "nuxt"],
715
"rules": {
816
"vue/multi-word-component-names": "off",
917
"vue/no-multiple-template-root": "off",
@@ -23,10 +31,9 @@
2331
{
2432
"html": {
2533
"void": "always",
26-
"normal": "never",
34+
"normal": "any",
2735
"component": "always"
28-
},
29-
"slot": "any"
36+
}
3037
}
3138
]
3239
}

.github/workflows/test.yml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
6+
jobs:
7+
lint:
8+
runs-on: ubuntu-20.04
9+
steps:
10+
- uses: actions/checkout@v3
11+
12+
- uses: actions/setup-node@v3
13+
with:
14+
node-version: 16
15+
16+
- name: Get yarn cache directory path
17+
id: yarn
18+
run: echo "::set-output name=dir::$(yarn cache dir)"
19+
20+
- uses: actions/cache@v3
21+
with:
22+
path: ${{ steps.yarn.outputs.dir }}
23+
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
24+
restore-keys: |
25+
${{ runner.os }}-yarn-
26+
27+
- name: Install Dependencies
28+
run: yarn
29+
30+
- name: run eslint
31+
run: npm run lint:code

.graphqlrc

+2
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ extensions:
1010
- typescript-operations
1111
- typed-document-node
1212
- fragment-matcher
13+
- add:
14+
content: /* eslint-disable */

app.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<NuxtLayout>
33
<NuxtPage />
44
<Menu v-if="menu" :x="menu.x" :y="menu.y" :options="menu.options" />
5-
<div :class="{ visible: showCurtain }" id="backdrop" @click="closeDialog" />
5+
<div id="backdrop" :class="{ visible: showCurtain }" @click="closeDialog" />
66
</NuxtLayout>
77
</template>
88

components/LocationPreview.vue

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<template>
2+
<StyledPanel>
3+
<h3>{{ location.name }}</h3>
4+
<p v-if="location.__typename === 'Location'">
5+
<em> referenced in {{ location.tales.totalCount }} tales </em>
6+
</p>
7+
</StyledPanel>
8+
</template>
9+
10+
<script lang="ts" setup>
11+
import { LocationSummaryFragment } from '~/graphql/generated'
12+
13+
defineProps<{
14+
location: LocationSummaryFragment
15+
}>()
16+
</script>

components/Nav.vue

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<nav>
33
<NuxtLink v-for="(link, i) of links" :key="i" :class="['tab', { active: link.to === active }]" :to="link.to">
4-
<component class="icon" :is="link.icon" />
4+
<component :is="link.icon" class="icon" />
55
<span> {{ link.display }} </span>
66
</NuxtLink>
77
<section class="flex gap-4 justify-self-end">
@@ -13,13 +13,14 @@
1313
</template>
1414

1515
<script lang="ts" setup>
16-
import { BookOpenIcon, MapIcon } from '@heroicons/vue/24/solid'
16+
import { BookOpenIcon, MapIcon, MapPinIcon } from '@heroicons/vue/24/solid'
1717
1818
const route = useActiveRoute()
1919
2020
const links = ref([
21-
{ display: 'Map', to: '/', icon: MapIcon },
21+
{ display: 'Map', to: '/map', icon: MapIcon },
2222
{ display: 'Libary', to: '/library', icon: BookOpenIcon },
23+
{ display: 'Locations', to: '/locations', icon: MapPinIcon },
2324
])
2425
2526
const active = computed(() => [...links.value].reverse().find(it => route.path.startsWith(it.to))?.to)
@@ -32,7 +33,7 @@ const { loggedIn } = useSession()
3233
<style lang="scss" scoped>
3334
nav {
3435
@apply pr-2;
35-
@apply grid items-center;
36+
@apply grid items-center grid-flow-col;
3637
grid-template-columns: repeat(v-bind('linkCount'), auto) 1fr;
3738
3839
@apply bg-solid-700;

components/Paginated/List.vue

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<slot />
88
</StyledList>
99
<PaginatedControls
10+
v-if="connection.pageInfo.hasPreviousPage || connection.pageInfo.hasNextPage"
1011
:connection="connection"
1112
:page-size="connection.nodes.length"
1213
@next="$emit('next')"

components/TalePreview.vue

+4-22
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,16 @@
11
<template>
2-
<div id="panel">
2+
<StyledPanel>
33
<h3>{{ tale.title }}</h3>
44
<p>
55
<em> {{ tale.locations.totalCount }} referenced locations </em>
66
</p>
7-
</div>
7+
</StyledPanel>
88
</template>
99

1010
<script lang="ts" setup>
11-
import { SummaryFragment } from '~/graphql/generated'
11+
import { TaleSummaryFragment } from '~/graphql/generated'
1212
1313
defineProps<{
14-
tale: SummaryFragment
14+
tale: TaleSummaryFragment
1515
}>()
1616
</script>
17-
18-
<style lang="scss" scoped>
19-
#panel {
20-
@apply p-3 rounded bg-solid-600 transition-colors;
21-
22-
&:hover {
23-
@apply bg-solid-700;
24-
}
25-
}
26-
27-
.dark-mode #panel {
28-
@apply bg-solid-700;
29-
30-
&:hover {
31-
@apply bg-solid-600;
32-
}
33-
}
34-
</style>

components/dialog/Base.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<Teleport to="body">
33
<div id="dialog">
4-
<h1 class="text-center" v-if="title">{{ title }}</h1>
4+
<h2 v-if="title" class="text-center">{{ title }}</h2>
55
<slot />
66
</div>
77
</Teleport>

components/dialog/CreateLocation.vue

+7-32
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,13 @@
11
<template>
2-
<DialogForm title="Create Marker" class="w-96" with-submit @submit="mutate">
3-
<form>
4-
<p>
5-
<PosDisplay :x="x" :y="y" :z="z" />
6-
</p>
7-
<FormGroup label="Name">
8-
<StyledTextInput autofocus v-model="name" placeholder="Name" />
9-
</FormGroup>
10-
</form>
11-
</DialogForm>
2+
<DialogBase title="Add Marker">
3+
<FormCreateLocation :initial-pos="pos" @saved="closeDialog" />
4+
</DialogBase>
125
</template>
136

147
<script lang="ts" setup>
15-
import { CreateLocationDocument, Maybe } from '~/graphql/generated'
8+
import { PosFragment } from '~/graphql/generated'
169
17-
const pos = withDefaults(
18-
defineProps<{
19-
x: number
20-
y?: Maybe<number>
21-
z: number
22-
}>(),
23-
{
24-
y: undefined,
25-
},
26-
)
27-
28-
const name = ref('')
29-
30-
const { mutate, error } = useMutation(CreateLocationDocument, () => ({
31-
variables: {
32-
input: { ...pos, name: name.value, world: 'overworld' },
33-
},
34-
refetchQueries: ['getLocation', 'getLocations'],
35-
}))
36-
37-
watch(error, v => console.log(v))
10+
defineProps<{
11+
pos: PosFragment
12+
}>()
3813
</script>

components/form/CreateLocation.vue

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<template>
2+
<section>
3+
<FormKit v-slot="{ value, state: { valid } }" type="form" :actions="false" :errors="errors">
4+
<FormKit name="world" validation="required" type="hidden" value="overworld" />
5+
6+
<InputPos :initial="initialPos" />
7+
8+
<FormKit name="name" validation="required" label="Name" type="text" />
9+
10+
<div id="buttons">
11+
<FormKit v-if="!onlyDraft" type="submit" :disabled="!valid" @click.prevent="save(value, false)" />
12+
<FormKit
13+
type="submit"
14+
:disabled="!valid"
15+
:classes="{ input: 'bg-solid-600' }"
16+
@click.prevent="save(value, true)"
17+
>
18+
Save as Draft
19+
</FormKit>
20+
</div>
21+
</FormKit>
22+
</section>
23+
</template>
24+
25+
<script lang="ts" setup>
26+
import {
27+
CreateLocationDocument,
28+
CreateLocationDraftDocument,
29+
CreateLocationInput,
30+
CreateLocationMutation,
31+
Permission,
32+
PosFragment,
33+
} from '~~/graphql/generated'
34+
35+
const { hasPermission } = useSession()
36+
const { query } = useActiveRoute()
37+
38+
const onlyDraft = computed(() => {
39+
if ('draft' in query) return true
40+
return !hasPermission(Permission.CreateLocation)
41+
})
42+
43+
const emit = defineEmits<{
44+
(e: 'saved', data: CreateLocationMutation, draft: boolean): void
45+
}>()
46+
47+
defineProps<{
48+
initialPos?: PosFragment
49+
}>()
50+
51+
const refetchQueries = ['getLocation', 'getLocations']
52+
const { mutate: createLocation, error } = useMutation(CreateLocationDocument, { refetchQueries })
53+
const { mutate: createLocationDraft, error: draftError } = useMutation(CreateLocationDraftDocument, { refetchQueries })
54+
const errors = computed(() =>
55+
[error, draftError]
56+
.map(it => it.value)
57+
.filter(notNull)
58+
.flatMap(extractMessages),
59+
)
60+
61+
async function save(input: CreateLocationInput, draft: boolean) {
62+
const create = draft ? createLocationDraft : createLocation
63+
const response = await create({ input })
64+
const data = response?.data
65+
if (data) emit('saved', data, draft)
66+
}
67+
</script>
68+
69+
<style lang="scss" scoped>
70+
form {
71+
#buttons {
72+
@apply flex gap-2;
73+
}
74+
}
75+
</style>

0 commit comments

Comments
 (0)