strapi-relation-names 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -171,6 +171,7 @@ The default label is also used when:
171
171
  - [ ] Live label previews while editing templates
172
172
  - [ ] Define default relation templates in plugin configuration
173
173
  - [x] Support nested field paths in templates
174
+ - [x] Support nested relation fields in templates
174
175
  - [ ] Import and export relation-label settings
175
176
  - [ ] Conditional labels and configurable fallback rules
176
177
  - [ ] Support label transformation functions (e.g., `toUpperCase`, `toLowerCase`, `capitalize`, etc.)
@@ -5,7 +5,7 @@ const designSystem = require("@strapi/design-system");
5
5
  const react = require("react");
6
6
  const reactIntl = require("react-intl");
7
7
  const admin = require("@strapi/strapi/admin");
8
- const index = require("./index-CKaZcnCM.js");
8
+ const index = require("./index-DFr0KRky.js");
9
9
  const FIELD_TYPES = /* @__PURE__ */ new Set([
10
10
  "string",
11
11
  "text",
@@ -31,6 +31,15 @@ const SYSTEM_SCALAR_FIELDS = [
31
31
  "status"
32
32
  ];
33
33
  const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
34
+ const getNestedSchemaUid = (attribute) => {
35
+ if (attribute.type === "component") {
36
+ return attribute.component;
37
+ }
38
+ if (attribute.type === "relation") {
39
+ return attribute.targetModel ?? attribute.target;
40
+ }
41
+ return void 0;
42
+ };
34
43
  const getPlaceholders = (template) => {
35
44
  if (!template.trim()) {
36
45
  return null;
@@ -74,10 +83,11 @@ const validateTemplate = (template, attributes, schemas) => {
74
83
  if (isLastSegment) {
75
84
  return !FIELD_TYPES.has(attribute.type ?? "");
76
85
  }
77
- if (attribute.type !== "component" || !attribute.component) {
86
+ const nestedSchemaUid = getNestedSchemaUid(attribute);
87
+ if (!nestedSchemaUid) {
78
88
  return true;
79
89
  }
80
- currentAttributes = schemas?.get(attribute.component)?.attributes;
90
+ currentAttributes = schemas?.get(nestedSchemaUid)?.attributes;
81
91
  }
82
92
  return true;
83
93
  });
@@ -95,14 +105,15 @@ const getAvailableFields = (schema, schemas, prefix = "", ancestors = /* @__PURE
95
105
  if (FIELD_TYPES.has(attribute.type ?? "")) {
96
106
  return [path];
97
107
  }
98
- if (attribute.type !== "component" || !attribute.component || ancestors.has(attribute.component)) {
108
+ const nestedSchemaUid = getNestedSchemaUid(attribute);
109
+ if (!nestedSchemaUid || ancestors.has(nestedSchemaUid)) {
99
110
  return [];
100
111
  }
101
112
  return getAvailableFields(
102
- schemas.get(attribute.component),
113
+ schemas.get(nestedSchemaUid),
103
114
  schemas,
104
115
  `${path}.`,
105
- /* @__PURE__ */ new Set([...ancestors, attribute.component])
116
+ /* @__PURE__ */ new Set([...ancestors, nestedSchemaUid])
106
117
  );
107
118
  });
108
119
  const systemFields = prefix === "" ? SYSTEM_SCALAR_FIELDS : [];
@@ -3,7 +3,7 @@ import { Main, Box, Typography, Field, TextInput, Flex, Button } from "@strapi/d
3
3
  import { useState, useCallback, useEffect, useMemo } from "react";
4
4
  import { useIntl } from "react-intl";
5
5
  import { useFetchClient } from "@strapi/strapi/admin";
6
- import { g as getTranslation } from "./index-DGXo1dxk.mjs";
6
+ import { g as getTranslation } from "./index-DdvdGn6T.mjs";
7
7
  const FIELD_TYPES = /* @__PURE__ */ new Set([
8
8
  "string",
9
9
  "text",
@@ -29,6 +29,15 @@ const SYSTEM_SCALAR_FIELDS = [
29
29
  "status"
30
30
  ];
31
31
  const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
32
+ const getNestedSchemaUid = (attribute) => {
33
+ if (attribute.type === "component") {
34
+ return attribute.component;
35
+ }
36
+ if (attribute.type === "relation") {
37
+ return attribute.targetModel ?? attribute.target;
38
+ }
39
+ return void 0;
40
+ };
32
41
  const getPlaceholders = (template) => {
33
42
  if (!template.trim()) {
34
43
  return null;
@@ -72,10 +81,11 @@ const validateTemplate = (template, attributes, schemas) => {
72
81
  if (isLastSegment) {
73
82
  return !FIELD_TYPES.has(attribute.type ?? "");
74
83
  }
75
- if (attribute.type !== "component" || !attribute.component) {
84
+ const nestedSchemaUid = getNestedSchemaUid(attribute);
85
+ if (!nestedSchemaUid) {
76
86
  return true;
77
87
  }
78
- currentAttributes = schemas?.get(attribute.component)?.attributes;
88
+ currentAttributes = schemas?.get(nestedSchemaUid)?.attributes;
79
89
  }
80
90
  return true;
81
91
  });
@@ -93,14 +103,15 @@ const getAvailableFields = (schema, schemas, prefix = "", ancestors = /* @__PURE
93
103
  if (FIELD_TYPES.has(attribute.type ?? "")) {
94
104
  return [path];
95
105
  }
96
- if (attribute.type !== "component" || !attribute.component || ancestors.has(attribute.component)) {
106
+ const nestedSchemaUid = getNestedSchemaUid(attribute);
107
+ if (!nestedSchemaUid || ancestors.has(nestedSchemaUid)) {
97
108
  return [];
98
109
  }
99
110
  return getAvailableFields(
100
- schemas.get(attribute.component),
111
+ schemas.get(nestedSchemaUid),
101
112
  schemas,
102
113
  `${path}.`,
103
- /* @__PURE__ */ new Set([...ancestors, attribute.component])
114
+ /* @__PURE__ */ new Set([...ancestors, nestedSchemaUid])
104
115
  );
105
116
  });
106
117
  const systemFields = prefix === "" ? SYSTEM_SCALAR_FIELDS : [];
@@ -89,7 +89,7 @@ const plugin = {
89
89
  id: `${PLUGIN_ID}.plugin.name`,
90
90
  defaultMessage: "Relation Names"
91
91
  },
92
- Component: () => Promise.resolve().then(() => require("./SettingsPage-CaBXOirN.js")).then((module2) => ({ default: module2.SettingsPage })),
92
+ Component: () => Promise.resolve().then(() => require("./SettingsPage-BGsuRnwx.js")).then((module2) => ({ default: module2.SettingsPage })),
93
93
  permissions: []
94
94
  });
95
95
  app.registerPlugin({
@@ -88,7 +88,7 @@ const plugin = {
88
88
  id: `${PLUGIN_ID}.plugin.name`,
89
89
  defaultMessage: "Relation Names"
90
90
  },
91
- Component: () => import("./SettingsPage-CpTyZSvB.mjs").then((module) => ({ default: module.SettingsPage })),
91
+ Component: () => import("./SettingsPage-Blk-4H_X.mjs").then((module) => ({ default: module.SettingsPage })),
92
92
  permissions: []
93
93
  });
94
94
  app.registerPlugin({
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const index = require("./index-CKaZcnCM.js");
3
+ const index = require("./index-DFr0KRky.js");
4
4
  exports.default = index.plugin;
@@ -1,4 +1,4 @@
1
- import { p } from "./index-DGXo1dxk.mjs";
1
+ import { p } from "./index-DdvdGn6T.mjs";
2
2
  export {
3
3
  p as default
4
4
  };
@@ -1,6 +1,8 @@
1
1
  type TemplateAttribute = {
2
2
  type?: string;
3
3
  component?: string;
4
+ target?: string;
5
+ targetModel?: string;
4
6
  private?: boolean;
5
7
  };
6
8
  type TemplateSchema = {
@@ -164,6 +164,9 @@ const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)
164
164
  const isRecord$1 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
165
165
  const getPathValue = (value, path) => {
166
166
  return path.split(".").reduce((current, segment) => {
167
+ if (Array.isArray(current)) {
168
+ return current.map((item) => isRecord$1(item) ? item[segment] : void 0).filter((item) => item !== void 0);
169
+ }
167
170
  return isRecord$1(current) ? current[segment] : void 0;
168
171
  }, value);
169
172
  };
@@ -215,10 +218,11 @@ const compileTemplate = (template, targetSchema, resolveSchema) => {
215
218
  if (isLastSegment) {
216
219
  return SCALAR_TYPES.has(attribute.type ?? "");
217
220
  }
218
- if (attribute.type !== "component" || !attribute.component || !resolveSchema) {
221
+ const nestedSchemaUid = attribute.type === "component" ? attribute.component : attribute.type === "relation" ? attribute.target : void 0;
222
+ if (!nestedSchemaUid || !resolveSchema) {
219
223
  return false;
220
224
  }
221
- schema = resolveSchema(attribute.component);
225
+ schema = resolveSchema(nestedSchemaUid);
222
226
  if (!schema) {
223
227
  return false;
224
228
  }
@@ -329,10 +333,32 @@ const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility
329
333
  };
330
334
  };
331
335
  const buildPopulate = (placeholders) => {
332
- const roots = new Set(
333
- placeholders.filter((placeholder) => placeholder.includes(".")).map((placeholder) => placeholder.split(".")[0])
334
- );
335
- return roots.size > 0 ? Object.fromEntries(Array.from(roots, (root) => [root, true])) : void 0;
336
+ const populate = {};
337
+ for (const placeholder of placeholders) {
338
+ const segments = placeholder.split(".");
339
+ if (segments.length < 2) {
340
+ continue;
341
+ }
342
+ let current = populate;
343
+ for (const [index2, segment] of segments.slice(0, -1).entries()) {
344
+ const existing = current[segment];
345
+ if (existing === true) {
346
+ break;
347
+ }
348
+ if (index2 === segments.length - 2) {
349
+ current[segment] = existing ?? true;
350
+ break;
351
+ }
352
+ if (!existing) {
353
+ current[segment] = { populate: {} };
354
+ }
355
+ const next = current[segment];
356
+ if (next !== true) {
357
+ current = next.populate;
358
+ }
359
+ }
360
+ }
361
+ return Object.keys(populate).length > 0 ? populate : void 0;
336
362
  };
337
363
  const hydrateValues = async (strapi, ctx, runtime, values) => {
338
364
  const targetSchema = strapi.getModel(runtime.targetUid);
@@ -162,6 +162,9 @@ const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)
162
162
  const isRecord$1 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
163
163
  const getPathValue = (value, path) => {
164
164
  return path.split(".").reduce((current, segment) => {
165
+ if (Array.isArray(current)) {
166
+ return current.map((item) => isRecord$1(item) ? item[segment] : void 0).filter((item) => item !== void 0);
167
+ }
165
168
  return isRecord$1(current) ? current[segment] : void 0;
166
169
  }, value);
167
170
  };
@@ -213,10 +216,11 @@ const compileTemplate = (template, targetSchema, resolveSchema) => {
213
216
  if (isLastSegment) {
214
217
  return SCALAR_TYPES.has(attribute.type ?? "");
215
218
  }
216
- if (attribute.type !== "component" || !attribute.component || !resolveSchema) {
219
+ const nestedSchemaUid = attribute.type === "component" ? attribute.component : attribute.type === "relation" ? attribute.target : void 0;
220
+ if (!nestedSchemaUid || !resolveSchema) {
217
221
  return false;
218
222
  }
219
- schema = resolveSchema(attribute.component);
223
+ schema = resolveSchema(nestedSchemaUid);
220
224
  if (!schema) {
221
225
  return false;
222
226
  }
@@ -327,10 +331,32 @@ const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility
327
331
  };
328
332
  };
329
333
  const buildPopulate = (placeholders) => {
330
- const roots = new Set(
331
- placeholders.filter((placeholder) => placeholder.includes(".")).map((placeholder) => placeholder.split(".")[0])
332
- );
333
- return roots.size > 0 ? Object.fromEntries(Array.from(roots, (root) => [root, true])) : void 0;
334
+ const populate = {};
335
+ for (const placeholder of placeholders) {
336
+ const segments = placeholder.split(".");
337
+ if (segments.length < 2) {
338
+ continue;
339
+ }
340
+ let current = populate;
341
+ for (const [index2, segment] of segments.slice(0, -1).entries()) {
342
+ const existing = current[segment];
343
+ if (existing === true) {
344
+ break;
345
+ }
346
+ if (index2 === segments.length - 2) {
347
+ current[segment] = existing ?? true;
348
+ break;
349
+ }
350
+ if (!existing) {
351
+ current[segment] = { populate: {} };
352
+ }
353
+ const next = current[segment];
354
+ if (next !== true) {
355
+ current = next.populate;
356
+ }
357
+ }
358
+ }
359
+ return Object.keys(populate).length > 0 ? populate : void 0;
334
360
  };
335
361
  const hydrateValues = async (strapi, ctx, runtime, values) => {
336
362
  const targetSchema = strapi.getModel(runtime.targetUid);
@@ -1,6 +1,9 @@
1
1
  import { Core } from '@strapi/strapi';
2
2
  import { SchemaLike } from './utils/relation-labels';
3
- declare const buildPopulate: (placeholders: string[]) => Record<string, true> | undefined;
3
+ type Populate = Record<string, true | {
4
+ populate: Populate;
5
+ }>;
6
+ declare const buildPopulate: (placeholders: string[]) => Populate | undefined;
4
7
  declare const relationLabels: ({ strapi }: {
5
8
  strapi: Core.Strapi;
6
9
  }) => {
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "author": "Christoph Tupi <christoph.tupi@gmail.com>",
6
6
  "keywords": [],
7
7
  "type": "commonjs",
8
- "version": "1.1.0",
8
+ "version": "1.2.0",
9
9
  "strapi": {
10
10
  "kind": "plugin",
11
11
  "name": "strapi-relation-names",