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};