@aduptive/strapi-blockscene 2.0.0-alpha.8 → 2.0.0-alpha.9
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 +97 -6
- package/dist/admin.cjs +21 -7
- package/dist/admin.mjs +21 -7
- package/dist/server.cjs +110 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -147,9 +147,10 @@ layout, guessed from the component name and display name (hero, banner,
|
|
|
147
147
|
cover; text, rich, quote, title; image, media, video, gallery, carousel; list,
|
|
148
148
|
query, archive, related, posts, projects; card; cta, button, link; form,
|
|
149
149
|
contact; wrapper, column, grid, divider, spacer, section; else text). Override
|
|
150
|
-
it, and add up to 10 tags (1 to 24 characters), in Settings, Blockscene,
|
|
151
|
-
the plugin config
|
|
152
|
-
|
|
150
|
+
it, and add up to 10 tags (1 to 24 characters), in Settings, Blockscene, in
|
|
151
|
+
the plugin config (`components[uid].typology` and `components[uid].tags`) or in
|
|
152
|
+
the component schema ([`pluginOptions.blockscene`](#describing-blocks-and-fields-in-the-schema)).
|
|
153
|
+
A Settings value wins over the config, the config over the schema. The former `category` config key still
|
|
153
154
|
works: it becomes the block's tag when no tags are set.
|
|
154
155
|
|
|
155
156
|
The native "Add a component to <zone>" button of an editable zone opens this
|
|
@@ -269,7 +270,16 @@ as an input: the plugin removes it from the components' edit layouts through
|
|
|
269
270
|
the Content Manager's layout hook (it may be listed in "Configure the view" of
|
|
270
271
|
a component; leave it out of the layout). It is not marked `visible: false`,
|
|
271
272
|
because before Strapi 5.45 the edit view crashes on a value whose attribute the
|
|
272
|
-
admin does not know.
|
|
273
|
+
admin does not know. It is kept out of the Content-Type Builder instead (the
|
|
274
|
+
plugin wraps the builder's component reads), so saving a component there in
|
|
275
|
+
`strapi develop` never writes it to the component's JSON file. With earlier
|
|
276
|
+
versions (up to 2.0.0-alpha.8, and 1.0.0-alpha.8 on Strapi 4) the builder did
|
|
277
|
+
write it (`"bsHidden": { "type": "boolean", "configurable": false, "default": false }`);
|
|
278
|
+
a file that has it keeps working, and the builder keeps it there on later saves.
|
|
279
|
+
To clean it up, delete that entry from the component JSON by hand (the plugin
|
|
280
|
+
still adds the attribute at startup, so the column and its data stay). To check,
|
|
281
|
+
edit a Dynamic Zone component in the builder (for example its display name),
|
|
282
|
+
save, and diff its JSON file: only your change is there. Rename it with the plugin config
|
|
273
283
|
`hiddenAttribute: 'myName'`, or set `hiddenAttribute: false` to add nothing (no
|
|
274
284
|
eye, nothing stripped). A component that already has an attribute of that name
|
|
275
285
|
with another type is skipped with a warning. Removing the plugin (or setting
|
|
@@ -777,8 +787,10 @@ same hook the i18n plugin uses), so nothing in the DOM is patched and the
|
|
|
777
787
|
saved view configuration is never changed. Per field, the first that applies:
|
|
778
788
|
|
|
779
789
|
1. **Project texts** from the plugin config `fields` (below);
|
|
780
|
-
2. **
|
|
781
|
-
|
|
790
|
+
2. **schema metadata**: `pluginOptions.blockscene` on the attribute, with its
|
|
791
|
+
translations (see [Describing blocks and fields in the schema](#describing-blocks-and-fields-in-the-schema));
|
|
792
|
+
3. **a label set in "Configure the view"** (anything other than the attribute name);
|
|
793
|
+
4. **the humanized name** when "Friendly field labels" is on: `mobileColumnsCount`
|
|
782
794
|
reads "Mobile columns count", `page_seo` "Page seo", acronym runs stay
|
|
783
795
|
(`pageSEO` "Page SEO", `ctaURL` "Cta URL").
|
|
784
796
|
|
|
@@ -838,6 +850,85 @@ content type turned off all leave the native labels; any error leaves the
|
|
|
838
850
|
layout untouched. Changing the interface language applies from the next edit
|
|
839
851
|
view.
|
|
840
852
|
|
|
853
|
+
## Describing blocks and fields in the schema
|
|
854
|
+
|
|
855
|
+
The same texts can live in the component and content type JSON files, next to
|
|
856
|
+
what they describe, instead of the long `components` and `fields` config maps:
|
|
857
|
+
|
|
858
|
+
```jsonc
|
|
859
|
+
// src/components/sections/rich-text.json
|
|
860
|
+
{
|
|
861
|
+
"collectionName": "components_sections_rich_texts",
|
|
862
|
+
"info": { "displayName": "Rich text", "description": "Formatted text with an optional title and anchor." },
|
|
863
|
+
"pluginOptions": { "blockscene": { "tags": ["Editorial"] } },
|
|
864
|
+
"attributes": {
|
|
865
|
+
"anchor": { "type": "string", "pluginOptions": { "blockscene": { "help": "Id for links to this block (#id), without the \"#\"." } } },
|
|
866
|
+
"text": { "type": "richtext" }
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
// src/components/shared/tag.json, attribute "label"
|
|
870
|
+
"label": { "type": "string", "pluginOptions": { "blockscene": { "label": "Tag text", "help": "One or two words." } } }
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
- **Block**: the native `info.description` is the gallery description;
|
|
874
|
+
`pluginOptions.blockscene` takes `label`, `typology`, `tags`, `keywords` and
|
|
875
|
+
`image`, with the same rules as the `components` config entries.
|
|
876
|
+
- **Field** (any content type or component attribute): `pluginOptions.blockscene`
|
|
877
|
+
takes `label` (up to 80 characters), `description` (300), `placeholder` (120)
|
|
878
|
+
and `help` (500), each a plain string in the source language, English.
|
|
879
|
+
|
|
880
|
+
Other languages go in one flat file per locale, given to the plugin config:
|
|
881
|
+
|
|
882
|
+
```js
|
|
883
|
+
// config/plugins.js
|
|
884
|
+
blockscene: { config: { translations: { 'pt-BR': require('./blockscene/pt-BR.json') } } },
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
```json
|
|
888
|
+
{
|
|
889
|
+
"sections.rich-text": "Texto formatado",
|
|
890
|
+
"sections.rich-text.description": "Texto formatado com título e âncora opcionais.",
|
|
891
|
+
"sections.rich-text.anchor": "Âncora",
|
|
892
|
+
"sections.rich-text.anchor.help": "Id para links a este bloco (#id), sem o \"#\".",
|
|
893
|
+
"shared.tag.label": "Texto da tag"
|
|
894
|
+
}
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
Keys are `<uid>` (block label, else the `displayName`), `<uid>.description`
|
|
898
|
+
(block description), `<uid>.<attribute>` (field label) and
|
|
899
|
+
`<uid>.<attribute>.<description|placeholder|help>`. A component attribute named
|
|
900
|
+
`description` takes its label as `<uid>.description.label`. The admin picks the
|
|
901
|
+
language as for the field labels above; a missing key shows the schema's
|
|
902
|
+
English text, and a translated field label with no English one in the schema
|
|
903
|
+
shows Strapi's own label in English.
|
|
904
|
+
|
|
905
|
+
Precedence, per text: the plugin config (`components`, `fields`, and Settings
|
|
906
|
+
for typology and tags) > schema metadata > native Strapi ("Configure the view"
|
|
907
|
+
labels, `displayName`) > automatic (humanized names, guessed typology). The
|
|
908
|
+
existing config keeps working unchanged. Everything is read once at boot:
|
|
909
|
+
invalid values (a typology outside the list, a label over 80 characters, an
|
|
910
|
+
unknown key) and translation keys naming no known block or field are left out,
|
|
911
|
+
with one warning listing them by uid.
|
|
912
|
+
|
|
913
|
+
**Content-Type Builder.** Tested on Strapi 5.52.1 and 4.26.1 in development
|
|
914
|
+
mode: editing and saving a component in the Content-Type Builder keeps
|
|
915
|
+
`pluginOptions.blockscene` (block and attributes) and `info.description`. It
|
|
916
|
+
has no field for them, so add and change them in the JSON files.
|
|
917
|
+
|
|
918
|
+
To move an existing config, from a clone of this repository (idempotent; each
|
|
919
|
+
schema file keeps its indentation and key order; labels equal to what the
|
|
920
|
+
plugin would show anyway, the humanized name or the `displayName`, are dropped):
|
|
921
|
+
|
|
922
|
+
```bash
|
|
923
|
+
node scripts/schema-metadata.mjs ../my-project/backend --fields fields.json --components components.json [--out dir] [--source en] [--dry-run]
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
`fields.json` and `components.json` hold the two config maps as JSON. The
|
|
927
|
+
translation files go to `config/blockscene/<locale>.json`; the script lists
|
|
928
|
+
what it could not place and the `components` keys that stay in the config
|
|
929
|
+
(`variants`, `template`). Remove the migrated entries from the config
|
|
930
|
+
afterwards: the config wins.
|
|
931
|
+
|
|
841
932
|
## Version history (Strapi 5)
|
|
842
933
|
|
|
843
934
|
Off by default. Turn it on in Settings, Blockscene, "Version history" (or from
|
package/dist/admin.cjs
CHANGED
|
@@ -482,6 +482,11 @@ function localized(value, locale = "en") {
|
|
|
482
482
|
const key = keys.find((k) => k.toLowerCase() === lower) ?? keys.find((k) => k.toLowerCase() === lang(lower)) ?? keys.find((k) => lang(k) === lang(lower)) ?? keys.find((k) => lang(k) === "en") ?? keys[0];
|
|
483
483
|
return key === void 0 ? void 0 : value[key];
|
|
484
484
|
}
|
|
485
|
+
function localizeBlocks(catalog, locale) {
|
|
486
|
+
if (!catalog?.components) return catalog;
|
|
487
|
+
const text = (value) => localized(value, locale) ?? void 0;
|
|
488
|
+
return { ...catalog, components: Object.fromEntries(Object.entries(catalog.components).map(([uid, entry]) => [uid, entry && (typeof entry.label === "object" || typeof entry.description === "object") ? { ...entry, label: text(entry.label), description: text(entry.description) } : entry])) };
|
|
489
|
+
}
|
|
485
490
|
function fieldText(fields, owner, name, label, locale, on) {
|
|
486
491
|
const entry = owner && fields?.[owner]?.[name] || {};
|
|
487
492
|
const out = {};
|
|
@@ -489,7 +494,7 @@ function fieldText(fields, owner, name, label, locale, on) {
|
|
|
489
494
|
if (text !== void 0) out.label = text;
|
|
490
495
|
for (const key of ["description", "placeholder", "help"]) {
|
|
491
496
|
const value = localized(entry[key], locale);
|
|
492
|
-
if (value
|
|
497
|
+
if (value != null) out[key] = value;
|
|
493
498
|
}
|
|
494
499
|
return Object.keys(out).length ? out : null;
|
|
495
500
|
}
|
|
@@ -6370,7 +6375,7 @@ function ZoneGallery({
|
|
|
6370
6375
|
setConfig(null);
|
|
6371
6376
|
setDetail(null);
|
|
6372
6377
|
get("/blockscene/catalog").then(({ data }) => {
|
|
6373
|
-
if (active) setConfig(data);
|
|
6378
|
+
if (active) setConfig(localizeBlocks(data, t.locale));
|
|
6374
6379
|
}).catch(() => {
|
|
6375
6380
|
if (active) setError(t.failed);
|
|
6376
6381
|
});
|
|
@@ -7053,6 +7058,14 @@ var EdgeButton = import_styled_components6.default.button`
|
|
|
7053
7058
|
`;
|
|
7054
7059
|
var HOVER_ATTR = "data-blockscene-hover";
|
|
7055
7060
|
var HOVER_GRACE = 1200;
|
|
7061
|
+
var accordionCard = (row, ds) => {
|
|
7062
|
+
const toggle = row.querySelector("button[aria-expanded]");
|
|
7063
|
+
if (!toggle) return row;
|
|
7064
|
+
if (ds === 2) return toggle.closest("div[data-state]:has(> h3)") || row;
|
|
7065
|
+
for (let el = toggle.parentElement; el && el !== row; el = el.parentElement)
|
|
7066
|
+
if (getComputedStyle(el).borderTopStyle !== "none") return el;
|
|
7067
|
+
return row;
|
|
7068
|
+
};
|
|
7056
7069
|
var Frame2 = import_styled_components6.default.iframe`
|
|
7057
7070
|
position: absolute;
|
|
7058
7071
|
top: 0;
|
|
@@ -7746,7 +7759,8 @@ function PagePreview({
|
|
|
7746
7759
|
const item = hovered && active ? findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[indexOf(hovered)] : void 0;
|
|
7747
7760
|
hoverRow.current = item || null;
|
|
7748
7761
|
if (!item) return setEdge(null);
|
|
7749
|
-
item
|
|
7762
|
+
const card = accordionCard(item, host.ds);
|
|
7763
|
+
card.setAttribute(HOVER_ATTR, "");
|
|
7750
7764
|
const measure = () => {
|
|
7751
7765
|
const r = item.getBoundingClientRect();
|
|
7752
7766
|
setEdge(r.bottom < 0 ? "up" : r.top > window.innerHeight ? "down" : null);
|
|
@@ -7755,7 +7769,7 @@ function PagePreview({
|
|
|
7755
7769
|
window.addEventListener("scroll", measure, true);
|
|
7756
7770
|
window.addEventListener("resize", measure);
|
|
7757
7771
|
return () => {
|
|
7758
|
-
|
|
7772
|
+
card.removeAttribute(HOVER_ATTR);
|
|
7759
7773
|
window.removeEventListener("scroll", measure, true);
|
|
7760
7774
|
window.removeEventListener("resize", measure);
|
|
7761
7775
|
};
|
|
@@ -7824,7 +7838,7 @@ function PagePreview({
|
|
|
7824
7838
|
},
|
|
7825
7839
|
children: [
|
|
7826
7840
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("style", { children: `${SPLIT_STYLE}
|
|
7827
|
-
[${HOVER_ATTR}] {
|
|
7841
|
+
[${HOVER_ATTR}] { ${host.ds === 2 ? `box-shadow: 0 0 0 1px ${theme?.colors?.primary600 || "#4945ff"};` : `border-color: ${theme?.colors?.primary600 || "#4945ff"} !important;`} }
|
|
7828
7842
|
${/* Strapi 4's scrolled header is fixed to the viewport's right edge: beside the pane in split mode, gone under it otherwise. */
|
|
7829
7843
|
resizable ? "[data-strapi-header-sticky] { right: var(--bp-pane) !important; }" : "[data-strapi-header-sticky] { display: none !important; }"}` }),
|
|
7830
7844
|
resizable && edge && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
@@ -9460,8 +9474,8 @@ var cached = null;
|
|
|
9460
9474
|
var pending = null;
|
|
9461
9475
|
var flight = null;
|
|
9462
9476
|
var load = (get) => flight || (flight = get("/blockscene/catalog").then(({ data }) => {
|
|
9463
|
-
cached = data;
|
|
9464
|
-
return
|
|
9477
|
+
cached = localizeBlocks(data, adminLocale());
|
|
9478
|
+
return cached;
|
|
9465
9479
|
}).finally(() => {
|
|
9466
9480
|
flight = null;
|
|
9467
9481
|
}));
|
package/dist/admin.mjs
CHANGED
|
@@ -501,6 +501,11 @@ function localized(value, locale = "en") {
|
|
|
501
501
|
const key = keys.find((k) => k.toLowerCase() === lower) ?? keys.find((k) => k.toLowerCase() === lang(lower)) ?? keys.find((k) => lang(k) === lang(lower)) ?? keys.find((k) => lang(k) === "en") ?? keys[0];
|
|
502
502
|
return key === void 0 ? void 0 : value[key];
|
|
503
503
|
}
|
|
504
|
+
function localizeBlocks(catalog, locale) {
|
|
505
|
+
if (!catalog?.components) return catalog;
|
|
506
|
+
const text = (value) => localized(value, locale) ?? void 0;
|
|
507
|
+
return { ...catalog, components: Object.fromEntries(Object.entries(catalog.components).map(([uid, entry]) => [uid, entry && (typeof entry.label === "object" || typeof entry.description === "object") ? { ...entry, label: text(entry.label), description: text(entry.description) } : entry])) };
|
|
508
|
+
}
|
|
504
509
|
function fieldText(fields, owner, name, label, locale, on) {
|
|
505
510
|
const entry = owner && fields?.[owner]?.[name] || {};
|
|
506
511
|
const out = {};
|
|
@@ -508,7 +513,7 @@ function fieldText(fields, owner, name, label, locale, on) {
|
|
|
508
513
|
if (text !== void 0) out.label = text;
|
|
509
514
|
for (const key of ["description", "placeholder", "help"]) {
|
|
510
515
|
const value = localized(entry[key], locale);
|
|
511
|
-
if (value
|
|
516
|
+
if (value != null) out[key] = value;
|
|
512
517
|
}
|
|
513
518
|
return Object.keys(out).length ? out : null;
|
|
514
519
|
}
|
|
@@ -6389,7 +6394,7 @@ function ZoneGallery({
|
|
|
6389
6394
|
setConfig(null);
|
|
6390
6395
|
setDetail(null);
|
|
6391
6396
|
get("/blockscene/catalog").then(({ data }) => {
|
|
6392
|
-
if (active) setConfig(data);
|
|
6397
|
+
if (active) setConfig(localizeBlocks(data, t.locale));
|
|
6393
6398
|
}).catch(() => {
|
|
6394
6399
|
if (active) setError(t.failed);
|
|
6395
6400
|
});
|
|
@@ -7072,6 +7077,14 @@ var EdgeButton = styled6.button`
|
|
|
7072
7077
|
`;
|
|
7073
7078
|
var HOVER_ATTR = "data-blockscene-hover";
|
|
7074
7079
|
var HOVER_GRACE = 1200;
|
|
7080
|
+
var accordionCard = (row, ds) => {
|
|
7081
|
+
const toggle = row.querySelector("button[aria-expanded]");
|
|
7082
|
+
if (!toggle) return row;
|
|
7083
|
+
if (ds === 2) return toggle.closest("div[data-state]:has(> h3)") || row;
|
|
7084
|
+
for (let el = toggle.parentElement; el && el !== row; el = el.parentElement)
|
|
7085
|
+
if (getComputedStyle(el).borderTopStyle !== "none") return el;
|
|
7086
|
+
return row;
|
|
7087
|
+
};
|
|
7075
7088
|
var Frame2 = styled6.iframe`
|
|
7076
7089
|
position: absolute;
|
|
7077
7090
|
top: 0;
|
|
@@ -7765,7 +7778,8 @@ function PagePreview({
|
|
|
7765
7778
|
const item = hovered && active ? findZoneList(live.current.zoneLabel)?.querySelectorAll(":scope > li")[indexOf(hovered)] : void 0;
|
|
7766
7779
|
hoverRow.current = item || null;
|
|
7767
7780
|
if (!item) return setEdge(null);
|
|
7768
|
-
item
|
|
7781
|
+
const card = accordionCard(item, host.ds);
|
|
7782
|
+
card.setAttribute(HOVER_ATTR, "");
|
|
7769
7783
|
const measure = () => {
|
|
7770
7784
|
const r = item.getBoundingClientRect();
|
|
7771
7785
|
setEdge(r.bottom < 0 ? "up" : r.top > window.innerHeight ? "down" : null);
|
|
@@ -7774,7 +7788,7 @@ function PagePreview({
|
|
|
7774
7788
|
window.addEventListener("scroll", measure, true);
|
|
7775
7789
|
window.addEventListener("resize", measure);
|
|
7776
7790
|
return () => {
|
|
7777
|
-
|
|
7791
|
+
card.removeAttribute(HOVER_ATTR);
|
|
7778
7792
|
window.removeEventListener("scroll", measure, true);
|
|
7779
7793
|
window.removeEventListener("resize", measure);
|
|
7780
7794
|
};
|
|
@@ -7843,7 +7857,7 @@ function PagePreview({
|
|
|
7843
7857
|
},
|
|
7844
7858
|
children: [
|
|
7845
7859
|
/* @__PURE__ */ jsx9("style", { children: `${SPLIT_STYLE}
|
|
7846
|
-
[${HOVER_ATTR}] {
|
|
7860
|
+
[${HOVER_ATTR}] { ${host.ds === 2 ? `box-shadow: 0 0 0 1px ${theme?.colors?.primary600 || "#4945ff"};` : `border-color: ${theme?.colors?.primary600 || "#4945ff"} !important;`} }
|
|
7847
7861
|
${/* Strapi 4's scrolled header is fixed to the viewport's right edge: beside the pane in split mode, gone under it otherwise. */
|
|
7848
7862
|
resizable ? "[data-strapi-header-sticky] { right: var(--bp-pane) !important; }" : "[data-strapi-header-sticky] { display: none !important; }"}` }),
|
|
7849
7863
|
resizable && edge && /* @__PURE__ */ jsx9(
|
|
@@ -9490,8 +9504,8 @@ var cached = null;
|
|
|
9490
9504
|
var pending = null;
|
|
9491
9505
|
var flight = null;
|
|
9492
9506
|
var load = (get) => flight || (flight = get("/blockscene/catalog").then(({ data }) => {
|
|
9493
|
-
cached = data;
|
|
9494
|
-
return
|
|
9507
|
+
cached = localizeBlocks(data, adminLocale());
|
|
9508
|
+
return cached;
|
|
9495
9509
|
}).finally(() => {
|
|
9496
9510
|
flight = null;
|
|
9497
9511
|
}));
|
package/dist/server.cjs
CHANGED
|
@@ -302,6 +302,7 @@ var require_settings = __commonJS({
|
|
|
302
302
|
}
|
|
303
303
|
for (const [uid, schema] of Object.entries(config.schemas || {})) {
|
|
304
304
|
const entry = entries[uid] ||= {};
|
|
305
|
+
for (const [key, value] of Object.entries(config.blocks?.[uid] || {})) if (entry[key] === void 0) entry[key] = value;
|
|
305
306
|
entry.facets = facetsOf(uid, schema, config.schemas);
|
|
306
307
|
entry.typology ||= guessTypology(uid, schema?.info?.displayName);
|
|
307
308
|
entry.tags ||= [];
|
|
@@ -473,6 +474,70 @@ var require_settings = __commonJS({
|
|
|
473
474
|
}
|
|
474
475
|
return out;
|
|
475
476
|
}
|
|
477
|
+
var str = (max) => (value) => typeof value === "string" && value.trim() !== "" && value.length <= max;
|
|
478
|
+
var BLOCK_META = { label: str(80), typology: (value) => TYPOLOGIES2.includes(value), tags: validTags, keywords: str(500), image: (value) => safeUrl2(value) !== void 0 };
|
|
479
|
+
var localize = (holder, key, source, locale, text2) => {
|
|
480
|
+
holder[key] = { ...plain(holder[key]) ? holder[key] : { en: holder[key] ?? source }, [locale]: text2 };
|
|
481
|
+
};
|
|
482
|
+
function schemaMetadata2(schemas = {}, translations = null, notes = []) {
|
|
483
|
+
const blocks = {}, fields = {};
|
|
484
|
+
const own = (holder, where) => {
|
|
485
|
+
const value = holder?.pluginOptions?.blockscene;
|
|
486
|
+
if (value === void 0 || plain(value)) return value || {};
|
|
487
|
+
notes.push(`${where}: pluginOptions.blockscene must be an object`);
|
|
488
|
+
return {};
|
|
489
|
+
};
|
|
490
|
+
for (const [uid, schema] of Object.entries(schemas)) {
|
|
491
|
+
if (!uid.includes("::")) {
|
|
492
|
+
const block = {};
|
|
493
|
+
for (const [key, value] of Object.entries(own(schema, uid))) {
|
|
494
|
+
if (!Object.hasOwn(BLOCK_META, key)) notes.push(`${uid}: unknown key "${key}"`);
|
|
495
|
+
else if (!BLOCK_META[key](value)) notes.push(`${uid}: invalid ${key}`);
|
|
496
|
+
else block[key] = key === "tags" ? cleanTags(value) : value;
|
|
497
|
+
}
|
|
498
|
+
if (typeof schema?.info?.description === "string" && schema.info.description.trim()) block.description = schema.info.description;
|
|
499
|
+
if (Object.keys(block).length) blocks[uid] = block;
|
|
500
|
+
}
|
|
501
|
+
for (const [name, attr] of Object.entries(schema?.attributes || {})) {
|
|
502
|
+
const texts = {};
|
|
503
|
+
for (const [key, value] of Object.entries(own(attr, `${uid}.${name}`))) {
|
|
504
|
+
if (!Object.hasOwn(FIELD_TEXT, key)) notes.push(`${uid}.${name}: unknown key "${key}"`);
|
|
505
|
+
else if (!str(FIELD_TEXT[key])(value)) notes.push(`${uid}.${name}: ${key} must be a string of 1 to ${FIELD_TEXT[key]} characters`);
|
|
506
|
+
else texts[key] = value;
|
|
507
|
+
}
|
|
508
|
+
if (Object.keys(texts).length) (fields[uid] ||= {})[name] = texts;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (translations != null && !plain(translations)) {
|
|
512
|
+
notes.push('translations must be { "<locale>": { "<key>": text } }');
|
|
513
|
+
translations = null;
|
|
514
|
+
}
|
|
515
|
+
const stale = [];
|
|
516
|
+
for (const [locale, entries] of Object.entries(translations || {})) {
|
|
517
|
+
if (!LOCALE.test(locale) || !plain(entries) || JSON.stringify(entries).length > 256 * 1024) {
|
|
518
|
+
notes.push(`translations "${locale}" ignored: expected a locale code with a flat { "<key>": text } map under 256 KB`);
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
for (const [key, text2] of Object.entries(entries)) {
|
|
522
|
+
const uid = Object.keys(schemas).find((uid2) => key === uid2 || key.startsWith(`${uid2}.`));
|
|
523
|
+
const [name, part = "label", extra] = uid ? key.slice(uid.length + 1).split(".") : [];
|
|
524
|
+
const block = uid && !uid.includes("::");
|
|
525
|
+
const target = block && key === uid ? [blocks[uid] ||= {}, "label", schemas[uid]?.info?.displayName || uid, 80] : block && key === `${uid}.description` ? [blocks[uid] ||= {}, "description", null, 300] : uid && Object.hasOwn(schemas[uid]?.attributes || {}, name) && Object.hasOwn(FIELD_TEXT, part) && extra === void 0 ? [null, part, null, FIELD_TEXT[part]] : null;
|
|
526
|
+
if (!target) {
|
|
527
|
+
stale.push(`${locale} "${key}"`);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const [holder, field, source, max] = target;
|
|
531
|
+
if (!str(max)(text2)) {
|
|
532
|
+
notes.push(`translations ${locale} "${key}": a string of 1 to ${max} characters`);
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
localize(holder || ((fields[uid] ||= {})[name] ||= {}), field, source, locale, text2);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (stale.length) notes.push(`translation keys naming no block or field were skipped: ${stale.slice(0, 20).join(", ")}${stale.length > 20 ? ` and ${stale.length - 20} more` : ""}`);
|
|
539
|
+
return { blocks, fields };
|
|
540
|
+
}
|
|
476
541
|
var VARIANT = { id: /^[a-z0-9][a-z0-9_-]{0,39}$/, perComponent: 12, bytes: 16 * 1024, total: 256 * 1024, items: 100 };
|
|
477
542
|
var text = (value) => typeof value === "string";
|
|
478
543
|
var finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
@@ -566,7 +631,7 @@ var require_settings = __commonJS({
|
|
|
566
631
|
return out;
|
|
567
632
|
}
|
|
568
633
|
var mergePrefs2 = (saved, componentUids2) => Object.fromEntries(Object.entries(PREFS).map(([key, max]) => [key, Array.isArray(saved?.[key]) ? [...new Set(saved[key].filter((uid) => componentUids2.includes(uid)))].slice(0, max) : []]));
|
|
569
|
-
module2.exports = { PLUGIN: PLUGIN2, HIDDEN_MODES, TEMPLATES: TEMPLATES2, TYPOLOGIES: TYPOLOGIES2, guessTypology, facetsOf, validatePrefs: validatePrefs2, mergePrefs: mergePrefs2, ICONS, DEFAULTS: DEFAULTS2, catalog: catalog2, validateSettings: validateSettings2, validateFields: validateFields2, validateVariants: validateVariants2, mergeSaved: mergeSaved2, layer: layer2, overrides: overrides2, safeUrl: safeUrl2, fail: fail2 };
|
|
634
|
+
module2.exports = { PLUGIN: PLUGIN2, HIDDEN_MODES, TEMPLATES: TEMPLATES2, TYPOLOGIES: TYPOLOGIES2, guessTypology, facetsOf, validatePrefs: validatePrefs2, mergePrefs: mergePrefs2, ICONS, DEFAULTS: DEFAULTS2, catalog: catalog2, validateSettings: validateSettings2, validateFields: validateFields2, validateVariants: validateVariants2, schemaMetadata: schemaMetadata2, mergeSaved: mergeSaved2, layer: layer2, overrides: overrides2, safeUrl: safeUrl2, fail: fail2 };
|
|
570
635
|
}
|
|
571
636
|
});
|
|
572
637
|
|
|
@@ -594,6 +659,22 @@ var require_hidden = __commonJS({
|
|
|
594
659
|
}
|
|
595
660
|
return { added, skipped };
|
|
596
661
|
}
|
|
662
|
+
function hideFromBuilder2(services, uids, name) {
|
|
663
|
+
const has = new Set(uids);
|
|
664
|
+
const drop = (attributes) => Array.isArray(attributes) ? attributes.filter((attr) => attr?.name !== name) : attributes && typeof attributes === "object" ? Object.fromEntries(Object.entries(attributes).filter(([key]) => key !== name)) : attributes;
|
|
665
|
+
const extend = (uid, wrap) => {
|
|
666
|
+
if (services?.get?.(uid)) services.extend(uid, wrap);
|
|
667
|
+
};
|
|
668
|
+
extend("plugin::content-type-builder.components", (service) => ({ ...service, formatComponent(component, ...rest) {
|
|
669
|
+
const out = service.formatComponent(component, ...rest);
|
|
670
|
+
return has.has(component?.uid) && out?.schema ? { ...out, schema: { ...out.schema, attributes: drop(out.schema.attributes) } } : out;
|
|
671
|
+
} }));
|
|
672
|
+
extend("plugin::content-type-builder.schema", (service) => ({ ...service, async getSchema(...args) {
|
|
673
|
+
const out = await service.getSchema(...args);
|
|
674
|
+
for (const uid of has) if (out?.components?.[uid]) out.components[uid] = { ...out.components[uid], attributes: drop(out.components[uid].attributes) };
|
|
675
|
+
return out;
|
|
676
|
+
} }));
|
|
677
|
+
}
|
|
597
678
|
function stripHidden(data, schema, schemaOf, name, depth = 0) {
|
|
598
679
|
if (!data || typeof data !== "object" || !schema || depth > 20) return data;
|
|
599
680
|
if (Array.isArray(data)) {
|
|
@@ -652,7 +733,7 @@ var require_hidden = __commonJS({
|
|
|
652
733
|
}
|
|
653
734
|
}));
|
|
654
735
|
}
|
|
655
|
-
module2.exports = { DEFAULT_NAME, hiddenName: hiddenName2, zoneComponents, injectHidden: injectHidden2, stripHidden, registerHiddenStrip: registerHiddenStrip2 };
|
|
736
|
+
module2.exports = { DEFAULT_NAME, hiddenName: hiddenName2, zoneComponents, injectHidden: injectHidden2, hideFromBuilder: hideFromBuilder2, stripHidden, registerHiddenStrip: registerHiddenStrip2 };
|
|
656
737
|
}
|
|
657
738
|
});
|
|
658
739
|
|
|
@@ -1604,12 +1685,13 @@ var require_history = __commonJS({
|
|
|
1604
1685
|
});
|
|
1605
1686
|
|
|
1606
1687
|
// server/index.js
|
|
1607
|
-
var { PLUGIN, TEMPLATES, TYPOLOGIES, DEFAULTS, catalog, validateSettings, validateFields, validateVariants, mergeSaved, layer, overrides, validatePrefs, mergePrefs, safeUrl, fail } = require_settings();
|
|
1688
|
+
var { PLUGIN, TEMPLATES, TYPOLOGIES, DEFAULTS, catalog, validateSettings, validateFields, validateVariants, schemaMetadata, mergeSaved, layer, overrides, validatePrefs, mergePrefs, safeUrl, fail } = require_settings();
|
|
1608
1689
|
var { safeGroups, validateGroups, layoutFields } = require_groups();
|
|
1609
|
-
var { hiddenName, injectHidden, registerHiddenStrip } = require_hidden();
|
|
1690
|
+
var { hiddenName, injectHidden, hideFromBuilder, registerHiddenStrip } = require_hidden();
|
|
1610
1691
|
var { CRON, covers, eventContentType, historyService, historyController, registerHistory, registerPurge } = require_history();
|
|
1611
1692
|
var { trashContentType, trashService, trashController } = require_trash();
|
|
1612
1693
|
var STRAPI5 = true;
|
|
1694
|
+
var withHidden = [];
|
|
1613
1695
|
var store = (strapi) => strapi.store({ type: "plugin", name: PLUGIN });
|
|
1614
1696
|
var LEGACY_PLUGIN = "block-picker";
|
|
1615
1697
|
async function readSettings(strapi) {
|
|
@@ -1730,6 +1812,9 @@ module.exports = {
|
|
|
1730
1812
|
settings: null,
|
|
1731
1813
|
// Edit view field texts from code (never stored): { uid: { attribute: { label, description, placeholder } } }. See README "Field labels".
|
|
1732
1814
|
fields: null,
|
|
1815
|
+
// Other languages for the schema metadata (pluginOptions.blockscene, info.description), one flat map per locale:
|
|
1816
|
+
// { 'pt-BR': require('./blockscene/pt-BR.json') }. See README "Describing blocks and fields in the schema".
|
|
1817
|
+
translations: null,
|
|
1733
1818
|
// "Hide on the site": boolean attribute added to every Dynamic Zone component (a DB column); false adds nothing.
|
|
1734
1819
|
hiddenAttribute: "bsHidden"
|
|
1735
1820
|
},
|
|
@@ -1739,9 +1824,11 @@ module.exports = {
|
|
|
1739
1824
|
register({ strapi }) {
|
|
1740
1825
|
const plugin = strapi.plugin(PLUGIN);
|
|
1741
1826
|
const name = hiddenName(plugin.config("hiddenAttribute"));
|
|
1827
|
+
withHidden = [];
|
|
1742
1828
|
if (name === null) strapi.log.warn(`[${PLUGIN}] "hiddenAttribute" config ignored: expected a name like "bsHidden" or false. Hiding blocks is off.`);
|
|
1743
1829
|
if (!name) return;
|
|
1744
|
-
const { skipped } = injectHidden(strapi.components, strapi.contentTypes, name);
|
|
1830
|
+
const { added, skipped } = injectHidden(strapi.components, strapi.contentTypes, name);
|
|
1831
|
+
withHidden = added;
|
|
1745
1832
|
if (skipped.length) strapi.log.warn(`[${PLUGIN}] "${name}" already exists with another type on ${skipped.join(", ")}; those blocks cannot be hidden.`);
|
|
1746
1833
|
},
|
|
1747
1834
|
async bootstrap({ strapi }) {
|
|
@@ -1761,6 +1848,7 @@ module.exports = {
|
|
|
1761
1848
|
strapi.plugin(PLUGIN).service("settings").variants();
|
|
1762
1849
|
registerPublishGuard(strapi);
|
|
1763
1850
|
registerHiddenStrip(strapi, hiddenName(strapi.plugin(PLUGIN).config("hiddenAttribute")), strapi.plugin(PLUGIN));
|
|
1851
|
+
if (withHidden.length) hideFromBuilder(typeof strapi.get === "function" ? strapi.get("services") : strapi.container.get("services"), withHidden, hiddenName(strapi.plugin(PLUGIN).config("hiddenAttribute")));
|
|
1764
1852
|
if (STRAPI5) {
|
|
1765
1853
|
registerHistory(strapi);
|
|
1766
1854
|
registerPurge(strapi);
|
|
@@ -1784,18 +1872,28 @@ module.exports = {
|
|
|
1784
1872
|
}
|
|
1785
1873
|
return project;
|
|
1786
1874
|
};
|
|
1875
|
+
let meta;
|
|
1876
|
+
const schemaMeta = () => {
|
|
1877
|
+
if (meta !== void 0) return meta;
|
|
1878
|
+
const notes = [];
|
|
1879
|
+
meta = schemaMetadata({ ...strapi.contentTypes, ...strapi.components }, strapi.plugin(PLUGIN).config("translations"), notes);
|
|
1880
|
+
if (notes.length) strapi.log?.warn(`[${PLUGIN}] schema metadata left out: ${notes.slice(0, 20).join("; ")}${notes.length > 20 ? `; and ${notes.length - 20} more` : ""}.`);
|
|
1881
|
+
return meta;
|
|
1882
|
+
};
|
|
1787
1883
|
let fields;
|
|
1788
1884
|
const fieldTexts = () => {
|
|
1789
1885
|
if (fields !== void 0) return fields;
|
|
1790
1886
|
const schemas = Object.fromEntries(Object.entries({ ...strapi.contentTypes, ...strapi.components }).map(([uid, schema]) => [uid, Object.keys(schema?.attributes || {})]));
|
|
1791
1887
|
const stale = [];
|
|
1888
|
+
let code = {};
|
|
1792
1889
|
try {
|
|
1793
|
-
|
|
1890
|
+
code = validateFields(strapi.plugin(PLUGIN).config("fields"), schemas, stale);
|
|
1794
1891
|
} catch (error) {
|
|
1795
|
-
fields
|
|
1796
|
-
strapi.log?.warn(`[${PLUGIN}] "fields" config ignored: ${error.message}. Field labels fall back to the Content Manager's own.`);
|
|
1892
|
+
strapi.log?.warn(`[${PLUGIN}] "fields" config ignored: ${error.message}. Field labels fall back to the schema metadata and the Content Manager's own.`);
|
|
1797
1893
|
}
|
|
1798
1894
|
if (stale.length) strapi.log?.warn(`[${PLUGIN}] "fields" entries for fields the schema no longer has were skipped: ${stale.slice(0, 20).join(", ")}${stale.length > 20 ? ` and ${stale.length - 20} more` : ""}.`);
|
|
1895
|
+
fields = structuredClone(schemaMeta().fields);
|
|
1896
|
+
for (const [uid, entries] of Object.entries(code)) for (const [name, entry] of Object.entries(entries)) (fields[uid] ||= {})[name] = { ...fields[uid][name], ...entry };
|
|
1799
1897
|
return fields;
|
|
1800
1898
|
};
|
|
1801
1899
|
let variants;
|
|
@@ -1811,6 +1909,7 @@ module.exports = {
|
|
|
1811
1909
|
projectDefaults,
|
|
1812
1910
|
get,
|
|
1813
1911
|
fields: fieldTexts,
|
|
1912
|
+
schema: schemaMeta,
|
|
1814
1913
|
variants: insertVariants,
|
|
1815
1914
|
async set(value) {
|
|
1816
1915
|
const next = validateSettings(value, componentUids(strapi), contentTypeUids(strapi), apiUids(strapi), strapi.components);
|
|
@@ -1840,7 +1939,8 @@ module.exports = {
|
|
|
1840
1939
|
blockPreview: plugin.config("blockPreview"),
|
|
1841
1940
|
groups: plugin.config("groups"),
|
|
1842
1941
|
componentUids: componentUids(strapi),
|
|
1843
|
-
schemas: strapi.components
|
|
1942
|
+
schemas: strapi.components,
|
|
1943
|
+
blocks: plugin.service("settings").schema().blocks
|
|
1844
1944
|
});
|
|
1845
1945
|
const settings = await plugin.service("settings").get();
|
|
1846
1946
|
const media = await resolveMedia(strapi, settings);
|
|
@@ -1874,7 +1974,7 @@ module.exports = {
|
|
|
1874
1974
|
settings: ({ strapi }) => ({
|
|
1875
1975
|
async find(ctx) {
|
|
1876
1976
|
const settings = await strapi.plugin(PLUGIN).service("settings").get();
|
|
1877
|
-
const auto = catalog({ components: strapi.plugin(PLUGIN).config("components"), schemas: strapi.components }).components;
|
|
1977
|
+
const auto = catalog({ components: strapi.plugin(PLUGIN).config("components"), schemas: strapi.components, blocks: strapi.plugin(PLUGIN).service("settings").schema().blocks }).components;
|
|
1878
1978
|
const components = Object.entries(strapi.components || {}).map(([uid, schema]) => ({
|
|
1879
1979
|
uid,
|
|
1880
1980
|
displayName: schema.info?.displayName || uid,
|
package/package.json
CHANGED