cinqcinqdev-seo 1.0.13 → 1.0.15

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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0"
6
6
  },
7
- "version": "1.0.13",
7
+ "version": "1.0.15",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "unknown"
@@ -43,7 +43,7 @@ const resolveSection = (type) => {
43
43
  <div v-if="selectedId === index" class="absolute top-0 left-0 bg-blue-500 text-white text-[10px] px-2 py-1 z-20 font-bold uppercase">
44
44
  {{ block.type }}
45
45
  </div>
46
- <component :is="resolveSection(block.type)" v-bind="block.props" />
46
+ <component :is="resolveSection(block.type)" v-bind="block.props" data-section-root />
47
47
  </div>
48
48
  </template>
49
49
 
@@ -1264,6 +1264,7 @@ const _buildStyleCss = (s, id) => {
1264
1264
  if (s.buttonBg) btn.push(`background-color:${s.buttonBg} !important`);
1265
1265
  if (s.buttonColor) btn.push(`color:${s.buttonColor} !important`);
1266
1266
  if (btn.length) parts.push(`${btnSel}{${btn.join(";")}}`);
1267
+ const srSel = `${id} [data-section-root]`;
1267
1268
  const bl = [];
1268
1269
  if (s.backgroundColor) bl.push(`background-color:${s.backgroundColor} !important`);
1269
1270
  if (s.gradientFrom || s.gradientTo) {
@@ -1285,7 +1286,7 @@ const _buildStyleCss = (s, id) => {
1285
1286
  if (s.borderColor) bl.push(`border-color:${s.borderColor}`);
1286
1287
  if (s.borderStyle && s.borderStyle !== "none") bl.push(`border-style:${s.borderStyle}`);
1287
1288
  if (s.borderRadius) bl.push(`border-radius:${s.borderRadius}px`);
1288
- if (bl.length) parts.push(`${id}{${bl.join(";")}}`);
1289
+ if (bl.length) parts.push(`${srSel}{${bl.join(";")}}`);
1289
1290
  return parts.join("");
1290
1291
  };
1291
1292
  export const computeBlockCss = (props, blockId) => {
@@ -502,13 +502,34 @@ const seoTree = computed(() => {
502
502
  const selectedCMSConfig = computed(
503
503
  () => selectedBlockIndex.value !== null ? CMS_COMPONENTS[page.value?.content[selectedBlockIndex.value]?.type] : null
504
504
  );
505
+ const _getPanelFields = (panel) => {
506
+ const cfg = selectedCMSConfig.value;
507
+ if (!cfg?.fields || selectedBlockIndex.value === null) return [];
508
+ const props = page.value?.content[selectedBlockIndex.value]?.props ?? {};
509
+ return Object.entries(cfg.fields).filter(([key]) => STYLE_PANEL_MAP[key] === panel).filter(([, f]) => !f.showIf || f.showIf(props)).map(([key, f]) => ({ key, ...f }));
510
+ };
511
+ const colorSectionFields = computed(() => _getPanelFields("colors"));
512
+ const typoSectionFields = computed(() => _getPanelFields("typo"));
513
+ const effectsSectionFields = computed(() => _getPanelFields("effects"));
514
+ const STYLE_PANEL_MAP = {
515
+ bgColor: "colors",
516
+ textColor: "colors",
517
+ overlayColor: "colors",
518
+ highlightColor: "colors",
519
+ textSize: "typo",
520
+ titleSize: "typo",
521
+ overlay: "effects",
522
+ overlayDirection: "effects",
523
+ overlayStrength: "effects"
524
+ };
505
525
  const CONTENT_FIELD_TYPES = ["text", "textarea", "image", "list", "richtext"];
506
526
  const visibleFields = computed(() => {
507
527
  const cfg = selectedCMSConfig.value;
508
528
  if (!cfg?.fields) return {};
509
529
  const props = page.value?.content[selectedBlockIndex.value]?.props ?? {};
510
530
  return Object.fromEntries(
511
- Object.entries(cfg.fields).filter(([, f]) => {
531
+ Object.entries(cfg.fields).filter(([key, f]) => {
532
+ if (STYLE_PANEL_MAP[key]) return false;
512
533
  const isContent = CONTENT_FIELD_TYPES.includes(f.type);
513
534
  if (selectedFieldGroup.value === "contenu" ? !isContent : isContent) return false;
514
535
  if (typeof f.showIf === "function" && !f.showIf(props)) return false;
@@ -1168,6 +1189,30 @@ const savePage = async () => {
1168
1189
  </div>
1169
1190
  </div>
1170
1191
  </div>
1192
+ <!-- Section color props (bgColor, textColor, overlayColor…) from block schema -->
1193
+ <template v-if="colorSectionFields.length">
1194
+ <div class="border-t border-gray-100 pt-3 space-y-3">
1195
+ <p class="text-[8px] font-black uppercase tracking-widest text-[#3d35ff]/60">Section Colors</p>
1196
+ <div v-for="f in colorSectionFields" :key="f.key">
1197
+ <label class="text-[7px] font-black uppercase text-gray-400 block mb-1">{{ f.label }}</label>
1198
+ <div class="flex items-center gap-1.5 bg-gray-50 rounded-xl ring-1 ring-gray-100 px-2 py-1.5">
1199
+ <input type="color" :value="page.content[selectedBlockIndex].props[f.key] || '#000000'" @input="page.content[selectedBlockIndex].props[f.key] = $event.target.value;
1200
+ trigger()" class="w-5 h-5 rounded cursor-pointer border-0 bg-transparent p-0" />
1201
+ <input :value="page.content[selectedBlockIndex].props[f.key]" @input="page.content[selectedBlockIndex].props[f.key] = $event.target.value;
1202
+ trigger()" placeholder="inherit" class="flex-1 bg-transparent text-[9px] font-mono outline-none w-0" />
1203
+ <button v-if="page.content[selectedBlockIndex].props[f.key]" @click="page.content[selectedBlockIndex].props[f.key] = '';
1204
+ trigger()" class="text-gray-300 hover:text-gray-500 text-[10px] shrink-0">✕</button>
1205
+ </div>
1206
+ <div class="flex flex-wrap gap-1 mt-1.5">
1207
+ <button v-for="c in ['#ffffff', '#000000', '#3d35ff', ...brandColors]" :key="c"
1208
+ @click="page.content[selectedBlockIndex].props[f.key] = c;
1209
+ trigger()"
1210
+ :title="c" :style="{ background: c }"
1211
+ :class="['w-4 h-4 rounded-full border transition-all', page.content[selectedBlockIndex].props[f.key] === c ? 'border-[#3d35ff] scale-110 shadow-sm' : 'border-black/10 hover:scale-110']" />
1212
+ </div>
1213
+ </div>
1214
+ </div>
1215
+ </template>
1171
1216
  </div>
1172
1217
  </div>
1173
1218
 
@@ -1179,6 +1224,22 @@ const savePage = async () => {
1179
1224
  <svg :class="['w-3 h-3 text-gray-400 transition-transform', styleSection === 'typo' ? 'rotate-180' : '']" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M19 9l-7 7-7-7"/></svg>
1180
1225
  </button>
1181
1226
  <div v-show="styleSection === 'typo'" class="p-4 space-y-4">
1227
+ <!-- Section typography props (textSize, titleSize) from block schema -->
1228
+ <template v-if="typoSectionFields.length">
1229
+ <div v-for="f in typoSectionFields" :key="f.key">
1230
+ <p class="text-[8px] font-black uppercase tracking-widest text-[#3d35ff]/60 mb-2">{{ f.label }}</p>
1231
+ <div class="grid grid-cols-3 gap-1.5">
1232
+ <button v-for="opt in f.options" :key="opt.value"
1233
+ @click="page.content[selectedBlockIndex].props[f.key] = opt.value;
1234
+ trigger()"
1235
+ :class="['py-2 px-1 rounded-xl border-2 text-center transition-all', page.content[selectedBlockIndex].props[f.key] === opt.value ? 'border-[#3d35ff] bg-blue-50/50 text-[#3d35ff]' : 'border-gray-100 text-gray-400 hover:border-gray-200']">
1236
+ <span class="text-base block mb-0.5">{{ opt.icon }}</span>
1237
+ <span class="text-[7px] font-black uppercase">{{ opt.label }}</span>
1238
+ </button>
1239
+ </div>
1240
+ </div>
1241
+ <div class="border-t border-gray-100 pt-1"></div>
1242
+ </template>
1182
1243
  <!-- Title row -->
1183
1244
  <div>
1184
1245
  <p class="text-[8px] font-black uppercase tracking-widest text-[#3d35ff]/60 mb-2">Title (h1–h4)</p>
@@ -1406,7 +1467,31 @@ const savePage = async () => {
1406
1467
  </div>
1407
1468
 
1408
1469
  <!-- Empty state -->
1409
- <p v-if="!activeEffects.length" class="text-[8px] text-gray-300 text-center py-3">No effects applied</p>
1470
+ <p v-if="!activeEffects.length && !effectsSectionFields.length" class="text-[8px] text-gray-300 text-center py-3">No effects applied</p>
1471
+
1472
+ <!-- Section effects (overlay, direction, strength) from block schema -->
1473
+ <template v-if="effectsSectionFields.length">
1474
+ <div v-for="f in effectsSectionFields" :key="f.key" class="rounded-xl border border-gray-100 overflow-hidden">
1475
+ <div class="px-3 py-2 bg-gray-50">
1476
+ <p class="text-[8px] font-black uppercase tracking-widest text-gray-700">{{ f.label }}</p>
1477
+ </div>
1478
+ <div class="p-3">
1479
+ <div v-if="f.type === 'cards'" class="grid grid-cols-3 gap-1.5">
1480
+ <button v-for="opt in f.options" :key="opt.value"
1481
+ @click="page.content[selectedBlockIndex].props[f.key] = opt.value;
1482
+ trigger()"
1483
+ :class="['py-2 px-1 rounded-xl border-2 text-center transition-all', page.content[selectedBlockIndex].props[f.key] === opt.value ? 'border-[#3d35ff] bg-blue-50/50 text-[#3d35ff]' : 'border-gray-100 text-gray-400 hover:border-gray-200']">
1484
+ <span class="text-sm block mb-0.5">{{ opt.icon }}</span>
1485
+ <span class="text-[7px] font-black uppercase">{{ opt.label }}</span>
1486
+ </button>
1487
+ </div>
1488
+ <select v-else-if="f.type === 'select'" :value="page.content[selectedBlockIndex].props[f.key]" @change="page.content[selectedBlockIndex].props[f.key] = $event.target.value;
1489
+ trigger()" class="w-full bg-gray-50 rounded-xl p-2 text-[9px] font-bold ring-1 ring-gray-100 outline-none">
1490
+ <option v-for="opt in f.options" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
1491
+ </select>
1492
+ </div>
1493
+ </div>
1494
+ </template>
1410
1495
 
1411
1496
  <!-- Add effect -->
1412
1497
  <div v-if="showEffectDropdown" class="fixed inset-0 z-[5]" @click="showEffectDropdown = false" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cinqcinqdev-seo",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "A reusable Nuxt 3 admin CMS module with visual page editor powered by Supabase",
5
5
  "license": "MIT",
6
6
  "module": "./dist/module.mjs",