Link
This is a slightly modified version of link.ts from the Payload website template, the original version can be found here.
modifications
Localized Label
The localizedLabel prop has been added, this defaults to false.
Required
The required props has been added, this defaults to false.
Code
1import type { Field, GroupField } from 'payload';
2
3import deepMerge from '@/src/utilities/deepMerge';
4
5export type LinkAppearances = 'default' | 'outline';
6
7export const appearanceOptions: Record<
8 LinkAppearances,
9 { label: string; value: string }
10> = {
11 default: {
12 label: 'Default',
13 value: 'default',
14 },
15 outline: {
16 label: 'Outline',
17 value: 'outline',
18 },
19};
20
21type LinkType = (options?: {
22 appearances?: LinkAppearances[] | false;
23 disableLabel?: boolean;
24 localizedLabel?: boolean;
25 overrides?: Partial<GroupField>;
26 required?: boolean;
27}) => Field;
28
29export const link: LinkType = ({
30 appearances,
31 disableLabel = false,
32 localizedLabel = false,
33 overrides = {},
34 required = false,
35} = {}) => {
36 const linkResult: Field = {
37 name: 'link',
38 type: 'group',
39 admin: {
40 hideGutter: true,
41 },
42 fields: [
43 {
44 type: 'row',
45 fields: [
46 {
47 name: 'type',
48 type: 'radio',
49 admin: {
50 layout: 'horizontal',
51 width: '50%',
52 },
53 defaultValue: 'reference',
54 options: [
55 {
56 label: 'Internal link',
57 value: 'reference',
58 },
59 {
60 label: 'Custom URL',
61 value: 'custom',
62 },
63 ],
64 },
65 {
66 name: 'newTab',
67 type: 'checkbox',
68 label: 'Open in new tab',
69 admin: {
70 width: '50%',
71 style: { alignSelf: 'flex-end' },
72 },
73 },
74 ],
75 },
76 ],
77 };
78
79 const linkTypes: Field[] = [
80 {
81 name: 'reference',
82 type: 'relationship',
83 label: 'Document to link to',
84 relationTo: ['pages', 'posts'],
85 required,
86 admin: {
87 condition: (_, siblingData) => siblingData?.type === 'reference',
88 width: '50%',
89 },
90 },
91 {
92 name: 'url',
93 type: 'text',
94 label: 'Custom URL',
95 required,
96 admin: {
97 condition: (_, siblingData) => siblingData?.type === 'custom',
98 width: '50%',
99 },
100 },
101 ];
102
103 if (!disableLabel) {
104 linkTypes.map((linkType) => ({
105 ...linkType,
106 admin: {
107 ...linkType.admin,
108 width: '50%',
109 },
110 }));
111
112 linkResult.fields.push({
113 type: 'row',
114 fields: [
115 ...linkTypes,
116 {
117 name: 'label',
118 type: 'text',
119 admin: {
120 width: '50%',
121 },
122 label: 'Label',
123 required,
124 localized: localizedLabel,
125 },
126 ],
127 });
128 } else {
129 linkResult.fields = [...linkResult.fields, ...linkTypes];
130 }
131
132 if (appearances !== false) {
133 let appearanceOptionsToUse = [
134 appearanceOptions.default,
135 appearanceOptions.outline,
136 ];
137
138 if (appearances) {
139 appearanceOptionsToUse = appearances.map(
140 (appearance) => appearanceOptions[appearance]
141 );
142 }
143
144 linkResult.fields.push({
145 name: 'appearance',
146 type: 'select',
147 admin: {
148 description: 'Choose how the link should be rendered.',
149 },
150 defaultValue: 'default',
151 options: appearanceOptionsToUse,
152 });
153 }
154
155 return deepMerge(linkResult, overrides);
156};