@vectoriox/iox-ui 5.68.0 → 5.69.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.
@@ -75,6 +75,7 @@ const SPEC_SITE_PLAN_SHAPE = {
75
75
  "count": "integer ≥ 0",
76
76
  "sample": "boolean — items are flagged isSample",
77
77
  "isPublished": "boolean",
78
+ "perItem?": "per field, one string per sample: real content that wins over the template",
78
79
  "[field]?": "the field's value — strings are templates: {n} = the item's 1-based number, {sampleImage:n} / {sampleImage:n+k} = a bundled sample image"
79
80
  },
80
81
  "cmsDataSource": {
@@ -10887,6 +10888,7 @@ const ROLE_KEYS$1 = shapeKeys(SHAPE['roles']);
10887
10888
  const PAGE_SEO_KEYS = shapeKeys(SHAPE['pageSeo']);
10888
10889
  const ARRAY_TYPE = 'array';
10889
10890
  const FIELD_KEYS = shapeKeys(SHAPE['field']);
10891
+ const SLUG_FIELD = 'slug';
10890
10892
  const SEED_FIXED_KEYS = shapeKeys(SHAPE['seed']);
10891
10893
  const CMS_SOURCE_KEYS = shapeKeys(SHAPE['cmsDataSource']);
10892
10894
  const RELATIVE_SOURCE_KEYS = shapeKeys(SHAPE['relativeDataSource']);
@@ -11025,6 +11027,21 @@ function validateContentTypes(c, list) {
11025
11027
  for (const key of ['sample', 'isPublished'])
11026
11028
  if (typeof seed[key] !== 'boolean')
11027
11029
  c.add(SitePlanIssueCode.Shape, `${path}.seed.${key}`, 'must be a boolean');
11030
+ const perItem = seed['perItem'];
11031
+ if (perItem !== undefined) {
11032
+ if (!isPlainObject(perItem))
11033
+ c.add(SitePlanIssueCode.Shape, `${path}.seed.perItem`, 'must be an object');
11034
+ else
11035
+ for (const [field, list] of Object.entries(perItem)) {
11036
+ const at = `${path}.seed.perItem.${field}`;
11037
+ if (!fieldSet.has(field))
11038
+ c.add(SitePlanIssueCode.UnknownField, at, `"${field}" is not a field of ${name}`);
11039
+ else if (!Array.isArray(list) || list.length !== seed['count'] || list.some(v => typeof v !== 'string'))
11040
+ c.add(SitePlanIssueCode.Shape, at, `must be ${String(seed['count'])} strings, one per sample`);
11041
+ else if (field === SLUG_FIELD && new Set(list).size !== list.length)
11042
+ c.add(SitePlanIssueCode.Shape, at, 'each sample needs its own slug');
11043
+ }
11044
+ }
11028
11045
  for (const key of Object.keys(seed)) {
11029
11046
  if (!SEED_FIXED_KEYS.includes(key) && !fieldSet.has(key))
11030
11047
  c.add(SitePlanIssueCode.UnknownField, `${path}.seed.${key}`, `"${key}" is not a field of ${name}`);
@@ -13061,7 +13078,8 @@ function composeItems(type, fillEmpty) {
13061
13078
  for (const field of type.fields) {
13062
13079
  const given = seed[field.name];
13063
13080
  const empty = !(field.name in seed) || given === '' || (Array.isArray(given) && !given.length);
13064
- const value = empty && fillEmpty ? defaultSeed(type, field) : given;
13081
+ const own = seed.perItem?.[field.name]?.[n - 1];
13082
+ const value = own || (empty && fillEmpty ? defaultSeed(type, field) : given);
13065
13083
  if (value !== undefined)
13066
13084
  item[field.name] = expandSeedValue(value, n);
13067
13085
  }
@@ -13669,6 +13687,7 @@ const WIRE_KEYS = {
13669
13687
  seedValue: {
13670
13688
  field: { refs: ['seed.[field]'], text: 'string — a field of the type' },
13671
13689
  value: { refs: ['seed.[field]'], text: `string | string[] — ${describeShapeKey(PLAN_SHAPE['seed'], '[field]')} (a list for an array field)` },
13690
+ each: { refs: ['seed.perItem'], text: 'string[] — one value per sample, in order, when each sample has its own content', none: '[]' },
13672
13691
  },
13673
13692
  dataSource: {
13674
13693
  alias: { refs: ['cmsDataSource.alias', 'relativeDataSource.alias'] },
@@ -13798,7 +13817,7 @@ const shapedAs = (entry, options, properties) => {
13798
13817
  return jsonShapedObject(wireEntryShape(entry, options), Object.fromEntries(Object.entries(properties).filter(([k]) => !left.includes(k))), `wire ${entry}`);
13799
13818
  };
13800
13819
  function wireContentTypeJsonSchema(options = {}) {
13801
- const value = shaped(PlanWireEntry.SeedValue, { field: jsonText(), value: jsonAnyOf([jsonText(), jsonListOf(jsonText())]) });
13820
+ const value = shaped(PlanWireEntry.SeedValue, { field: jsonText(), value: jsonAnyOf([jsonText(), jsonListOf(jsonText())]), each: jsonListOf(jsonText()) });
13802
13821
  return shapedAs(PlanWireEntry.ContentType, options, {
13803
13822
  name: jsonText(),
13804
13823
  label: jsonText(),
@@ -13910,8 +13929,18 @@ function expandWireContentType(wire, options = {}) {
13910
13929
  const { values, ...fixed } = seed;
13911
13930
  if (fixed['count'] === 0 && emptyList(values))
13912
13931
  return out;
13913
- const map = listToMap(values ?? [], 'field', 'value');
13914
- return { ...out, seed: isPlainObject(map) ? { ...fixed, ...map } : { ...fixed, values } };
13932
+ const perItem = {};
13933
+ const plain = Array.isArray(values) ? values.map(row => {
13934
+ if (!isPlainObject(row))
13935
+ return row;
13936
+ const { each, ...rest } = row;
13937
+ if (Array.isArray(each) && each.length)
13938
+ perItem[String(rest['field'])] = each;
13939
+ return rest;
13940
+ }) : values;
13941
+ const map = listToMap(plain ?? [], 'field', 'value');
13942
+ const items = Object.keys(perItem).length ? { perItem } : {};
13943
+ return { ...out, seed: isPlainObject(map) ? { ...fixed, ...items, ...map } : { ...fixed, values } };
13915
13944
  }
13916
13945
  function expandWireDataSource(wire) {
13917
13946
  if (!isPlainObject(wire))
@@ -13994,10 +14023,10 @@ function fieldToWire(field) {
13994
14023
  function contentTypeToWire(type, options = {}) {
13995
14024
  const played = type.roles;
13996
14025
  const roles = ROLE_KEYS.filter(role => played[role]).map(role => ({ role, field: played[role] }));
13997
- const { count = 0, sample = false, isPublished = false, ...values } = type.seed ?? {};
14026
+ const { count = 0, sample = false, isPublished = false, perItem = {}, ...values } = type.seed ?? {};
13998
14027
  return {
13999
14028
  name: type.name, label: type.label, roles, ...(options.children === false ? {} : { fields: type.fields.map(fieldToWire) }),
14000
- seed: { count, sample, isPublished, values: mapToList(values, 'field', 'value') },
14029
+ seed: { count, sample, isPublished, values: mapToList(values, 'field', 'value').map(row => ({ ...row, each: perItem[String(row['field'])] ?? [] })) },
14001
14030
  };
14002
14031
  }
14003
14032
  function dataSourceToWire(source) {
@@ -14949,7 +14978,10 @@ function writtenTexts(plan) {
14949
14978
  });
14950
14979
  plan.contentTypes?.forEach((type, t) => {
14951
14980
  add(`contentTypes[${t}].label`, type.label);
14952
- for (const [field, value] of Object.entries(type.seed ?? {})) {
14981
+ const { perItem, ...seed } = type.seed ?? {};
14982
+ for (const [field, list] of Object.entries(perItem ?? {}))
14983
+ list.forEach((v, k) => add(`contentTypes[${t}].seed.perItem.${field}[${k}]`, v));
14984
+ for (const [field, value] of Object.entries(seed)) {
14953
14985
  const values = Array.isArray(value) ? value : [value];
14954
14986
  values.forEach((v, k) => add(`contentTypes[${t}].seed.${field}${Array.isArray(value) ? `[${k}]` : ''}`, v));
14955
14987
  }