@paroicms/site-generator-plugin 0.24.2 → 0.25.0

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.
@@ -150,13 +150,24 @@ function getDefaultStringValueForField(fieldName) {
150
150
  return undefined;
151
151
  }
152
152
  export function generateLocalizedFooterMention(siteSchema) {
153
- const { languages, languageLabels } = siteSchema;
153
+ const { languages } = siteSchema;
154
154
  return Object.fromEntries(languages.map((language) => [
155
155
  language,
156
156
  {
157
157
  j: {
158
- ops: markdownToDelta(`Powered by ParoiCMS in ${languageLabels[language]}`),
158
+ ops: markdownToDelta(getPoweredByLabel(language)),
159
159
  },
160
160
  },
161
161
  ]));
162
162
  }
163
+ function getPoweredByLabel(language) {
164
+ const translations = {
165
+ en: "Powered by ParoiCMS",
166
+ fr: "Propulsé par ParoiCMS",
167
+ es: "Desarrollado por ParoiCMS",
168
+ de: "Betrieben von ParoiCMS",
169
+ it: "Alimentato da ParoiCMS",
170
+ pt: "Desenvolvido por ParoiCMS",
171
+ };
172
+ return translations[language] ?? translations.en;
173
+ }
@@ -1,3 +1,4 @@
1
+ import { isDef, } from "@paroicms/public-anywhere-lib";
1
2
  import { camelToKebabCase, camelToTitleCase } from "../lib/utils.js";
2
3
  import { createNodeContents } from "./create-node-contents.js";
3
4
  import { invokeGenerateFakeContent, } from "./invoke-generate-fake-content.js";
@@ -20,7 +21,7 @@ export async function generateMultipleFieldSetContents(ctx, options, report) {
20
21
  ? [{ tagName: "title", key: "title", format: "text", tagDescription: "Write the title here" }]
21
22
  : [];
22
23
  if (nodeType.fields) {
23
- outputTags.push(...nodeType.fields.map(toFakeContentOutputTag).filter(Boolean));
24
+ outputTags.push(...nodeType.fields.map(toFakeContentOutputTag).filter(isDef));
24
25
  }
25
26
  const defaultLanguage = siteSchema.languages?.[0];
26
27
  const typeLabel = translateText(schemaI18n, `nodeTypes.${nodeType.typeName}.label`, {
@@ -1,11 +1,11 @@
1
- import { messageOf } from "@paroicms/public-anywhere-lib";
1
+ import { isDef, messageOf } from "@paroicms/public-anywhere-lib";
2
2
  let seq = 0;
3
3
  export async function batchInvokeMinistral(ctx, prompts, options) {
4
4
  const startTime = Date.now();
5
5
  const responses = await execBatchInvokeMinistral(ctx, prompts, options);
6
6
  const llmMessages = responses
7
7
  .map((msg) => msg.response.body.choices[0]?.message.content)
8
- .filter(Boolean);
8
+ .filter(isDef);
9
9
  const llmReport = {
10
10
  llmTaskName: options.llmTaskName,
11
11
  modelName: ctx.mistralModelName,
@@ -1,5 +1,5 @@
1
1
  import { ensureDirectory } from "@paroicms/internal-server-lib";
2
- import { isObj, messageOf } from "@paroicms/public-anywhere-lib";
2
+ import { isDef, isObj, messageOf } from "@paroicms/public-anywhere-lib";
3
3
  import { readFile, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
  import { estimateTokenCount } from "./llm-tokens.js";
@@ -112,7 +112,7 @@ async function writeDebugLlmInputOutputs(ctx, stepHandle, list, llmReport, start
112
112
  stepHandle?.stepNumber,
113
113
  llmReport.llmTaskName,
114
114
  llmReport.errorMessage ? "ERROR" : undefined,
115
- ].filter(Boolean);
115
+ ].filter(isDef);
116
116
  const baseName = nameParts.join("-");
117
117
  const header = [
118
118
  `Model: ${llmReport.modelName}`,
@@ -1,3 +1,4 @@
1
+ import { isDef } from "@paroicms/public-anywhere-lib";
1
2
  import { loadStep } from "../../db/db-read.queries.js";
2
3
  import { insertStep, saveCompletedSchemaStep, updateStep, } from "../../db/db-write.queries.js";
3
4
  import { reorderObjectKeys } from "../helpers/js-utils.js";
@@ -210,7 +211,7 @@ function createUnusedInformationPrompt(unusedInformation, analysis) {
210
211
  .map(([typeName, entry]) => {
211
212
  return entry.prompt ? `${typeName}: ${entry.prompt}` : undefined;
212
213
  })
213
- .filter(Boolean);
214
+ .filter(isDef);
214
215
  if (prompts.length > 0) {
215
216
  const nodeTypePrompts = `To do:\n\n- ${prompts.join("- \n")}`;
216
217
  return unusedInformation ? `${nodeTypePrompts}\n\n${unusedInformation}` : nodeTypePrompts;
@@ -1,3 +1,4 @@
1
+ import { isDef } from "@paroicms/public-anywhere-lib";
1
2
  import { getJtHomeType, getJtSiteType, isMultilingual } from "./jt-site-schema-helpers.js";
2
3
  import { indent } from "./template-helpers.js";
3
4
  export function templateOfDocumentBreadcrumb() {
@@ -24,7 +25,7 @@ export function templateOfSiteHeader(ctx) {
24
25
  ];
25
26
  return `<div class="_bg2">
26
27
  <header class="Container Header">
27
- ${indent(content.filter(Boolean).join("\n"), 2, { skipFirst: true })}
28
+ ${indent(content.filter(isDef).join("\n"), 2, { skipFirst: true })}
28
29
  </header>
29
30
  </div>
30
31
  <div
@@ -42,7 +43,7 @@ function templateOfSiteLogoTitle(ctx) {
42
43
  const content = [
43
44
  siteType.fields?.includes("logo")
44
45
  ? `{% if site.field.logo %}
45
- {% useImage logo image: site.field.logo resize: "50x50" %}
46
+ {% set logo = image(site.field.logo, resize: "50x50") %}
46
47
  <img
47
48
  src="{{ logo.url }}"
48
49
  width="{{ logo.width }}"
@@ -57,7 +58,7 @@ function templateOfSiteLogoTitle(ctx) {
57
58
  class="Header-logo"
58
59
  href="{{ site.home.url }}"
59
60
  data-mobile-menu-part="logo">
60
- ${indent(content.filter(Boolean).join("\n"), 1, { skipFirst: true })}
61
+ ${indent(content.filter(isDef).join("\n"), 1, { skipFirst: true })}
61
62
  </a>`;
62
63
  }
63
64
  function templateOfMainMenu(ctx) {
@@ -66,8 +67,8 @@ function templateOfMainMenu(ctx) {
66
67
  const menuItems = homeType.routingChildren?.filter((typeName) => typeName !== "search" && typeName !== "searchPage");
67
68
  if (!menuItems || menuItems.length === 0)
68
69
  return;
69
- const itemTemplates = menuItems.map((typeName) => `{% if site.home.${typeName} %}
70
- {% assign ${typeName} = site.home.${typeName}.doc %}
70
+ const itemTemplates = menuItems.map((typeName) => `{% set ${typeName} = doc(site.home.${typeName}) %}
71
+ {% if ${typeName} %}
71
72
  <a
72
73
  data-menu-item-id="{{ ${typeName}.id }}"
73
74
  href="{{ ${typeName}.url }}">
@@ -88,12 +89,9 @@ function templateOfSearchOpener(ctx) {
88
89
  if (!typeName)
89
90
  return;
90
91
  return `<div class="Row center">
91
- {% if site.home.${typeName} %}
92
- {% assign ${typeName} = site.home.${typeName}.doc %}
93
- <span
94
- data-effect="paSearchOpener"
95
- data-search-url="{{ ${typeName}.url }}"
96
- data-icon-color="#fff"></span>
92
+ {% set ${typeName} = doc(site.home.${typeName}) %}
93
+ {% if ${typeName} %}
94
+ {% out searchOpener(url: ${typeName}.url, iconColor: "#fff") %}
97
95
  {% endif %}
98
96
  <div data-mobile-menu="button"></div>
99
97
  </div>`;
@@ -102,7 +100,7 @@ export function templateOfSiteFooter(ctx) {
102
100
  const content = [templateOfSiteFooterMention(ctx), templateOfLanguageSelector(ctx)];
103
101
  return `<div class="_bg2">
104
102
  <footer class="Container">
105
- ${indent(content.filter(Boolean).join("\n"), 2, { skipFirst: true })}
103
+ ${indent(content.filter(isDef).join("\n"), 2, { skipFirst: true })}
106
104
  </footer>
107
105
  </div>`;
108
106
  }
@@ -120,10 +118,11 @@ function templateOfLanguageSelector(ctx) {
120
118
  return `<ul class="LanguageSelector">
121
119
  {% for translation in doc.translations %}
122
120
  <li>
123
- <a
124
- class="{% if translation.active %}active{% endif %}"
125
- href="{{ translation.url }}"
126
- title="{{ translation.languageLabel }}">{{ translation.language }}</a>
121
+ {% if translation.active %}
122
+ <span>{{ translation.languageLabel }}</span>
123
+ {% else %}
124
+ <a href="{{ translation.url }}">{{ translation.languageLabel }}</a>
125
+ {% endif %}
127
126
  </li>
128
127
  {% endfor %}
129
128
  </ul>`;
@@ -0,0 +1,40 @@
1
+ import { getJtRegularDocumentType, getPossibleJtLabelingFieldTypes, } from "./jt-site-schema-helpers.js";
2
+ import { indent } from "./template-helpers.js";
3
+ export function templateOfDocumentCard(ctx, docVariableName, { parentType } = {}) {
4
+ const { siteSchema } = ctx;
5
+ const possibleTypes = parentType
6
+ ? (parentType.regularChildren ?? []).map((childName) => getJtRegularDocumentType(siteSchema, childName))
7
+ : undefined;
8
+ const labelingFields = getPossibleJtLabelingFieldTypes(siteSchema, possibleTypes);
9
+ const labelingTemplates = labelingFields.map((field) => {
10
+ const fieldName = ` ${docVariableName}.field.${field.name}`;
11
+ return `{% if ${fieldName} %}
12
+ <ul class="FlexWrap">
13
+ {% for labelDoc in ${fieldName} %}
14
+ <li class="Label">{{ labelDoc.title }}</li>
15
+ {% endfor %}
16
+ </ul>
17
+ {% endif %}`;
18
+ });
19
+ return `<a href="{{ ${docVariableName}.url }}">
20
+ <article>
21
+ {% if ${docVariableName}.defaultImage %}
22
+ <div>
23
+ {% set im = image(${docVariableName}.defaultImage, resize: "120x120") %}
24
+ <img
25
+ src="{{ im.url }}"
26
+ width="{{ im.width }}"
27
+ height="{{ im.height }}"
28
+ loading="lazy"
29
+ alt=""
30
+ >
31
+ </div>
32
+ {% endif %}
33
+ <div>
34
+ <h3>{{ ${docVariableName}.title }}</h3>
35
+ <p>{{ ${docVariableName}.excerpt | makeExcerpt: 40 }}</p>
36
+ ${indent(labelingTemplates.join("\n"), 3, { skipFirst: true })}
37
+ </div>
38
+ </article>
39
+ </a>`;
40
+ }
@@ -1,7 +1,10 @@
1
+ import { isDef } from "@paroicms/public-anywhere-lib";
1
2
  import { camelToKebabCase } from "../lib/utils.js";
2
3
  import { templateOfDocumentBreadcrumb } from "./common-template-creator.js";
4
+ import { templateOfDocumentCard } from "./document-card-template-creator.js";
3
5
  import { createIdKeyProvider } from "./id-key-provider.js";
4
6
  import { getJtPartType, getJtRoutingDocumentType, hasTemporalChildren, } from "./jt-site-schema-helpers.js";
7
+ import { templatesOfLabeledList } from "./labeled-list-template-creator.js";
5
8
  import { getPredefinedDataType, indent, localizedLabelTemplate } from "./template-helpers.js";
6
9
  export function templateOfDocumentType(ctx, documentType) {
7
10
  const childrenTemplate = templateOfDocumentChildren(ctx, documentType, createIdKeyProvider());
@@ -11,20 +14,24 @@ export function templateOfDocumentType(ctx, documentType) {
11
14
  })
12
15
  : undefined;
13
16
  const titleFieldsTemplate = templateOfTitleAndFields(ctx, documentType);
14
- const listTemplates = documentType.lists?.map((list) => templateOfList(ctx, list, { listKey: `doc.list.${list.listName}`, nested: false })) ?? [];
17
+ const listTemplates = documentType.lists
18
+ ?.map((list) => templateOfList(ctx, list, { listKey: `doc.list.${list.listName}`, nested: false }))
19
+ .filter(isDef) ?? [];
15
20
  const specialTemplate = templateOfSpecialDocument(ctx, documentType);
21
+ const labeledListTemplates = templatesOfLabeledList(ctx, documentType);
16
22
  ctx.addLiquidFile("partials", "breadcrumb.liquid", templateOfDocumentBreadcrumb(), {
17
23
  skipIfExists: true,
18
24
  });
19
25
  const blocks = [
20
- `{% render "partials/breadcrumb" doc: doc site: site %}`,
26
+ `{% render "partials/breadcrumb" doc: doc %}`,
21
27
  featuredImageTemplate,
22
28
  titleFieldsTemplate,
23
- childrenTemplate,
24
29
  specialTemplate,
25
30
  ...listTemplates,
26
- ].filter(Boolean);
27
- return `{% layout "layouts/main-layout.liquid" doc: doc site: site %}
31
+ ...labeledListTemplates,
32
+ childrenTemplate,
33
+ ].filter(isDef);
34
+ return `{% layout "layouts/main-layout.liquid" %}
28
35
  {% block %}
29
36
  ${blocks.join("\n\n")}
30
37
  {% endblock %}`;
@@ -38,8 +45,8 @@ function templateOfTitleAndFields(ctx, documentType) {
38
45
  const textBlock = `<div class="TextWidth">
39
46
  ${indent(blocks1.join("\n"), 1, { skipFirst: true })}
40
47
  </div>`;
41
- const siblingsTemplate = documentType.documentKind === "regular" && templateOfSiblingLinks(ctx);
42
- const blocks2 = [textBlock, siblingsTemplate].filter(Boolean);
48
+ const siblingsTemplate = documentType.documentKind === "regular" ? templateOfSiblingLinks(ctx) : undefined;
49
+ const blocks2 = [textBlock, siblingsTemplate].filter(isDef);
43
50
  return `<div class="_bg2">
44
51
  <div class="Container">
45
52
  <div class="Page">
@@ -51,31 +58,31 @@ function templateOfTitleAndFields(ctx, documentType) {
51
58
  function templateOfSpecialDocument(ctx, documentType) {
52
59
  const { addLiquidFile } = ctx;
53
60
  if (documentType.typeName === "search" || documentType.typeName === "searchPage") {
54
- addLiquidFile("partials", "result-item.public.liquid", templateOfDocumentTile("doc"));
55
- return `<div
56
- class="Container"
57
- data-effect="paSearchApp"
58
- data-template="result-item"
59
- data-limit="10"></div>`;
61
+ addLiquidFile("partials", "result-item.public.liquid", templateOfDocumentCard(ctx, "doc"));
62
+ return `<div class="Container">
63
+ <div class="Page">
64
+ {% out searchApp(class: "Container", template: "partials/result-item.public", by: 10) %}
65
+ </div>
66
+ </div>`;
60
67
  }
61
68
  if (documentType.typeName === "contact" || documentType.typeName === "contactPage") {
62
69
  return `<div class="Container">
63
- <div class="TextWidth Pt">
64
- <div
65
- data-effect="paContactForm"
66
- data-home-url="{{ site.home.url }}"></div>
70
+ <div class="Page">
71
+ <div class="TextWidth Pt">
72
+ {% out contactForm %}
73
+ </div>
67
74
  </div>
68
75
  </div>`;
69
76
  }
70
77
  }
71
78
  function templateOfDocumentChildren(ctx, parentType, parentIdKeyProvider) {
72
79
  const routingBlocks = templateOfRoutingChildren(ctx, parentType, parentIdKeyProvider);
73
- const regularBlocks = parentType.documentKind === "routing" &&
74
- parentType.regularChildren &&
75
- templateOfRegularDocumentTiles(ctx, parentType, parentIdKeyProvider, {
80
+ const regularBlocks = parentType.documentKind === "routing" && parentType.regularChildren
81
+ ? templateOfRegularDocumentCards(ctx, parentType, parentIdKeyProvider, {
76
82
  mode: "all",
77
- });
78
- return [routingBlocks, regularBlocks].filter(Boolean).join("\n\n") || undefined;
83
+ })
84
+ : undefined;
85
+ return [routingBlocks, regularBlocks].filter(isDef).join("\n\n") || undefined;
79
86
  }
80
87
  function templateOfRoutingChildren(ctx, parentType, parentIdKeyProvider) {
81
88
  const { siteSchema } = ctx;
@@ -87,7 +94,7 @@ function templateOfRoutingChildren(ctx, parentType, parentIdKeyProvider) {
87
94
  }
88
95
  return templateOfRoutingChild(ctx, child, parentIdKeyProvider);
89
96
  })
90
- .filter(Boolean);
97
+ .filter(isDef);
91
98
  if (routingBlocks.length === 0)
92
99
  return;
93
100
  return `<div class="Container">
@@ -101,63 +108,52 @@ function templateOfRoutingChild(ctx, child, parentIdKeyProvider) {
101
108
  const idKeyProvider = parentIdKeyProvider.createForRoutingChild(typeName);
102
109
  const key = idKeyProvider.key;
103
110
  if (!hasTemporalChildren(siteSchema, child) || regularChildrenSorting !== "publishDate desc") {
104
- const buttonTemplate = `{% if ${key} %}
105
- {% assign ${variableName} = ${key}.doc %}
111
+ const buttonTemplate = `{% set ${variableName} = doc(${key}) %}
112
+ {% if ${variableName} %}
106
113
  <a class="Button" href="{{ ${variableName}.url }}">{{ ${variableName}.title }}</a>
107
114
  {% endif %}`;
108
115
  return buttonTemplate;
109
116
  }
110
- const tilesTemplate = templateOfRegularDocumentTiles(ctx, child, idKeyProvider, {
117
+ const cardsTemplate = templateOfRegularDocumentCards(ctx, child, idKeyProvider, {
111
118
  mode: "sampleOnly",
112
119
  });
113
- return `{% if ${key} %}
114
- {% assign ${variableName} = ${key}.doc %}
115
- <div class="Pt">
116
- <h2>
117
- <a class="TextLink" href="{{ ${variableName}.url }}">{{ ${variableName}.title }}</a>
118
- </h2>
119
- ${indent(tilesTemplate, 1, { skipFirst: true })}
120
- </div>
120
+ return `{% set ${variableName} = doc(${key}) %}
121
+ {% if ${variableName} %}
122
+ <div class="Pt">
123
+ <h2>
124
+ <a class="TextLink" href="{{ ${variableName}.url }}">{{ ${variableName}.title }}</a>
125
+ </h2>
126
+ ${indent(cardsTemplate, 2, { skipFirst: true })}
127
+ </div>
121
128
  {% endif %}`;
122
129
  }
123
- function templateOfRegularDocumentTiles(ctx, parentType, parentIdKeyProvider, { mode }) {
130
+ function templateOfRegularDocumentCards(ctx, parentType, parentIdKeyProvider, { mode }) {
124
131
  const { siteSchema, addLiquidFile } = ctx;
125
132
  const { typeName: parentTypeName } = parentType;
126
133
  const childrenVariableName = `${parentTypeName}Children`;
127
134
  const childVariableName = `${parentTypeName}Child`;
128
- const idKey = parentIdKeyProvider.idKey;
135
+ const key = parentIdKeyProvider.key;
129
136
  if (mode === "sampleOnly") {
130
- return `{% getDocs ${childrenVariableName} parentId: ${idKey} limit: 4 %}
137
+ return `{% set ${childrenVariableName} = docs(${key}.children, limit: 4) %}
131
138
  <div class="List">
132
139
  {% for ${childVariableName} in ${childrenVariableName} %}
133
- ${indent(templateOfDocumentTile(childVariableName), 2, { skipFirst: true })}
140
+ ${indent(templateOfDocumentCard(ctx, childVariableName, { parentType }), 2, { skipFirst: true })}
134
141
  {% endfor %}
135
142
  </div>`;
136
143
  }
137
144
  if (!hasTemporalChildren(siteSchema, parentType)) {
138
- return `{% getDocs ${childrenVariableName} parentId: ${idKey} %}
145
+ return `{% set ${childrenVariableName} = docs(${key}.children) %}
139
146
  <div class="Container List Pt Pb">
140
147
  {% for ${childVariableName} in ${childrenVariableName} %}
141
- ${indent(templateOfDocumentTile(childVariableName), 2, { skipFirst: true })}
148
+ ${indent(templateOfDocumentCard(ctx, childVariableName, { parentType }), 2, { skipFirst: true })}
142
149
  {% endfor %}
143
150
  </div>`;
144
151
  }
145
- const tileTemplateName = `${camelToKebabCase(parentTypeName)}-tile`;
146
- addLiquidFile("partials", `${tileTemplateName}.public.liquid`, templateOfDocumentTile("doc"));
147
- return `{% getPaginatedDocs ${childrenVariableName} parentId: ${idKey} pageSize: 10 %}
152
+ const cardTemplateName = `${camelToKebabCase(parentTypeName)}-card.public`;
153
+ addLiquidFile("partials", `${cardTemplateName}.liquid`, templateOfDocumentCard(ctx, "doc", { parentType }), { skipIfExists: true });
154
+ return `{% set ${childrenVariableName} = paginatedDocs(${key}.children, by: 10) %}
148
155
  <div class="Container">
149
- <div
150
- class="Page List"
151
- data-effect="paInfiniteLoading"
152
- data-parent-id="{{ ${idKey} }}"
153
- data-start="{{ ${childrenVariableName}.pageSize }}"
154
- data-limit="{{ ${childrenVariableName}.pageSize }}"
155
- data-total="{{ ${childrenVariableName}.total }}"
156
- data-template="${tileTemplateName}">
157
- {% for ${childrenVariableName} in ${childrenVariableName}.items %}
158
- {% render "partials/${tileTemplateName}.public.liquid" doc: ${childrenVariableName} %}
159
- {% endfor %}
160
- </div>
156
+ {% out infiniteLoading(class: "Page List", paginatedDocs: ${childrenVariableName}, template: "partials/${cardTemplateName}") %}
161
157
  </div>`;
162
158
  }
163
159
  function templateOfFields(ctx, fields, { parentKey }) {
@@ -178,9 +174,9 @@ function templateOfField(ctx, fieldOrName, parentKey) {
178
174
  <div class="Field">
179
175
  {% for tag in ${parentKey}.${fieldName} %}
180
176
  {% if tag.inRightLanguage %}
181
- <a href="{{ tag.url }}">{{ tag.title }}</a>
177
+ <a class="Label" href="{{ tag.url }}">{{ tag.title }}</a>
182
178
  {% else %}
183
- <span>{{ tag.title }}</span>
179
+ <span class="Label">{{ tag.title }}</span>
184
180
  {% endif %}
185
181
  {% endfor %}
186
182
  </div>
@@ -201,7 +197,7 @@ function templateOfField(ctx, fieldOrName, parentKey) {
201
197
  if (dataType === "gallery") {
202
198
  return `<div class="Field">
203
199
  {% for media in ${parentKey}.${fieldName} %}
204
- {% useImage im image: media resize: "150x150" %}
200
+ {% set im = image(media, resize: "150x150") %}
205
201
  <img
206
202
  class="Field-img"
207
203
  src="{{ im.url }}"
@@ -216,7 +212,7 @@ function templateOfField(ctx, fieldOrName, parentKey) {
216
212
  }
217
213
  if (dataType === "media") {
218
214
  const mediaKey = `${parentKey}.${fieldName}`;
219
- return `{% useImage im image: ${mediaKey} resize: "x250x" %}
215
+ return `{% set im = image(${mediaKey}, resize: "x250x") %}
220
216
  <div class="Field">
221
217
  <img
222
218
  class="Field-img"
@@ -236,21 +232,23 @@ function templateOfField(ctx, fieldOrName, parentKey) {
236
232
  }
237
233
  function templateOfPicture({ imageKey }) {
238
234
  return `{% if ${imageKey} %}
239
- {% useImage smallIm image: ${imageKey} resize: "360x48" %}
240
- {% useImage largeIm image: ${imageKey} resize: "1200x160" %}
235
+ {% set smallIm = image(${imageKey}, resize: "360x48") %}
236
+ {% set largeIm = image(${imageKey}, resize: "1200x160") %}
241
237
  <div class="Container">
242
238
  <picture class="Hero">
243
239
  <source
244
240
  srcset="{{ largeIm.url }}"
245
241
  width="{{ largeIm.width }}"
246
242
  height="{{ largeIm.height }}"
247
- media="(min-width: 361px)">
243
+ media="(min-width: 361px)"
244
+ >
248
245
  <img
249
246
  src="{{ smallIm.url }}"
250
247
  width="{{ smallIm.width }}"
251
248
  height="{{ smallIm.height }}"
252
249
  loading="lazy"
253
- alt="">
250
+ alt=""
251
+ >
254
252
  </picture>
255
253
  </div>
256
254
  {% endif %}`;
@@ -273,7 +271,7 @@ function templateOfList(ctx, list, { listKey, nested }) {
273
271
  return `{% ${ifOrElsif} part.type == "${partType.typeName}" %}
274
272
  {% render "partials/${partTemplateName}.liquid" part: part %}`;
275
273
  })
276
- .filter(Boolean);
274
+ .filter(isDef);
277
275
  partTemplates.push("{% endif %}");
278
276
  if (nested) {
279
277
  return `{% if ${listKey} %}
@@ -303,7 +301,7 @@ function templateOfPart(ctx, part, partKey) {
303
301
  nested: true,
304
302
  })
305
303
  : undefined,
306
- ].filter(Boolean);
304
+ ].filter(isDef);
307
305
  return `<section class="TextWidth">
308
306
  ${indent(templates.join("\n\n"), 1, { skipFirst: true })}
309
307
  </section>`;
@@ -318,34 +316,15 @@ function templateOfSiblingLinks(ctx) {
318
316
  fr: "Suivant",
319
317
  });
320
318
  return `<div class="Row spaceBetween">
321
- {% if doc.siblings.previous %}
322
- <a href="{{ doc.siblings.previous.url }}" title="{{ doc.siblings.previous.title }}">← ${previousLabelTemplate}</a>
319
+ {% set previous = doc(doc.siblings.previous) %}
320
+ {% if previous %}
321
+ <a href="{{ previous.url }}" title="{{ previous.title }}">← ${previousLabelTemplate}</a>
323
322
  {% else %}
324
323
  <span></span>
325
324
  {% endif %}
326
- {% if doc.siblings.next %}
327
- <a href="{{ doc.siblings.next.url }}" title="{{ doc.siblings.next.title }}">${nextLabelTemplate} →</a>
325
+ {% set next = doc(doc.siblings.next) %}
326
+ {% if next %}
327
+ <a href="{{ next.url }}" title="{{ next.title }}">${nextLabelTemplate} →</a>
328
328
  {% endif %}
329
329
  </div>`;
330
330
  }
331
- function templateOfDocumentTile(docVariableName) {
332
- return `<a href="{{ ${docVariableName}.url }}">
333
- <article>
334
- {% if ${docVariableName}.defaultImage %}
335
- <div>
336
- {% useImage im image: ${docVariableName}.defaultImage resize: "120x120" %}
337
- <img
338
- src="{{ im.url }}"
339
- width="{{ im.width }}"
340
- height="{{ im.height }}"
341
- loading="lazy"
342
- alt="">
343
- </div>
344
- {% endif %}
345
- <div>
346
- <h3>{{ ${docVariableName}.title }}</h3>
347
- <p>{{ ${docVariableName}.excerpt | makeExcerpt: 40 }}</p>
348
- </div>
349
- </article>
350
- </a>`;
351
- }
@@ -1,6 +1,6 @@
1
1
  export function createIdKeyProvider() {
2
2
  return {
3
- key: "doc",
3
+ key: "doc.routing",
4
4
  idKey: "doc.id",
5
5
  getChildRoutingKey: (typeName) => `doc.routing.${typeName}`,
6
6
  createForRoutingChild: (routingTypeName) => createChildIdKeyProvider(`doc.routing.${routingTypeName}`),
@@ -53,3 +53,19 @@ export function getFirstSiteLanguage(siteSchema) {
53
53
  throw new Error("Missing language in site schema");
54
54
  return siteSchema.languages[0];
55
55
  }
56
+ export function getPossibleJtLabelingFieldTypes(siteSchema, possibleTypes) {
57
+ const searchInTypes = possibleTypes ??
58
+ siteSchema.nodeTypes?.filter((type) => type.kind === "document" && type.documentKind === "regular") ??
59
+ [];
60
+ const result = [];
61
+ for (const documentType of searchInTypes) {
62
+ for (const fieldType of documentType.fields ?? []) {
63
+ if (typeof fieldType !== "string" &&
64
+ fieldType.storedAs === "labeling" &&
65
+ !result.find((alreadyThere) => alreadyThere.name === fieldType.name)) {
66
+ result.push(fieldType);
67
+ }
68
+ }
69
+ }
70
+ return result;
71
+ }
@@ -0,0 +1,56 @@
1
+ import { isDef } from "@paroicms/public-anywhere-lib";
2
+ import { camelToKebabCase } from "../lib/utils.js";
3
+ import { templateOfDocumentCard } from "./document-card-template-creator.js";
4
+ export function templatesOfLabeledList(ctx, documentType) {
5
+ const { addLiquidFile } = ctx;
6
+ const labeledByUs = ctx.labeledDocuments.filter((doc) => {
7
+ const parentTypes = ctx.getParentTypes(documentType);
8
+ return parentTypes.some((parentType) => parentType.typeName === doc.taxonomyType.typeName);
9
+ });
10
+ return labeledByUs
11
+ .map((labeledDocument) => {
12
+ const parentRoutingKey = getRoutingKeyInCluster(ctx, labeledDocument.parentDocumentType, {
13
+ sameClusterAs: labeledDocument.taxonomyType,
14
+ });
15
+ if (!parentRoutingKey)
16
+ return;
17
+ const labeledListKey = `labeled${upperCaseFirstLetter(labeledDocument.parentDocumentType.typeName)}`;
18
+ const cardTemplateName = `${camelToKebabCase(labeledDocument.documentType.typeName)}-card.public`;
19
+ addLiquidFile("partials", `${cardTemplateName}.liquid`, templateOfDocumentCard(ctx, "doc", { parentType: labeledDocument.parentDocumentType }), { skipIfExists: true });
20
+ return `{% set ${labeledListKey} = paginatedDocs(${parentRoutingKey}.children, by: 10, term: doc, labeledWith: "${labeledDocument.field.name}") %}
21
+ <div class="Container">
22
+ {% out infiniteLoading(class: "Page List", paginatedDocs: ${labeledListKey}, template: "partials/${cardTemplateName}") %}
23
+ </div>`;
24
+ })
25
+ .filter(isDef);
26
+ }
27
+ function upperCaseFirstLetter(str) {
28
+ return `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
29
+ }
30
+ function getRoutingKeyInCluster(ctx, documentType, options) {
31
+ const { siteSchema } = ctx;
32
+ const { nodeTypes } = siteSchema;
33
+ if (!nodeTypes)
34
+ return;
35
+ const getNodeType = (typeName) => nodeTypes.find((nt) => nt.typeName === typeName);
36
+ function findPath(target, current, parentPath) {
37
+ const currentPath = [...parentPath, current];
38
+ if (current === target)
39
+ return currentPath;
40
+ const node = getNodeType(current);
41
+ if (!node || node.kind !== "document" || !node.routingChildren)
42
+ return;
43
+ for (const childName of node.routingChildren) {
44
+ const found = findPath(target, childName, currentPath);
45
+ if (found)
46
+ return found;
47
+ }
48
+ }
49
+ const clusterRootTypeName = "home"; // limitation here: only works for the "home" cluster
50
+ const targetPath = findPath(documentType.typeName, clusterRootTypeName, []);
51
+ const sameClusterPath = findPath(options.sameClusterAs.typeName, clusterRootTypeName, []);
52
+ if (!targetPath || !sameClusterPath)
53
+ return;
54
+ targetPath.shift(); // remove leading 'home'
55
+ return `doc.cluster.routing.${targetPath.join(".")}`;
56
+ }