Last active
June 8, 2023 12:25
-
-
Save acomagu/db91e95b2084a529c090f4e9abd71ac2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { list } from '@keystone-6/core'; | |
import { graphql } from '@keystone-6/core'; | |
import { allowAll } from '@keystone-6/core/access'; | |
import { checkbox, relationship, text, timestamp } from '@keystone-6/core/fields'; | |
import { select } from '@keystone-6/core/fields'; | |
import { BaseListTypeInfo, CommonFieldConfig, FieldTypeFunc, fieldType } from '@keystone-6/core/types'; | |
interface TranslatedFieldConfig< | |
ListTypeInfo extends BaseListTypeInfo, | |
LanguageTag extends string, | |
> extends CommonFieldConfig<ListTypeInfo> { | |
languages: LanguageTag[]; | |
} | |
type TranslatedFieldArgs = Record<string, graphql.Arg<graphql.ScalarType<string>>>; | |
type TranslatedField = Record<string, string | undefined | null>; | |
function translated<ListTypeInfo extends BaseListTypeInfo, LanguageTag extends string>( | |
config: TranslatedFieldConfig<ListTypeInfo, LanguageTag>, | |
): FieldTypeFunc<ListTypeInfo> { | |
const translatedFieldInput = graphql.inputObject<TranslatedFieldArgs>({ | |
name: 'TranslatedFieldInput', | |
fields: Object.fromEntries([ | |
[config.languages[0], graphql.arg({ type: graphql.nonNull(graphql.String) })], | |
...config.languages.slice(1).map(tag => [tag, graphql.arg({ type: graphql.String })]), | |
]), | |
}); | |
const translatedFieldOutput = graphql.object<TranslatedField>()({ | |
name: 'TranslatedFieldOutput', | |
fields: Object.fromEntries([ | |
[config.languages[0], graphql.field({ | |
type: graphql.nonNull(graphql.String), | |
resolve: v => (v as any)[config.languages[0]], | |
})], | |
...config.languages.slice(1).map(tag => [tag, graphql.field({ | |
type: graphql.String, | |
resolve: v => (v as any)[tag], | |
})]), | |
]), | |
}); | |
function resolveInput(value?: TranslatedField | null) { | |
if (value?.[config.languages[0]] == undefined) return {}; | |
const res = { | |
[config.languages[0]]: value?.[config.languages[0]], | |
...Object.fromEntries(config.languages.slice(1).map(tag => | |
[tag, value?.[tag] ?? null], | |
)), | |
}; | |
return res; | |
} | |
return () => fieldType({ | |
kind: 'multi', | |
fields: { | |
[config.languages[0]]: { | |
kind: 'scalar', | |
mode: 'required', | |
scalar: 'String', | |
}, | |
...Object.fromEntries(config.languages.slice(1).map(tag => [tag, { | |
kind: 'scalar', | |
mode: 'optional', | |
scalar: 'String', | |
}])), | |
}, | |
})({ | |
...config, | |
getAdminMeta: () => ({ | |
languages: config.languages, | |
}), | |
input: { | |
create: { | |
arg: graphql.arg({ type: translatedFieldInput }), | |
resolve: resolveInput, | |
}, | |
update: { | |
arg: graphql.arg({ type: translatedFieldInput }), | |
resolve: resolveInput, | |
}, | |
}, | |
output: graphql.field({ | |
type: translatedFieldOutput, | |
}), | |
views: './views', | |
}); | |
} | |
export const lists = { | |
Person: list({ | |
access: allowAll, | |
fields: { | |
label: text({ validation: { isRequired: true } }), | |
name: translated({ languages: ['ja', 'en', 'ch'] }), | |
}, | |
}), | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"dependencies": { | |
"@keystone-6/auth": "^7.0.0", | |
"@keystone-6/core": "^5.0.0", | |
"@keystone-6/fields-document": "^7.0.0", | |
"typescript": "^4.9.5" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { FieldContainer, FieldDescription, FieldLabel, TextInput } from '@keystone-ui/fields'; | |
import { CellLink, CellContainer } from '@keystone-6/core/admin-ui/components'; | |
import { | |
CardValueComponent, | |
FieldController, | |
CellComponent, | |
FieldControllerConfig, | |
FieldProps, | |
} from '@keystone-6/core/types'; | |
export type AdminTranslatedFieldMeta = { | |
languages: string[]; | |
}; | |
export function Field({ field, value, onChange, autoFocus }: FieldProps<typeof controller>) { | |
const disabled = onChange === undefined; | |
return Object.keys(value ?? {}).map(tag => <> | |
<FieldContainer as="fieldset"> | |
<FieldLabel>{field.label} ({tag})</FieldLabel> | |
<FieldDescription id={`${field.path}-description-${tag}`}> | |
{field.description} | |
</FieldDescription> | |
<div> | |
<TextInput | |
type="text" | |
onChange={event => { | |
onChange?.({ ...value, [tag]: event.target.value }); | |
}} | |
disabled={disabled} | |
value={value?.[tag]} | |
autoFocus={autoFocus} | |
/> | |
</div> | |
</FieldContainer> | |
</>); | |
} | |
export const Cell: CellComponent = ({ item, field, linkTo }) => { | |
let value = item[field.path] + ''; | |
return linkTo ? <CellLink {...linkTo}>{value}</CellLink> : <CellContainer>{value}</CellContainer>; | |
}; | |
Cell.supportsLinkTo = true; | |
export const CardValue: CardValueComponent = ({ item, field }) => { | |
return ( | |
<FieldContainer> | |
<FieldLabel>{field.label}</FieldLabel> | |
{item[field.path]} | |
</FieldContainer> | |
); | |
}; | |
export const controller = ( | |
config: FieldControllerConfig<AdminTranslatedFieldMeta>, | |
): FieldController< | |
Record<string, string> | null, | |
string | |
> => { | |
return { | |
path: config.path, | |
label: config.label, | |
description: config.description, | |
graphqlSelection: `${config.path} { | |
${config.fieldMeta.languages.join('\n')} | |
}`, | |
defaultValue: null, | |
deserialize: data => { | |
return config.fieldMeta.languages.reduce((sum, tag) => ({ | |
...sum, | |
[tag]: data[config.path]?.[tag] ?? null, | |
}), {}); | |
}, | |
serialize: value => ({ [config.path]: value }), | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment