@morphemecms/cms 0.11.0 → 0.13.0-next-20261106203613
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/astro.mjs +2 -1
- package/defaults/blockTexts.ts +9 -9
- package/defaults/collections.ts +257 -257
- package/defaults/colors.ts +74 -74
- package/defaults/company.ts +9 -9
- package/defaults/editor.ts +65 -65
- package/defaults/i18n/da-DK/blockTexts.ts +12 -0
- package/defaults/i18n/da-DK/collections.ts +220 -0
- package/defaults/i18n/da-DK/colors.ts +102 -0
- package/defaults/i18n/da-DK/company.ts +7 -0
- package/defaults/i18n/da-DK/editor.ts +13 -0
- package/defaults/i18n/da-DK/index.ts +31 -0
- package/defaults/i18n/da-DK/internalLinks.ts +12 -0
- package/defaults/i18n/da-DK/opens.ts +7 -0
- package/defaults/i18n/da-DK/popup.ts +9 -0
- package/defaults/i18n/da-DK/privacy.ts +38 -0
- package/defaults/i18n/da-DK/rules.ts +56 -0
- package/defaults/i18n/da-DK/sitedata.ts +8 -0
- package/defaults/i18n/da-DK/standard.ts +8 -0
- package/defaults/i18n/da-DK/standardPages.ts +17 -0
- package/defaults/internalLinks.ts +8 -8
- package/defaults/opens.ts +2 -2
- package/defaults/popup.tsx +7 -7
- package/defaults/privacy.ts +37 -37
- package/defaults/rules.ts +68 -66
- package/defaults/schema.ts +1 -1
- package/defaults/sitedata.ts +4 -4
- package/defaults/standard.ts +9 -6
- package/defaults/standardPages.ts +14 -14
- package/defaults/texts.ts +7 -510
- package/lib/appId.d.mts +1 -0
- package/lib/appId.mjs +6 -0
- package/lib/appRoute.d.mts +2 -2
- package/lib/appRoute.mjs +3 -3
- package/lib/appRoute.test.mjs +8 -8
- package/lib/apps.mjs +13 -3
- package/lib/apps.test.mjs +10 -1
- package/lib/contracts.d.mts +3 -2
- package/lib/contracts.mjs +3 -2
- package/lib/folderChanges.d.mts +29 -0
- package/lib/folderChanges.mjs +34 -0
- package/lib/licenses.mjs +2 -2
- package/lib/licenses.owner.test.mjs +9 -0
- package/lib/menu.mjs +4 -4
- package/lib/menu.test.mjs +101 -101
- package/lib/scheduled.mjs +1 -1
- package/lib/section-ranges.d.mts +1 -1
- package/lib/section-ranges.mjs +8 -14
- package/lib/section-ranges.test.mjs +26 -5
- package/lib/storeEntries.d.mts +3 -1
- package/lib/storeEntries.mjs +22 -8
- package/lib/storeEntries.test.mjs +59 -7
- package/lib/text-sections.d.mts +11 -1
- package/lib/text-sections.mjs +18 -8
- package/lib/text-sections.test.mjs +34 -1
- package/lib/translate.d.mts +11 -0
- package/lib/translate.mjs +13 -1
- package/lib/translate.test.mjs +23 -2
- package/lib/video-id.d.mts +6 -2
- package/lib/video-id.mjs +5 -1
- package/lib/video.d.mts +6 -2
- package/lib/video.mjs +5 -1
- package/lib/without-h1.d.mts +1 -0
- package/lib/without-h1.mjs +19 -4
- package/lib/without-h1.test.mjs +30 -1
- package/package.json +3 -2
- package/scripts/app-screenshots.mjs +9 -8
- package/scripts/check-content.mjs +12 -2
- package/scripts/check-language.mjs +0 -1
- package/scripts/check-site-types.mjs +26 -0
- package/scripts/editor-screens.mjs +3 -3
- package/scripts/site-types.mjs +14 -0
- package/scripts/site-types.test.mjs +21 -0
- package/site/activeTheme.test.mjs +52 -0
- package/site/appSelf.d.mts +2 -0
- package/site/appSelf.mjs +23 -0
- package/site/appSelf.test.mjs +35 -0
- package/site/content.ts +5 -2
- package/site/defineSite.mjs +2 -2
- package/site/schema.ts +3 -3
- package/site/siteModules.mjs +2 -1
- package/site/theme.mjs +19 -8
- package/src/Advanced.tsx +2 -2
- package/src/App.tsx +79 -78
- package/src/AppAdds.tsx +1 -1
- package/src/AppList.tsx +39 -24
- package/src/BlockDialog.tsx +39 -18
- package/src/BlockParts.tsx +6 -6
- package/src/BlockSettings.tsx +2 -2
- package/src/BrandForm.tsx +2 -2
- package/src/Colors.tsx +6 -6
- package/src/CompanyForm.tsx +1 -1
- package/src/Consents.tsx +3 -3
- package/src/CreateFab.tsx +8 -0
- package/src/DataForm.tsx +10 -20
- package/src/Deleted.tsx +3 -3
- package/src/EditorFooter.tsx +2 -2
- package/src/EntryForm.tsx +3 -3
- package/src/FooterSettings.tsx +2 -2
- package/src/FormFrame.tsx +4 -3
- package/src/HeaderSettings.tsx +3 -3
- package/src/Keywords.tsx +5 -5
- package/src/Library.tsx +75 -82
- package/src/Licenses.tsx +8 -8
- package/src/ListFab.tsx +13 -0
- package/src/MainDrawerParts.tsx +3 -3
- package/src/MainMenu.tsx +1 -1
- package/src/MarkdownEditor.tsx +88 -47
- package/src/Media.tsx +11 -20
- package/src/MediaTags.tsx +2 -2
- package/src/Notifications.tsx +2 -2
- package/src/Parts.tsx +17 -17
- package/src/Redirects.tsx +14 -22
- package/src/Screen.tsx +16 -6
- package/src/SeoScores.tsx +6 -6
- package/src/SideColumn.tsx +2 -2
- package/src/StartFab.tsx +6 -8
- package/src/ThemeStore.tsx +37 -16
- package/src/Tokens.tsx +2 -2
- package/src/apps.ts +7 -8
- package/src/checkSite.ts +86 -20
- package/src/drawerYield.d.mts +3 -0
- package/src/drawerYield.mjs +10 -0
- package/src/drawerYield.test.mjs +22 -0
- package/src/editor.css +59 -38
- package/src/examples.ts +1 -1
- package/src/fabScreens.test.mjs +28 -0
- package/src/fabScreens.ts +16 -0
- package/src/i18n/da-DK/library.ts +2 -2
- package/src/i18n/da-DK/text.ts +1 -1
- package/src/i18n/en-US/apps.ts +1 -1
- package/src/i18n/en-US/common.ts +8 -8
- package/src/i18n/en-US/library.ts +5 -5
- package/src/i18n/en-US/parts.ts +3 -3
- package/src/i18n/en-US/text.ts +1 -1
- package/src/i18n/index.ts +4 -8
- package/src/images.ts +2 -2
- package/src/legacyMove.test.mjs +137 -0
- package/src/legacyMove.ts +131 -0
- package/src/licenseData.ts +3 -3
- package/src/markdown.mjs +10 -7
- package/src/markdown.test.mjs +16 -2
- package/src/menuChildren.ts +5 -5
- package/src/menuOpen.ts +20 -4
- package/src/movedRoutes.d.mts +8 -1
- package/src/movedRoutes.mjs +102 -21
- package/src/movedRoutes.test.mjs +128 -6
- package/src/nav.ts +1 -1
- package/src/notificationData.ts +1 -1
- package/src/pageH1Panel.d.mts +4 -0
- package/src/pageH1Panel.mjs +31 -0
- package/src/pageH1Panel.test.mjs +56 -0
- package/src/pagePreview.tsx +22 -12
- package/src/schema.ts +2 -2
- package/src/sectionFrame.ts +3 -3
- package/src/sectionState.d.mts +3 -3
- package/src/sectionState.mjs +6 -6
- package/src/sectionState.test.mjs +14 -2
- package/src/sheetPlacement.d.mts +6 -4
- package/src/sheetPlacement.mjs +15 -10
- package/src/sheetPlacement.test.mjs +16 -9
- package/src/sidePanel.ts +38 -0
- package/src/site.ts +37 -13
- package/src/standard/accordion/index.tsx +1 -1
- package/src/standard/accordion/shared.ts +1 -1
- package/src/standard/buttons/index.tsx +1 -1
- package/src/standard/cards/index.tsx +1 -1
- package/src/standard/cards/shared.ts +1 -1
- package/src/standard/cookies/index.tsx +1 -1
- package/src/standard/posts/index.tsx +1 -1
- package/src/standard/testimonials/index.tsx +1 -1
- package/src/standard/testimonials/list.ts +1 -1
- package/src/store.ts +36 -31
- package/src/tagUses.d.mts +1 -1
- package/src/tagUses.mjs +2 -2
- package/src/tagUses.test.mjs +1 -1
- package/src/topBarSlot.tsx +7 -15
- package/src/ui.tsx +33 -22
- package/src/virtual.d.ts +6 -0
- package/starter/package.json +1 -1
- package/theme/components/PageHeading.astro +22 -0
- package/theme/layouts/Base.astro +2 -2
- package/theme/layouts/PageLayout.astro +37 -20
- package/theme/styles/global.css +2 -1
- package/theme/styles/ui.css +40 -10
package/defaults/collections.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// (packages/editor/site/*.ts); keep them in step.
|
|
4
4
|
// markdown: a folder of .md files (front matter + text): blog posts and pages.
|
|
5
5
|
// folder: a folder of .json files, one entry per file: categories, promotions (the testimonials are the standard
|
|
6
|
-
// block "
|
|
6
|
+
// block "Testimonials"'s, packages/editor/src/standard/testimonials/list.ts, an app's lists its own,
|
|
7
7
|
// src/apps/<app>/lists.ts).
|
|
8
8
|
// files: single .json files: the settings (the standard pages …), the footer and the menu.
|
|
9
9
|
import { AppWindow, ArrowsHorizontal, ColumnsPlusLeft, ColumnsPlusRight, File, SquareHalfBottom } from '@phosphor-icons/react';
|
|
@@ -13,32 +13,32 @@ import { config } from '@site/config';
|
|
|
13
13
|
import { standardPaths } from '@site/standardPages';
|
|
14
14
|
|
|
15
15
|
// The fields of one button of the action bar (the bottom bar on a phone, ActionBar.astro) and of the button in the header
|
|
16
|
-
// ("
|
|
16
|
+
// ("Header button", src/data/header.json): its text, where it leads, and the promotion or page it leads to.
|
|
17
17
|
const barButtonLabel: Field = {
|
|
18
18
|
name: 'label',
|
|
19
|
-
label: '
|
|
19
|
+
label: 'Button text',
|
|
20
20
|
type: 'string',
|
|
21
21
|
max: 30,
|
|
22
|
-
hint:
|
|
22
|
+
hint: "If the field is empty, the standard text or the page's name is used.",
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
const barButtonTo: Field = {
|
|
26
26
|
name: 'to',
|
|
27
|
-
label: '
|
|
27
|
+
label: 'Leads to',
|
|
28
28
|
type: 'select',
|
|
29
29
|
required: true,
|
|
30
30
|
default: 'booking',
|
|
31
31
|
options: [
|
|
32
|
-
{ label: 'Book
|
|
33
|
-
{ label: '
|
|
34
|
-
{ label: '
|
|
35
|
-
{ label: '
|
|
32
|
+
{ label: 'Book an appointment', value: 'booking' },
|
|
33
|
+
{ label: 'Call', value: 'phone' },
|
|
34
|
+
{ label: 'Open promotion', value: 'promotion', onPage: 'promotions' },
|
|
35
|
+
{ label: 'A page', value: 'page' },
|
|
36
36
|
],
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
const barButtonOpen: Field = {
|
|
40
40
|
name: 'open',
|
|
41
|
-
label: '
|
|
41
|
+
label: 'Promotion',
|
|
42
42
|
type: 'relation',
|
|
43
43
|
to: 'promotions',
|
|
44
44
|
onPage: true,
|
|
@@ -48,7 +48,7 @@ const barButtonOpen: Field = {
|
|
|
48
48
|
|
|
49
49
|
const barButtonHref: Field = {
|
|
50
50
|
name: 'href',
|
|
51
|
-
label: '
|
|
51
|
+
label: 'Page',
|
|
52
52
|
type: 'relation',
|
|
53
53
|
to: 'pages',
|
|
54
54
|
fixed: true,
|
|
@@ -57,39 +57,39 @@ const barButtonHref: Field = {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
// The page Google should count as the original of a page or a post (rel=canonical); empty is itself.
|
|
60
|
-
// Under "
|
|
60
|
+
// Under "Search engines" with the Google texts (EntryForm.tsx).
|
|
61
61
|
const canonical: Field = {
|
|
62
62
|
name: 'canonical',
|
|
63
63
|
label: 'Canonical URL',
|
|
64
64
|
type: 'relation',
|
|
65
65
|
to: 'pages',
|
|
66
66
|
fixed: true,
|
|
67
|
-
hint: '
|
|
67
|
+
hint: 'The page Google should show instead of this one.',
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
// Not for search engines: noindex, and not in the sitemap. Under "
|
|
70
|
+
// Not for search engines: noindex, and not in the sitemap. Under "Search engines" too.
|
|
71
71
|
const noindex: Field = {
|
|
72
72
|
name: 'noindex',
|
|
73
|
-
label: '
|
|
73
|
+
label: 'Hide from search engines (noindex)',
|
|
74
74
|
type: 'boolean',
|
|
75
|
-
hint: '
|
|
75
|
+
hint: 'The page is not shown in Google.',
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
// The text of the picture at the top of a post (a page has its pictures in its text): "
|
|
79
|
-
// it), else its
|
|
78
|
+
// The text of the picture at the top of a post (a page has its pictures in its text): "Decorative picture" (drawn with alt="", screen readers skip
|
|
79
|
+
// it), else its alt text.
|
|
80
80
|
const topImageText: Field[] = [
|
|
81
81
|
{
|
|
82
82
|
name: 'imageDecorative',
|
|
83
|
-
label: '
|
|
83
|
+
label: 'Decorative picture',
|
|
84
84
|
type: 'boolean',
|
|
85
|
-
toggle: ['
|
|
86
|
-
hint: '
|
|
85
|
+
toggle: ['On', 'Off'],
|
|
86
|
+
hint: 'Screen readers skip the picture.',
|
|
87
87
|
},
|
|
88
|
-
{ name: 'imageAlt', label: '
|
|
88
|
+
{ name: 'imageAlt', label: 'Alt text', type: 'string', only: { field: 'imageDecorative', is: ['', 'false'] } },
|
|
89
89
|
];
|
|
90
90
|
|
|
91
|
-
// "Status" at the top of a page or post (EntryForm.tsx):
|
|
92
|
-
//
|
|
91
|
+
// "Status" at the top of a page or post (EntryForm.tsx): Draft, Hidden, Published or Scheduled, and for
|
|
92
|
+
// Scheduled when (packages/editor/lib/status.mjs). Published is no `status` in the file. A post's date follows the time.
|
|
93
93
|
const status = (hidden: string): Field[] => [
|
|
94
94
|
{
|
|
95
95
|
name: 'status',
|
|
@@ -97,96 +97,96 @@ const status = (hidden: string): Field[] => [
|
|
|
97
97
|
type: 'select',
|
|
98
98
|
segments: true,
|
|
99
99
|
options: [
|
|
100
|
-
{ value: 'draft', label: '
|
|
101
|
-
{ value: 'unlisted', label: '
|
|
102
|
-
{ value: '', label: '
|
|
103
|
-
{ value: 'scheduled', label: '
|
|
100
|
+
{ value: 'draft', label: 'Draft', hint: 'Not shown on the website.' },
|
|
101
|
+
{ value: 'unlisted', label: 'Hidden', hint: hidden },
|
|
102
|
+
{ value: '', label: 'Published', hint: 'Shown on the website.' },
|
|
103
|
+
{ value: 'scheduled', label: 'Scheduled', hint: 'Shown on the website from the time below.' },
|
|
104
104
|
],
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
name: 'publishAt',
|
|
108
|
-
label: '
|
|
108
|
+
label: 'Time',
|
|
109
109
|
type: 'datetime',
|
|
110
110
|
required: true,
|
|
111
111
|
only: { field: 'status', is: ['scheduled'] },
|
|
112
|
-
hint: '
|
|
112
|
+
hint: 'Published at most an hour after.',
|
|
113
113
|
},
|
|
114
114
|
];
|
|
115
115
|
|
|
116
|
-
// The Google texts, and the picture when the page is shared, which the editor shows under "
|
|
116
|
+
// The Google texts, and the picture when the page is shared, which the editor shows under "Social media" (SHARE_FIELDS
|
|
117
117
|
// in packages/editor/src/seo.ts).
|
|
118
118
|
const seo: Field[] = [
|
|
119
119
|
{
|
|
120
120
|
name: 'seoTitle',
|
|
121
|
-
label: 'Google
|
|
121
|
+
label: 'Google title',
|
|
122
122
|
type: 'string',
|
|
123
|
-
hint: '
|
|
123
|
+
hint: 'At most about 60 characters.',
|
|
124
124
|
max: 70,
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
name: 'seoDescription',
|
|
128
|
-
label: 'Google
|
|
128
|
+
label: 'Google description',
|
|
129
129
|
type: 'text',
|
|
130
|
-
hint: '
|
|
130
|
+
hint: 'About 140-155 characters.',
|
|
131
131
|
max: 170,
|
|
132
132
|
},
|
|
133
133
|
// og:image (Base.astro), cut to 1200 × 630 by packages/editor/scripts/responsive-images.mjs.
|
|
134
134
|
{
|
|
135
135
|
name: 'shareImage',
|
|
136
|
-
label: '
|
|
136
|
+
label: 'Picture when shared',
|
|
137
137
|
type: 'image',
|
|
138
|
-
hint: '
|
|
138
|
+
hint: 'Shown when the page is shared, e.g. on Facebook.',
|
|
139
139
|
},
|
|
140
140
|
];
|
|
141
141
|
|
|
142
|
-
// The title and description when a page or post is shared (og:title, og:description in Base.astro), under "
|
|
143
|
-
//
|
|
142
|
+
// The title and description when a page or post is shared (og:title, og:description in Base.astro), under "Social
|
|
143
|
+
// media" with the picture.
|
|
144
144
|
const shareTexts: Field[] = [
|
|
145
145
|
{
|
|
146
146
|
name: 'shareTitle',
|
|
147
|
-
label: '
|
|
147
|
+
label: 'Title when shared',
|
|
148
148
|
type: 'string',
|
|
149
|
-
hint: '
|
|
149
|
+
hint: 'If the field is empty, the Google title is used.',
|
|
150
150
|
max: 70,
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
153
|
name: 'shareDescription',
|
|
154
|
-
label: '
|
|
154
|
+
label: 'Description when shared',
|
|
155
155
|
type: 'text',
|
|
156
|
-
hint: '
|
|
156
|
+
hint: 'If the field is empty, the Google description is used.',
|
|
157
157
|
max: 170,
|
|
158
158
|
},
|
|
159
159
|
];
|
|
160
160
|
|
|
161
161
|
// The words that link to a page or post from the others (packages/editor/site/autolinks.mjs). Under
|
|
162
|
-
// "
|
|
162
|
+
// "Search engines" with the Google texts (EntryForm.tsx).
|
|
163
163
|
const keywords: Field[] = [
|
|
164
164
|
{
|
|
165
165
|
name: 'keyword',
|
|
166
|
-
label: '
|
|
166
|
+
label: 'Main keyword',
|
|
167
167
|
type: 'string',
|
|
168
168
|
chips: 'one',
|
|
169
|
-
hint: '
|
|
169
|
+
hint: 'The word becomes a link to here from other pages.',
|
|
170
170
|
},
|
|
171
171
|
{
|
|
172
172
|
name: 'keywords',
|
|
173
|
-
label: '
|
|
173
|
+
label: 'Other keywords',
|
|
174
174
|
type: 'string',
|
|
175
175
|
chips: 'many',
|
|
176
|
-
hint: '
|
|
176
|
+
hint: 'Each word becomes a link to here from other pages.',
|
|
177
177
|
},
|
|
178
178
|
];
|
|
179
179
|
|
|
180
|
-
// The business, on "
|
|
181
|
-
// (src/data/contact.json), shown at the top and bottom of every page and in the blocks that show them ("
|
|
182
|
-
// "
|
|
180
|
+
// The business, on "Business" (CompanyForm.tsx): its name and title, phone, e-mail, address and CVR number
|
|
181
|
+
// (src/data/contact.json), shown at the top and bottom of every page and in the blocks that show them ("Contact" and
|
|
182
|
+
// "Address"); and the bank details (src/data/payment.json), shown in "Payment". The calendar
|
|
183
183
|
// is the app Calendly's (morpheme-apps/apps/calendly/).
|
|
184
184
|
export const contactFields: Field[] = [
|
|
185
|
-
{ name: 'name', label: '
|
|
186
|
-
{ name: 'role', label: '
|
|
185
|
+
{ name: 'name', label: 'Name', type: 'string', required: true },
|
|
186
|
+
{ name: 'role', label: 'Title', type: 'string', required: true },
|
|
187
187
|
{
|
|
188
188
|
name: 'phone',
|
|
189
|
-
label: '
|
|
189
|
+
label: 'Phone',
|
|
190
190
|
type: 'string',
|
|
191
191
|
required: true,
|
|
192
192
|
digitsInPairs: 8,
|
|
@@ -194,41 +194,41 @@ export const contactFields: Field[] = [
|
|
|
194
194
|
},
|
|
195
195
|
{
|
|
196
196
|
name: 'email',
|
|
197
|
-
label: '
|
|
197
|
+
label: 'Email',
|
|
198
198
|
type: 'string',
|
|
199
199
|
required: true,
|
|
200
200
|
pattern: [
|
|
201
201
|
'^[A-Za-z0-9._%+-]+@[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)*\\.[A-Za-z]{2,}$',
|
|
202
|
-
'
|
|
202
|
+
'Write a valid email address',
|
|
203
203
|
],
|
|
204
204
|
},
|
|
205
205
|
{
|
|
206
206
|
name: 'street',
|
|
207
|
-
label: '
|
|
207
|
+
label: 'Street and number',
|
|
208
208
|
type: 'string',
|
|
209
209
|
required: true,
|
|
210
210
|
},
|
|
211
|
-
{ name: 'city', label: '
|
|
211
|
+
{ name: 'city', label: 'Postcode and town', type: 'string', required: true },
|
|
212
212
|
{
|
|
213
213
|
name: 'cvr',
|
|
214
|
-
label: 'CVR
|
|
214
|
+
label: 'CVR number',
|
|
215
215
|
type: 'string',
|
|
216
|
-
hint: '
|
|
217
|
-
pattern: ['^\\d{8}$', '
|
|
216
|
+
hint: 'Shown at the bottom of every page.',
|
|
217
|
+
pattern: ['^\\d{8}$', 'A CVR number has eight digits'],
|
|
218
218
|
},
|
|
219
219
|
];
|
|
220
220
|
|
|
221
|
-
// The social media (src/data/social.json, a part of its own under "
|
|
221
|
+
// The social media (src/data/social.json, a part of its own under "Business", company.ts): the site's profiles, and
|
|
222
222
|
// which share buttons a post shows. A switch that is not in the file is on (company.ts gives the form that default).
|
|
223
223
|
// A profile address: empty, or one that begins with https://.
|
|
224
|
-
const httpsAddress: [string, string] = ['^$|^https://\\S+$', '
|
|
224
|
+
const httpsAddress: [string, string] = ['^$|^https://\\S+$', 'Write an address that starts with https://'];
|
|
225
225
|
|
|
226
226
|
export const socialFields: Field[] = [
|
|
227
227
|
{
|
|
228
228
|
name: 'facebook',
|
|
229
229
|
label: 'Facebook',
|
|
230
230
|
type: 'string',
|
|
231
|
-
hint:
|
|
231
|
+
hint: "The address of the site's profile. Shown in Google's data about the business.",
|
|
232
232
|
pattern: httpsAddress,
|
|
233
233
|
},
|
|
234
234
|
{
|
|
@@ -267,94 +267,94 @@ export const socialFields: Field[] = [
|
|
|
267
267
|
type: 'string',
|
|
268
268
|
pattern: httpsAddress,
|
|
269
269
|
},
|
|
270
|
-
{ name: 'shareFacebook', label: '
|
|
271
|
-
{ name: 'shareX', label: '
|
|
272
|
-
{ name: 'shareLinkedin', label: '
|
|
273
|
-
{ name: 'sharePinterest', label: '
|
|
274
|
-
{ name: 'shareCopyLink', label: '
|
|
270
|
+
{ name: 'shareFacebook', label: 'Share on Facebook', type: 'boolean' },
|
|
271
|
+
{ name: 'shareX', label: 'Share on X', type: 'boolean' },
|
|
272
|
+
{ name: 'shareLinkedin', label: 'Share on LinkedIn', type: 'boolean' },
|
|
273
|
+
{ name: 'sharePinterest', label: 'Share on Pinterest', type: 'boolean', hint: 'Shown only when the post has a picture.' },
|
|
274
|
+
{ name: 'shareCopyLink', label: 'Copy link', type: 'boolean' },
|
|
275
275
|
];
|
|
276
276
|
|
|
277
|
-
// The bank details, a part of its own under "
|
|
277
|
+
// The bank details, a part of its own under "Payment" (company.ts, src/data/payment.json).
|
|
278
278
|
export const bankFields: Field[] = [
|
|
279
279
|
{ name: 'bank', label: 'Bank', type: 'string', required: true },
|
|
280
|
-
{ name: 'reg', label: 'Reg.
|
|
281
|
-
{ name: 'account', label: '
|
|
280
|
+
{ name: 'reg', label: 'Reg. no.', type: 'string', required: true },
|
|
281
|
+
{ name: 'account', label: 'Account number', type: 'string', required: true },
|
|
282
282
|
];
|
|
283
283
|
|
|
284
284
|
// The standard pages (src/data/standard-pages.json, config.standardPages, packages/editor/defaults/standardPages.ts), each a field that
|
|
285
285
|
// picks a page, as rows of a list (Part.rows): the front page, shown at / (packages/editor/site/home.ts), the blog page, shown at
|
|
286
286
|
// /blog/, the page shown for an address that does not exist (packages/editor/site/standardPages.ts) and the contact page under
|
|
287
|
-
// "
|
|
288
|
-
// "
|
|
287
|
+
// "Appearance" › "Default pages" (appearance in editor.ts), and the privacy policy and the cookie policy under "Privacy" ›
|
|
288
|
+
// "Privacy pages" (privacy.ts). Different pages, but the cookie policy may be the privacy policy (keptBy in rules.ts).
|
|
289
289
|
const standardPageFields: Field[] = [
|
|
290
290
|
{
|
|
291
291
|
name: 'front',
|
|
292
|
-
label: '
|
|
292
|
+
label: 'Front page',
|
|
293
293
|
type: 'relation',
|
|
294
294
|
to: 'standardPages',
|
|
295
295
|
required: true,
|
|
296
|
-
hint: '
|
|
296
|
+
hint: 'The page is shown on the front page, and its own address sends on there.',
|
|
297
297
|
},
|
|
298
298
|
{
|
|
299
299
|
name: 'blog',
|
|
300
|
-
label: '
|
|
300
|
+
label: 'Blog page',
|
|
301
301
|
type: 'relation',
|
|
302
302
|
to: 'standardPages',
|
|
303
303
|
required: true,
|
|
304
|
-
hint: '
|
|
304
|
+
hint: 'The page is shown at /blog/, and its own address sends on there.',
|
|
305
305
|
},
|
|
306
306
|
{
|
|
307
307
|
name: 'notFound',
|
|
308
|
-
label: '404
|
|
308
|
+
label: '404 page',
|
|
309
309
|
type: 'relation',
|
|
310
310
|
to: 'standardPages',
|
|
311
311
|
required: true,
|
|
312
|
-
hint: '
|
|
312
|
+
hint: 'The page is shown when an address does not exist.',
|
|
313
313
|
},
|
|
314
314
|
{
|
|
315
315
|
name: 'contact',
|
|
316
|
-
label: '
|
|
316
|
+
label: 'Contact page',
|
|
317
317
|
type: 'relation',
|
|
318
318
|
to: 'standardPages',
|
|
319
319
|
required: true,
|
|
320
|
-
hint: '
|
|
320
|
+
hint: 'The link to the directions goes here.',
|
|
321
321
|
},
|
|
322
322
|
{
|
|
323
323
|
name: 'privacy',
|
|
324
|
-
label: '
|
|
324
|
+
label: 'Privacy policy',
|
|
325
325
|
type: 'relation',
|
|
326
326
|
to: 'standardPages',
|
|
327
327
|
required: true,
|
|
328
|
-
hint: '
|
|
328
|
+
hint: 'The page that tells what information is stored, and why.',
|
|
329
329
|
},
|
|
330
330
|
{
|
|
331
331
|
name: 'cookies',
|
|
332
|
-
label: '
|
|
332
|
+
label: 'Cookie policy',
|
|
333
333
|
type: 'relation',
|
|
334
334
|
to: 'standardPages',
|
|
335
335
|
required: true,
|
|
336
|
-
hint: '
|
|
336
|
+
hint: "The cookie banner's link goes here; it may be the privacy policy.",
|
|
337
337
|
},
|
|
338
338
|
];
|
|
339
339
|
|
|
340
340
|
/** The fields of the standard pages by their keys, in that order. */
|
|
341
341
|
export const standardPageFieldsOf = (...keys: string[]) => keys.map((key) => standardPageFields.find((f) => f.name === key)!);
|
|
342
342
|
|
|
343
|
-
/** The "Book
|
|
344
|
-
* the shared ones ("
|
|
343
|
+
/** The "Book an appointment" box's own heading and text, in a page's text; empty,
|
|
344
|
+
* the shared ones ("Blocks" › "Book an appointment", booking.json). */
|
|
345
345
|
export const bookingTexts: Field[] = [
|
|
346
346
|
{
|
|
347
347
|
name: 'heading',
|
|
348
|
-
label: '
|
|
348
|
+
label: 'Heading',
|
|
349
349
|
type: 'string',
|
|
350
|
-
hint: '
|
|
350
|
+
hint: 'If empty, the default text is used.',
|
|
351
351
|
placeholder: (site) => site.booking.heading,
|
|
352
352
|
},
|
|
353
353
|
{
|
|
354
354
|
name: 'text',
|
|
355
|
-
label: '
|
|
355
|
+
label: 'Text',
|
|
356
356
|
type: 'text',
|
|
357
|
-
hint: '
|
|
357
|
+
hint: 'If empty, the default text is used.',
|
|
358
358
|
placeholder: (site) => site.booking.text,
|
|
359
359
|
},
|
|
360
360
|
];
|
|
@@ -362,64 +362,64 @@ export const bookingTexts: Field[] = [
|
|
|
362
362
|
// The icons the owner picks from on the site's own fields (the same as the standard blocks', iconNames in
|
|
363
363
|
// packages/editor/src/icon.tsx, which draws them).
|
|
364
364
|
export const icons = [
|
|
365
|
-
['
|
|
366
|
-
['
|
|
367
|
-
['
|
|
368
|
-
['
|
|
369
|
-
['
|
|
370
|
-
['
|
|
371
|
-
['
|
|
372
|
-
['
|
|
373
|
-
['
|
|
374
|
-
['
|
|
375
|
-
['
|
|
376
|
-
['
|
|
377
|
-
['
|
|
378
|
-
['
|
|
379
|
-
['
|
|
365
|
+
['Diploma', 'certificate'],
|
|
366
|
+
['Heart', 'heart'],
|
|
367
|
+
['Hand with heart', 'hand-heart'],
|
|
368
|
+
['Three people', 'users-three'],
|
|
369
|
+
['Two people', 'users'],
|
|
370
|
+
['Graduation cap', 'graduation-cap'],
|
|
371
|
+
['Handshake', 'handshake'],
|
|
372
|
+
['Book', 'book-open'],
|
|
373
|
+
['Book with text', 'book-open-text'],
|
|
374
|
+
['Medal', 'medal'],
|
|
375
|
+
['Conversation', 'chats'],
|
|
376
|
+
['Lotus flower', 'flower-lotus'],
|
|
377
|
+
['Leaf', 'leaf'],
|
|
378
|
+
['Light bulb', 'lightbulb'],
|
|
379
|
+
['Train', 'train'],
|
|
380
380
|
['Bus', 'bus'],
|
|
381
|
-
['
|
|
382
|
-
['
|
|
383
|
-
['
|
|
384
|
-
['
|
|
385
|
-
['
|
|
386
|
-
['
|
|
387
|
-
['
|
|
388
|
-
['
|
|
389
|
-
['
|
|
390
|
-
['
|
|
391
|
-
['
|
|
392
|
-
['
|
|
381
|
+
['Bicycle', 'bicycle'],
|
|
382
|
+
['Car', 'car'],
|
|
383
|
+
['Door', 'door-open'],
|
|
384
|
+
['Video camera', 'video-camera'],
|
|
385
|
+
['Lifebuoy', 'lifebuoy'],
|
|
386
|
+
['Phone', 'phone'],
|
|
387
|
+
['Clock', 'clock'],
|
|
388
|
+
['Map pin', 'map-pin'],
|
|
389
|
+
['Coffee', 'coffee'],
|
|
390
|
+
['Wheelchair', 'wheelchair'],
|
|
391
|
+
['Information', 'info'],
|
|
392
|
+
['Letter', 'envelope-simple'],
|
|
393
393
|
].map(([label, value]) => ({ label, value }));
|
|
394
394
|
|
|
395
395
|
// A link in the menu: any page, the front page and the blog too, stored by its address.
|
|
396
|
-
const menuHref: Field = { name: 'href', label: '
|
|
396
|
+
const menuHref: Field = { name: 'href', label: 'Page', type: 'relation', to: 'pages', fixed: true, required: true };
|
|
397
397
|
|
|
398
398
|
export const collections: Collection[] = [
|
|
399
399
|
{
|
|
400
400
|
kind: 'markdown',
|
|
401
401
|
id: 'blog',
|
|
402
|
-
create: '
|
|
403
|
-
label: '
|
|
404
|
-
singular: '
|
|
402
|
+
create: 'New blog post',
|
|
403
|
+
label: 'Blog posts',
|
|
404
|
+
singular: 'blog post',
|
|
405
405
|
folder: 'src/content/blog',
|
|
406
406
|
deletable: true,
|
|
407
407
|
fields: [
|
|
408
|
-
...status('
|
|
409
|
-
{ name: 'title', label: '
|
|
410
|
-
{ name: 'date', label: '
|
|
411
|
-
{ name: 'path', label: '
|
|
412
|
-
{ name: 'description', label: '
|
|
408
|
+
...status('Not shown on the blog or in Google, but can be opened at its address.'),
|
|
409
|
+
{ name: 'title', label: 'Title', type: 'string', required: true },
|
|
410
|
+
{ name: 'date', label: 'Date', type: 'date', required: true },
|
|
411
|
+
{ name: 'path', label: 'URL', type: 'string', address: 'post' },
|
|
412
|
+
{ name: 'description', label: 'Short description', type: 'text' },
|
|
413
413
|
{
|
|
414
414
|
name: 'categories',
|
|
415
415
|
label: 'Tags',
|
|
416
416
|
type: 'relation',
|
|
417
417
|
to: 'categories',
|
|
418
418
|
multiple: true,
|
|
419
|
-
add: '
|
|
420
|
-
edit: { to: '
|
|
419
|
+
add: 'Add tag',
|
|
420
|
+
edit: { to: 'new/tags', text: 'Create a tag' },
|
|
421
421
|
},
|
|
422
|
-
{ name: 'image', label: '
|
|
422
|
+
{ name: 'image', label: 'Picture at the top', type: 'image' },
|
|
423
423
|
...topImageText,
|
|
424
424
|
...seo,
|
|
425
425
|
...shareTexts,
|
|
@@ -431,46 +431,46 @@ export const collections: Collection[] = [
|
|
|
431
431
|
{
|
|
432
432
|
kind: 'markdown',
|
|
433
433
|
id: 'pages',
|
|
434
|
-
create: '
|
|
435
|
-
label: '
|
|
436
|
-
singular: '
|
|
434
|
+
create: 'New page',
|
|
435
|
+
label: 'Pages',
|
|
436
|
+
singular: 'page',
|
|
437
437
|
folder: 'src/content/pages',
|
|
438
438
|
fields: [
|
|
439
|
-
...status('
|
|
440
|
-
{ name: 'title', label: '
|
|
441
|
-
{ name: 'path', label: '
|
|
442
|
-
{ name: 'description', label: '
|
|
439
|
+
...status('Not shown in Google, but can be opened at its address.'),
|
|
440
|
+
{ name: 'title', label: 'Title', type: 'string', required: true },
|
|
441
|
+
{ name: 'path', label: 'URL', type: 'string', address: 'page' },
|
|
442
|
+
{ name: 'description', label: 'Short description', type: 'text' },
|
|
443
443
|
{
|
|
444
444
|
name: 'categories',
|
|
445
445
|
label: 'Tags',
|
|
446
446
|
type: 'relation',
|
|
447
447
|
to: 'categories',
|
|
448
448
|
multiple: true,
|
|
449
|
-
add: '
|
|
450
|
-
edit: { to: '
|
|
451
|
-
hint: '
|
|
449
|
+
add: 'Add tag',
|
|
450
|
+
edit: { to: 'new/tags', text: 'Create a tag' },
|
|
451
|
+
hint: 'Not shown on the website.',
|
|
452
452
|
},
|
|
453
453
|
{
|
|
454
454
|
name: 'template',
|
|
455
|
-
label: '
|
|
455
|
+
label: 'Template',
|
|
456
456
|
type: 'select',
|
|
457
457
|
segments: true,
|
|
458
458
|
layout: true,
|
|
459
459
|
// The options the theme in use offers, the first of its list the default (templateFieldOf in src/templateField.ts).
|
|
460
460
|
offered: 'templates',
|
|
461
461
|
options: [
|
|
462
|
-
{ label: '
|
|
463
|
-
{ label: '
|
|
464
|
-
{ label: '
|
|
465
|
-
{ label: '
|
|
462
|
+
{ label: 'Single column', value: 'single', icon: File },
|
|
463
|
+
{ label: 'Full width', value: 'full', icon: ArrowsHorizontal },
|
|
464
|
+
{ label: 'Sidebar left', value: 'sidebar-left', icon: ColumnsPlusLeft },
|
|
465
|
+
{ label: 'Sidebar right', value: 'sidebar-right', icon: ColumnsPlusRight },
|
|
466
466
|
],
|
|
467
|
-
hint: '
|
|
467
|
+
hint: 'The theme decides which templates there are.',
|
|
468
468
|
},
|
|
469
469
|
{
|
|
470
470
|
name: 'review',
|
|
471
|
-
label: '
|
|
471
|
+
label: 'For review',
|
|
472
472
|
type: 'text',
|
|
473
|
-
hint: '
|
|
473
|
+
hint: 'Only shown on pages under /ny/.',
|
|
474
474
|
},
|
|
475
475
|
...seo,
|
|
476
476
|
...shareTexts,
|
|
@@ -479,14 +479,14 @@ export const collections: Collection[] = [
|
|
|
479
479
|
noindex,
|
|
480
480
|
],
|
|
481
481
|
blocks: true,
|
|
482
|
-
// One "Book
|
|
482
|
+
// One "Book an appointment" a page (at the very end it goes across the page, PageLayout.astro). A new page
|
|
483
483
|
// starts empty.
|
|
484
484
|
template: { max: { booking: 1 } },
|
|
485
485
|
},
|
|
486
486
|
{
|
|
487
487
|
kind: 'folder',
|
|
488
|
-
id: '
|
|
489
|
-
create: '
|
|
488
|
+
id: 'tags',
|
|
489
|
+
create: 'New tag',
|
|
490
490
|
label: 'Tags',
|
|
491
491
|
singular: 'tag',
|
|
492
492
|
folder: 'src/data/categories',
|
|
@@ -494,29 +494,29 @@ export const collections: Collection[] = [
|
|
|
494
494
|
slugFrom: 'name',
|
|
495
495
|
summary: (d) => String(d.name ?? ''),
|
|
496
496
|
fields: [
|
|
497
|
-
{ name: 'name', label: '
|
|
497
|
+
{ name: 'name', label: 'Name', type: 'string', required: true },
|
|
498
498
|
...seo,
|
|
499
499
|
{
|
|
500
500
|
name: 'blogCategory',
|
|
501
|
-
label: '
|
|
501
|
+
label: 'Blog category',
|
|
502
502
|
type: 'boolean',
|
|
503
|
-
toggle: ['
|
|
503
|
+
toggle: ['Show', 'Hide'],
|
|
504
504
|
default: true,
|
|
505
|
-
hint: '
|
|
505
|
+
hint: 'Show the tag on the blog with its own page.',
|
|
506
506
|
},
|
|
507
507
|
],
|
|
508
508
|
},
|
|
509
509
|
{
|
|
510
|
-
// "
|
|
511
|
-
// src/data/popups/<slug>.json, read by packages/editor/site/popups.ts, on every page or those picked under "
|
|
510
|
+
// "Promotion": what is laid over the pages to get visitors to book, call or sign up, in
|
|
511
|
+
// src/data/popups/<slug>.json, read by packages/editor/site/popups.ts, on every page or those picked under "Pages".
|
|
512
512
|
// The pop-ups are shown by packages/editor/theme/components/Popup.astro; a kind an app adds without a part
|
|
513
513
|
// in the pop-up (src/apps/<app>/popups/View.tsx) shows itself (the app Calendly's booking sheet,
|
|
514
514
|
// morpheme-apps/apps/calendly/, when a button opens it).
|
|
515
515
|
kind: 'folder',
|
|
516
516
|
id: 'popups',
|
|
517
|
-
create: '
|
|
518
|
-
label: '
|
|
519
|
-
singular: '
|
|
517
|
+
create: 'New promotion',
|
|
518
|
+
label: 'Promotion',
|
|
519
|
+
singular: 'promotion',
|
|
520
520
|
folder: 'src/data/popups',
|
|
521
521
|
deletable: true,
|
|
522
522
|
// A pop-up by its heading; a kind without one by its name (the app's bar), else by the kind's name
|
|
@@ -534,8 +534,8 @@ export const collections: Collection[] = [
|
|
|
534
534
|
omit: {
|
|
535
535
|
bookingbar: ['image', 'heading', 'text', 'trigger', 'button'],
|
|
536
536
|
},
|
|
537
|
-
// What a button ("
|
|
538
|
-
opens: { standard: { label: 'Pop
|
|
537
|
+
// What a button ("Buttons": "Open promotion") can open of one: a pop-up itself, the action bar nothing (opens.ts).
|
|
538
|
+
opens: { standard: { label: 'Pop-up' } },
|
|
539
539
|
// A contact bar (`contactbar`, a kind of its own before it became an action bar) is an action bar with its two
|
|
540
540
|
// buttons, to call and to its page, without one the contact page (packages/editor/lib/contactBar.ts).
|
|
541
541
|
upgrade: (data, files) => fromContactBar(data, standardPaths(files.find((f) => f.path === config.standardPages)?.text).contact ?? ''),
|
|
@@ -548,49 +548,49 @@ export const collections: Collection[] = [
|
|
|
548
548
|
// Without one (those made before there were kinds), it is the standard one.
|
|
549
549
|
default: 'standard',
|
|
550
550
|
options: [
|
|
551
|
-
{ label: 'Pop
|
|
551
|
+
{ label: 'Pop-up', value: 'standard', icon: AppWindow, hint: 'With a picture, text and a button.' },
|
|
552
552
|
{
|
|
553
|
-
label: '
|
|
553
|
+
label: 'Action bar',
|
|
554
554
|
value: 'bookingbar',
|
|
555
555
|
icon: SquareHalfBottom,
|
|
556
|
-
hint: '
|
|
556
|
+
hint: 'Buttons or text at the bottom of the screen on a phone.',
|
|
557
557
|
},
|
|
558
558
|
],
|
|
559
559
|
},
|
|
560
560
|
{
|
|
561
561
|
name: 'enabled',
|
|
562
|
-
label: '
|
|
562
|
+
label: 'Activated',
|
|
563
563
|
type: 'boolean',
|
|
564
|
-
toggle: ['
|
|
564
|
+
toggle: ['On', 'Off'],
|
|
565
565
|
default: true,
|
|
566
566
|
},
|
|
567
|
-
// At the top of the box, to its edges in 3:2, as on the cards with a picture ("
|
|
568
|
-
{ name: 'image', label: '
|
|
569
|
-
{ name: 'heading', label: '
|
|
570
|
-
{ name: 'text', label: '
|
|
567
|
+
// At the top of the box, to its edges in 3:2, as on the cards with a picture ("Cards").
|
|
568
|
+
{ name: 'image', label: 'Picture', type: 'image', hint: 'Shown at the top, cropped to 3:2.' },
|
|
569
|
+
{ name: 'heading', label: 'Heading', type: 'string', required: true, max: 80 },
|
|
570
|
+
{ name: 'text', label: 'Text', type: 'text' },
|
|
571
571
|
// Where it is shown, for every kind: on every page, on those picked (without `where`, from
|
|
572
572
|
// before, those picked), or on the blog posts with one of the tags picked and those tags' pages.
|
|
573
573
|
{
|
|
574
574
|
name: 'where',
|
|
575
|
-
label: '
|
|
575
|
+
label: 'Shown on',
|
|
576
576
|
type: 'select',
|
|
577
577
|
default: 'some',
|
|
578
578
|
options: [
|
|
579
|
-
{ label: '
|
|
580
|
-
{ label: '
|
|
581
|
-
{ label: '
|
|
579
|
+
{ label: 'All pages', value: 'all' },
|
|
580
|
+
{ label: 'Selected pages', value: 'some' },
|
|
581
|
+
{ label: 'Blog posts with tags', value: 'tags' },
|
|
582
582
|
],
|
|
583
583
|
},
|
|
584
584
|
{
|
|
585
585
|
name: 'pages',
|
|
586
|
-
label: '
|
|
586
|
+
label: 'Pages',
|
|
587
587
|
type: 'relation',
|
|
588
588
|
to: 'pages',
|
|
589
589
|
fixed: true,
|
|
590
590
|
multiple: true,
|
|
591
591
|
required: true,
|
|
592
|
-
add: '
|
|
593
|
-
hint: '
|
|
592
|
+
add: 'Add page',
|
|
593
|
+
hint: 'Only shown on these pages.',
|
|
594
594
|
only: { field: 'where', is: ['some'] },
|
|
595
595
|
},
|
|
596
596
|
{
|
|
@@ -600,92 +600,92 @@ export const collections: Collection[] = [
|
|
|
600
600
|
to: 'categories',
|
|
601
601
|
multiple: true,
|
|
602
602
|
required: true,
|
|
603
|
-
add: '
|
|
604
|
-
hint:
|
|
603
|
+
add: 'Add tag',
|
|
604
|
+
hint: "Shown on blog posts with one of these tags and on the tags' pages.",
|
|
605
605
|
only: { field: 'where', is: ['tags'] },
|
|
606
606
|
},
|
|
607
607
|
// When it opens (packages/editor/theme/components/Popup.astro): after `delay`, at `scroll` % down the page, or as the
|
|
608
608
|
// visitor is about to leave it.
|
|
609
609
|
{
|
|
610
610
|
name: 'trigger',
|
|
611
|
-
label: '
|
|
611
|
+
label: 'Shown',
|
|
612
612
|
type: 'select',
|
|
613
613
|
required: true,
|
|
614
614
|
default: 'time',
|
|
615
|
-
hint: '
|
|
615
|
+
hint: 'Each visitor sees it until they close it.',
|
|
616
616
|
options: [
|
|
617
|
-
{ label: '
|
|
618
|
-
{ label: '
|
|
617
|
+
{ label: 'After a while', value: 'time' },
|
|
618
|
+
{ label: 'When the page is scrolled down', value: 'scroll' },
|
|
619
619
|
{
|
|
620
|
-
label: '
|
|
620
|
+
label: 'When the visitor is about to leave the page',
|
|
621
621
|
value: 'exit',
|
|
622
|
-
hint: '
|
|
622
|
+
hint: 'On a phone: when the visitor scrolls up quickly.',
|
|
623
623
|
},
|
|
624
624
|
],
|
|
625
625
|
},
|
|
626
626
|
{
|
|
627
627
|
name: 'delay',
|
|
628
|
-
label: '
|
|
628
|
+
label: 'Shown after',
|
|
629
629
|
type: 'select',
|
|
630
630
|
required: true,
|
|
631
631
|
default: '5',
|
|
632
632
|
only: { field: 'trigger', is: ['time'] },
|
|
633
633
|
options: [
|
|
634
|
-
{ label: '
|
|
635
|
-
{ label: '5
|
|
636
|
-
{ label: '10
|
|
637
|
-
{ label: '30
|
|
634
|
+
{ label: 'At once', value: '0' },
|
|
635
|
+
{ label: '5 seconds', value: '5' },
|
|
636
|
+
{ label: '10 seconds', value: '10' },
|
|
637
|
+
{ label: '30 seconds', value: '30' },
|
|
638
638
|
],
|
|
639
639
|
},
|
|
640
640
|
{
|
|
641
641
|
name: 'scroll',
|
|
642
|
-
label: '
|
|
642
|
+
label: 'Scrolled down',
|
|
643
643
|
type: 'select',
|
|
644
644
|
required: true,
|
|
645
645
|
default: '25',
|
|
646
646
|
options: [
|
|
647
|
-
{ label: '25
|
|
648
|
-
{ label: '50
|
|
649
|
-
{ label: '75
|
|
650
|
-
{ label: '
|
|
647
|
+
{ label: '25% of the page', value: '25' },
|
|
648
|
+
{ label: '50% of the page', value: '50' },
|
|
649
|
+
{ label: '75% of the page', value: '75' },
|
|
650
|
+
{ label: 'The whole page', value: '100' },
|
|
651
651
|
],
|
|
652
652
|
only: { field: 'trigger', is: ['scroll'] },
|
|
653
653
|
},
|
|
654
654
|
{
|
|
655
655
|
name: 'button',
|
|
656
|
-
label: '
|
|
656
|
+
label: 'Button',
|
|
657
657
|
type: 'select',
|
|
658
658
|
required: true,
|
|
659
659
|
default: 'booking',
|
|
660
660
|
options: [
|
|
661
|
-
{ label: 'Book
|
|
662
|
-
{ label: '
|
|
663
|
-
{ label: '
|
|
661
|
+
{ label: 'Book an appointment', value: 'booking' },
|
|
662
|
+
{ label: 'Call', value: 'phone' },
|
|
663
|
+
{ label: 'A page', value: 'page' },
|
|
664
664
|
{ label: 'Download PDF', value: 'file' },
|
|
665
|
-
{ label: '
|
|
665
|
+
{ label: 'No button', value: 'none' },
|
|
666
666
|
],
|
|
667
667
|
},
|
|
668
668
|
{
|
|
669
669
|
name: 'label',
|
|
670
|
-
label: '
|
|
670
|
+
label: 'Button text',
|
|
671
671
|
type: 'string',
|
|
672
672
|
max: 30,
|
|
673
|
-
hint: '
|
|
673
|
+
hint: 'If empty, the default text is used.',
|
|
674
674
|
only: { field: 'button', is: ['booking', 'phone', 'page', 'file'] },
|
|
675
675
|
},
|
|
676
|
-
// A PDF from "
|
|
676
|
+
// A PDF from "Files" (public/filer/), by its address (/filer/<name>.pdf).
|
|
677
677
|
{
|
|
678
678
|
name: 'file',
|
|
679
679
|
label: 'PDF',
|
|
680
680
|
type: 'relation',
|
|
681
681
|
to: 'files',
|
|
682
682
|
required: true,
|
|
683
|
-
edit: { to: '
|
|
683
|
+
edit: { to: 'files', text: 'Upload a PDF' },
|
|
684
684
|
only: { field: 'button', is: ['file'] },
|
|
685
685
|
},
|
|
686
686
|
{
|
|
687
687
|
name: 'href',
|
|
688
|
-
label: '
|
|
688
|
+
label: 'Page (button)',
|
|
689
689
|
type: 'relation',
|
|
690
690
|
to: 'pages',
|
|
691
691
|
fixed: true,
|
|
@@ -693,20 +693,20 @@ export const collections: Collection[] = [
|
|
|
693
693
|
only: { field: 'button', is: ['page'] },
|
|
694
694
|
},
|
|
695
695
|
// The action bar's one or two items, each a button or a text (`type`; without one, a button, as before there were
|
|
696
|
-
// texts). The first button is filled, a second with a line. Each button goes where a button of "
|
|
696
|
+
// texts). The first button is filled, a second with a line. Each button goes where a button of "Buttons" can
|
|
697
697
|
// (Button in packages/editor/src/standard/buttons/fields.ts, the targets of `buttonTargets` in config.ts and a
|
|
698
|
-
// page): "Book
|
|
699
|
-
//
|
|
698
|
+
// page): "Book an appointment" (what books on the page, an app's booking sheet, else the booking page), "Call", "Open
|
|
699
|
+
// promotion" (one shown on every page: a pop-up, or an app's booking sheet) or a page. A text is a list row of
|
|
700
700
|
// two lines, its heading over its text, beside the button (ActionBar.astro).
|
|
701
701
|
{
|
|
702
702
|
name: 'buttons',
|
|
703
|
-
label: '
|
|
704
|
-
singular: '
|
|
703
|
+
label: 'Buttons and text',
|
|
704
|
+
singular: 'item',
|
|
705
705
|
type: 'list',
|
|
706
706
|
summary: ['label', 'heading'],
|
|
707
707
|
required: true,
|
|
708
708
|
maxItems: 2,
|
|
709
|
-
hint: '
|
|
709
|
+
hint: 'At most two items.',
|
|
710
710
|
default: [{ type: 'button', label: '', to: 'booking' }],
|
|
711
711
|
only: { field: 'type', is: ['bookingbar'] },
|
|
712
712
|
fields: [
|
|
@@ -717,13 +717,13 @@ export const collections: Collection[] = [
|
|
|
717
717
|
segments: true,
|
|
718
718
|
default: 'button',
|
|
719
719
|
options: [
|
|
720
|
-
{ label: '
|
|
721
|
-
{ label: '
|
|
720
|
+
{ label: 'Button', value: 'button' },
|
|
721
|
+
{ label: 'Text', value: 'text' },
|
|
722
722
|
],
|
|
723
723
|
},
|
|
724
724
|
{
|
|
725
725
|
name: 'heading',
|
|
726
|
-
label: '
|
|
726
|
+
label: 'Heading',
|
|
727
727
|
type: 'string',
|
|
728
728
|
required: true,
|
|
729
729
|
max: 30,
|
|
@@ -731,10 +731,10 @@ export const collections: Collection[] = [
|
|
|
731
731
|
},
|
|
732
732
|
{
|
|
733
733
|
name: 'text',
|
|
734
|
-
label: '
|
|
734
|
+
label: 'Text',
|
|
735
735
|
type: 'string',
|
|
736
736
|
max: 40,
|
|
737
|
-
hint: '
|
|
737
|
+
hint: 'Shown under the heading.',
|
|
738
738
|
only: { field: 'type', is: ['text'] },
|
|
739
739
|
},
|
|
740
740
|
{ ...barButtonLabel, only: { field: 'type', is: ['button'] } },
|
|
@@ -754,18 +754,18 @@ export const collections: Collection[] = [
|
|
|
754
754
|
// src/data/footer.json, read by packages/editor/theme/layouts/Base.astro.
|
|
755
755
|
{
|
|
756
756
|
file: 'src/data/footer.json',
|
|
757
|
-
label: '
|
|
757
|
+
label: 'The footer',
|
|
758
758
|
view: '/',
|
|
759
759
|
fields: [
|
|
760
|
-
{ name: 'text', label: '
|
|
760
|
+
{ name: 'text', label: 'Text (under the name)', type: 'text', required: true },
|
|
761
761
|
{
|
|
762
762
|
name: 'contactHeading',
|
|
763
|
-
label: '
|
|
763
|
+
label: 'Heading (contact)',
|
|
764
764
|
type: 'string',
|
|
765
765
|
required: true,
|
|
766
|
-
edit: { to: '
|
|
766
|
+
edit: { to: 'company/contact', text: 'Edit phone, email and address' },
|
|
767
767
|
},
|
|
768
|
-
{ name: 'linksHeading', label: '
|
|
768
|
+
{ name: 'linksHeading', label: 'Heading (links)', type: 'string', required: true },
|
|
769
769
|
{
|
|
770
770
|
name: 'links',
|
|
771
771
|
label: 'Links',
|
|
@@ -773,7 +773,7 @@ export const collections: Collection[] = [
|
|
|
773
773
|
type: 'list',
|
|
774
774
|
summary: 'label',
|
|
775
775
|
required: true,
|
|
776
|
-
fields: [{ name: 'label', label: '
|
|
776
|
+
fields: [{ name: 'label', label: 'Text', type: 'string', required: true }, menuHref],
|
|
777
777
|
},
|
|
778
778
|
],
|
|
779
779
|
},
|
|
@@ -782,40 +782,40 @@ export const collections: Collection[] = [
|
|
|
782
782
|
{
|
|
783
783
|
kind: 'files',
|
|
784
784
|
id: 'menu',
|
|
785
|
-
label: '
|
|
786
|
-
singular: '
|
|
785
|
+
label: 'Main menu',
|
|
786
|
+
singular: 'main menu',
|
|
787
787
|
files: [
|
|
788
788
|
// src/data/menu.json, read by packages/editor/site/site.ts (nav, navShown) for the header (Header.astro, MobileMenu.tsx).
|
|
789
789
|
{
|
|
790
790
|
file: 'src/data/menu.json',
|
|
791
|
-
label: '
|
|
791
|
+
label: 'The main menu',
|
|
792
792
|
view: '/',
|
|
793
793
|
fields: [
|
|
794
794
|
{
|
|
795
795
|
name: 'enabled',
|
|
796
|
-
label: '
|
|
796
|
+
label: 'Activated',
|
|
797
797
|
type: 'boolean',
|
|
798
|
-
toggle: ['
|
|
798
|
+
toggle: ['On', 'Off'],
|
|
799
799
|
default: true,
|
|
800
|
-
hint: '
|
|
800
|
+
hint: 'When the menu is off, the header has neither menu nor menu button.',
|
|
801
801
|
},
|
|
802
802
|
],
|
|
803
803
|
root: {
|
|
804
804
|
name: 'items',
|
|
805
|
-
label: '
|
|
806
|
-
singular: '
|
|
805
|
+
label: 'Menu items',
|
|
806
|
+
singular: 'menu item',
|
|
807
807
|
type: 'list',
|
|
808
808
|
summary: 'label',
|
|
809
809
|
fields: [
|
|
810
|
-
{ name: 'label', label: '
|
|
810
|
+
{ name: 'label', label: 'Text', type: 'string', required: true },
|
|
811
811
|
menuHref,
|
|
812
812
|
{
|
|
813
813
|
name: 'children',
|
|
814
|
-
label: '
|
|
815
|
-
singular: '
|
|
814
|
+
label: 'Submenu',
|
|
815
|
+
singular: 'item',
|
|
816
816
|
type: 'list',
|
|
817
817
|
summary: 'label',
|
|
818
|
-
fields: [{ name: 'label', label: '
|
|
818
|
+
fields: [{ name: 'label', label: 'Text', type: 'string', required: true }, menuHref],
|
|
819
819
|
},
|
|
820
820
|
],
|
|
821
821
|
},
|
|
@@ -824,62 +824,62 @@ export const collections: Collection[] = [
|
|
|
824
824
|
},
|
|
825
825
|
{
|
|
826
826
|
kind: 'files',
|
|
827
|
-
id: '
|
|
828
|
-
label: '
|
|
829
|
-
singular: '
|
|
827
|
+
id: 'mobile-menu',
|
|
828
|
+
label: 'Mobile menu',
|
|
829
|
+
singular: 'mobile menu',
|
|
830
830
|
files: [
|
|
831
831
|
// src/data/mobile-menu.json, read by packages/editor/site/site.ts (mobileNav) for the bar under the header (MobileNav.astro).
|
|
832
832
|
{
|
|
833
833
|
file: 'src/data/mobile-menu.json',
|
|
834
|
-
label: '
|
|
834
|
+
label: 'The mobile menu',
|
|
835
835
|
view: '/',
|
|
836
836
|
fields: [
|
|
837
837
|
{
|
|
838
838
|
name: 'enabled',
|
|
839
|
-
label: '
|
|
839
|
+
label: 'Activated',
|
|
840
840
|
type: 'boolean',
|
|
841
|
-
toggle: ['
|
|
841
|
+
toggle: ['On', 'Off'],
|
|
842
842
|
default: false,
|
|
843
|
-
hint: '
|
|
843
|
+
hint: 'The items are on a line under the header on a phone and can be swiped sideways.',
|
|
844
844
|
},
|
|
845
845
|
],
|
|
846
846
|
root: {
|
|
847
847
|
name: 'items',
|
|
848
|
-
label: '
|
|
849
|
-
singular: '
|
|
848
|
+
label: 'Menu items',
|
|
849
|
+
singular: 'menu item',
|
|
850
850
|
type: 'list',
|
|
851
851
|
summary: 'label',
|
|
852
|
-
fields: [{ name: 'label', label: '
|
|
852
|
+
fields: [{ name: 'label', label: 'Text', type: 'string', required: true }, menuHref],
|
|
853
853
|
},
|
|
854
854
|
},
|
|
855
855
|
],
|
|
856
856
|
},
|
|
857
857
|
{
|
|
858
858
|
kind: 'files',
|
|
859
|
-
id: '
|
|
860
|
-
label: '
|
|
861
|
-
singular: '
|
|
859
|
+
id: 'header-button',
|
|
860
|
+
label: 'Header button',
|
|
861
|
+
singular: 'header button',
|
|
862
862
|
files: [
|
|
863
863
|
// src/data/header.json, optional (a site without it has no button and the menu button at the start), read by the header
|
|
864
864
|
// (Header.astro). The button is one of the action bar's (barButtonLabel …).
|
|
865
865
|
{
|
|
866
866
|
file: 'src/data/header.json',
|
|
867
|
-
label: '
|
|
867
|
+
label: 'The header',
|
|
868
868
|
view: '/',
|
|
869
869
|
optional: true,
|
|
870
870
|
fields: [
|
|
871
871
|
{
|
|
872
872
|
name: 'button',
|
|
873
|
-
label: '
|
|
873
|
+
label: 'Action button',
|
|
874
874
|
type: 'object',
|
|
875
875
|
fields: [
|
|
876
876
|
{
|
|
877
877
|
name: 'enabled',
|
|
878
|
-
label: '
|
|
878
|
+
label: 'Activated',
|
|
879
879
|
type: 'boolean',
|
|
880
|
-
toggle: ['
|
|
880
|
+
toggle: ['On', 'Off'],
|
|
881
881
|
default: false,
|
|
882
|
-
hint: '
|
|
882
|
+
hint: 'The button is in the header.',
|
|
883
883
|
},
|
|
884
884
|
barButtonLabel,
|
|
885
885
|
barButtonTo,
|
|
@@ -889,13 +889,13 @@ export const collections: Collection[] = [
|
|
|
889
889
|
},
|
|
890
890
|
{
|
|
891
891
|
name: 'menuSide',
|
|
892
|
-
label: '
|
|
892
|
+
label: 'Menu button position',
|
|
893
893
|
type: 'select',
|
|
894
894
|
segments: true,
|
|
895
895
|
default: 'start',
|
|
896
896
|
options: [
|
|
897
|
-
{ label: '
|
|
898
|
-
{ label: '
|
|
897
|
+
{ label: 'Left', value: 'start' },
|
|
898
|
+
{ label: 'Right', value: 'end' },
|
|
899
899
|
],
|
|
900
900
|
},
|
|
901
901
|
],
|