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