img popup base
All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 26s

This commit is contained in:
Julie Blanc 2026-03-23 11:13:50 +01:00
parent 87c08bc529
commit 75e7f375b3
20 changed files with 532 additions and 23 deletions

4
package-lock.json generated
View file

@ -1833,7 +1833,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -2012,7 +2011,6 @@
"integrity": "sha512-+VUy01yfDqNmIVMd/LLKl2TTtY0ovZN0rTonh+FhKr65mFwIYgU9WzgIZKS7U9/SPCQvWTsTGx9jyt+qRm/XFw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@bufbuild/protobuf": "^2.5.0",
"buffer-builder": "^0.2.0",
@ -2491,7 +2489,6 @@
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@ -2566,7 +2563,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz",
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.25",
"@vue/compiler-sfc": "3.5.25",

View file

@ -192,6 +192,29 @@
font-family: sans-serif;
}
.block-image.element-hovered {
outline: 2px solid #0d996050 !important;
cursor: pointer !important;
}
.block-image.element-selected {
outline: 2px dashed #0d9960 !important;
}
.image-hover-label {
position: absolute;
background: #0d9960;
color: white;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 600;
opacity: 0.3;
pointer-events: none;
z-index: 9999;
font-family: sans-serif;
}
/* Marks (to delete when merge in paged.js) */
.pagedjs_marks-crop {

View file

@ -103,3 +103,17 @@
--color-800: #5d28e6;
--color-900: #5223d6;
}
[data-color-type="image"]{
--color-050: #f0faf5;
--color-100: #d9f2e6;
--color-200: #b3e5cd;
--color-300: #7dd4b0;
--color-400: #43c08e;
--color-500: #1aad74;
--color-600: #0d9960;
--color-700: #0a8452;
--color-800: #087043;
--color-900: #065c35;
}

View file

@ -30,7 +30,7 @@
}
.toggle-setting:checked + label{
background: var(--color-purple);
background: var(--color-700);
&::after {
transform: translateX(10px);

View file

@ -189,6 +189,19 @@
--color-900: #5223d6;
}
[data-color-type=image] {
--color-050: #f0faf5;
--color-100: #d9f2e6;
--color-200: #b3e5cd;
--color-300: #7dd4b0;
--color-400: #43c08e;
--color-500: #1aad74;
--color-600: #0d9960;
--color-700: #0a8452;
--color-800: #087043;
--color-900: #065c35;
}
body,
html {
padding: 0;
@ -647,7 +660,7 @@ input[type=number] {
}
.toggle-setting:checked + label {
background: var(--color-purple);
background: var(--color-700);
}
.toggle-setting:checked + label::after {
transform: translateX(10px);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2.9918 21C2.44405 21 2 20.5551 2 20.0066V3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918ZM20 15V5H4V19L14 9L20 15ZM20 17.8284L14 11.8284L6.82843 19H20V17.8284ZM8 11C6.89543 11 6 10.1046 6 9C6 7.89543 6.89543 7 8 7C9.10457 7 10 7.89543 10 9C10 10.1046 9.10457 11 8 11Z"/></svg>

After

Width:  |  Height:  |  Size: 441 B

View file

@ -2,6 +2,7 @@
import PagedJsWrapper from './components/PagedJsWrapper.vue';
import EditorPanel from './components/editor/EditorPanel.vue';
import ElementPopup from './components/ElementPopup.vue';
import ImagePopup from './components/ImagePopup.vue';
// import PagePopup from './components/PagePopup.vue'; // DISABLED: page template styling feature
import PreviewLoader from './components/PreviewLoader.vue';
import SaveButton from './components/SaveButton.vue';
@ -23,6 +24,7 @@ const { loadFontsFromCss } = useProjectFonts();
const previewFrame1 = ref(null);
const previewFrame2 = ref(null);
const elementPopup = ref(null);
const imagePopup = ref(null);
// const pagePopup = ref(null); // DISABLED: page template styling feature
const activeTab = ref('');
@ -38,7 +40,8 @@ const {
handleIframeClick,
// handlePagePopupClose, // DISABLED: page template styling feature
handleElementPopupClose,
} = useIframeInteractions({ elementPopup });
handleImagePopupClose,
} = useIframeInteractions({ elementPopup, imagePopup });
// Setup preview renderer with double buffering
const {
@ -126,6 +129,11 @@ onMounted(async () => {
:iframeRef="activeFrame"
@close="handleElementPopupClose"
/>
<ImagePopup
ref="imagePopup"
@close="handleImagePopupClose"
/>
<!-- DISABLED: page template styling feature
<PagePopup
ref="pagePopup"

View file

@ -2,6 +2,7 @@
<BasePopup
ref="basePopup"
id="element-popup"
color-type="text"
:display-css="displayedCss"
:editable-css="editableFullCss"
:popup-width="800"

View file

@ -0,0 +1,255 @@
<template>
<BasePopup
ref="basePopup"
id="image-popup"
color-type="image"
:display-css="displayedCss"
:editable-css="editableCss"
:popup-width="440"
:popup-height="200"
:show-inheritance="false"
@close="handleClose"
@css-input="handleCssInput"
>
<template #header-left>
<span class="image-label">{{ selector || '' }}</span>
</template>
<template #controls>
<!-- Largeur -->
<div
class="setting__section"
data-setting="width"
:class="{ 'setting-disabled': !widthEnabled }"
@click="onSectionClick"
>
<div class="setting__header">
<input
type="checkbox"
id="toggle-width"
class="toggle-setting"
:checked="widthEnabled"
@change="onToggleWidth($event.target.checked)"
/><label for="toggle-width" aria-label="Activer le réglage de largeur" @click.stop></label>
<label class="label-with-tooltip" data-css="width">Largeur</label>
</div>
<div class="setting__body">
<InputWithUnit
v-model="widthModel"
:units="['%', 'px']"
:min="1"
:max="width.unit === 'px' ? 2000 : 200"
showRange
/>
</div>
<p class="info-default">Valeur par défaut : {{ imageDefaults.width.value }}{{ imageDefaults.width.unit }}</p>
</div>
</template>
</BasePopup>
</template>
<script setup>
import { ref, reactive, computed, watch, nextTick } from 'vue';
import InputWithUnit from './ui/InputWithUnit.vue';
import BasePopup from './ui/BasePopup.vue';
import { useStylesheetStore } from '../stores/stylesheet';
import { useImageDefaults } from '../composables/useImageDefaults';
const emit = defineEmits(['close']);
const stylesheetStore = useStylesheetStore();
const imageDefaults = useImageDefaults();
const basePopup = ref(null);
const visible = computed(() => basePopup.value?.visible ?? false);
const selector = ref('');
const width = reactive({ value: 100, unit: '%' });
const widthEnabled = ref(false);
const widthCache = ref(null); // { value, unit } | null persists user value when toggle is OFF
// Per-selector persistent state
const elementStates = new Map();
let isUpdatingFromStore = false;
// InputWithUnit model
const widthModel = computed({
get: () => ({ value: width.value, unit: width.unit }),
set: (v) => { width.value = v.value; width.unit = v.unit; },
});
// Watch width changes update CSS when toggle is ON
watch(() => [width.value, width.unit], () => {
if (isUpdatingFromStore || !widthEnabled.value || !selector.value) return;
stylesheetStore.updateProperty(selector.value, 'width', width.value, width.unit);
saveState();
});
// Sync greyed field with imageDefaults when toggle is OFF and no cache
watch(() => imageDefaults.width, (val) => {
if (!widthEnabled.value && widthCache.value === null) {
isUpdatingFromStore = true;
width.value = val.value;
width.unit = val.unit;
nextTick(() => { isUpdatingFromStore = false; });
}
}, { deep: true });
// --- Toggle ---
const onSectionClick = () => {
if (!widthEnabled.value) onToggleWidth(true);
};
const onToggleWidth = (enabled) => {
isUpdatingFromStore = true;
widthEnabled.value = enabled;
if (enabled) {
if (widthCache.value) {
width.value = widthCache.value.value;
width.unit = widthCache.value.unit;
widthCache.value = null;
}
stylesheetStore.updateProperty(selector.value, 'width', width.value, width.unit);
} else {
widthCache.value = { value: width.value, unit: width.unit };
removeWidthProp();
width.value = imageDefaults.width.value;
width.unit = imageDefaults.width.unit;
}
saveState();
nextTick(() => { isUpdatingFromStore = false; });
};
// --- CSS helpers ---
const removeWidthProp = () => {
if (!selector.value) return;
const block = stylesheetStore.extractBlock(selector.value);
if (!block || !stylesheetStore.customCss.includes(block)) return;
const newBlock = block.replace(/[ \t]*width\s*:[^;]*;[ \t]*\n?/g, '');
const inner = newBlock.replace(/^[^{]*\{/, '').replace(/\}[^}]*$/, '');
if (!inner.trim()) {
stylesheetStore.replaceInCustomCss(block, '');
} else {
stylesheetStore.replaceBlock(block, newBlock);
}
};
// --- Displayed CSS ---
const displayedCss = computed(() => {
if (!selector.value) return '';
const widthVal = `${width.value}${width.unit}`;
const defaultVal = `${imageDefaults.width.value}${imageDefaults.width.unit}`;
const comment = (!widthEnabled.value && widthVal === defaultVal) ? ' /* hérité de .block-image */' : '';
return `${selector.value} {\n width: ${widthVal};${comment}\n}`;
});
const editableCss = computed(() => {
return displayedCss.value.replace(/ \/\* hérité de \.block-image \*\//g, '');
});
// --- CSS input (edit mode) ---
const handleCssInput = (newCss) => {
isUpdatingFromStore = true;
const match = newCss.match(/width\s*:\s*([\d.]+)(%|px)/i);
if (match) {
width.value = parseFloat(match[1]);
width.unit = match[2];
}
const newBlock = `${selector.value} {\n width: ${width.value}${width.unit};\n}\n`;
const oldBlock = stylesheetStore.extractBlock(selector.value) || '';
if (oldBlock) {
stylesheetStore.replaceInCustomCss(oldBlock, newBlock);
} else {
stylesheetStore.setCustomCss((stylesheetStore.customCss || '') + '\n' + newBlock);
}
nextTick(() => { isUpdatingFromStore = false; });
};
// --- State persistence ---
const saveState = () => {
if (!selector.value) return;
elementStates.set(selector.value, {
toggle: widthEnabled.value,
cache: widthCache.value ? { ...widthCache.value } : null,
value: { value: width.value, unit: width.unit },
});
};
// --- Open ---
const handleImageClick = (figure, event) => {
const uniqueClass = Array.from(figure.classList).find(cls => cls.startsWith('block-image--'));
if (!uniqueClass) return;
isUpdatingFromStore = true;
selector.value = `.${uniqueClass}`;
const stored = elementStates.get(selector.value);
if (stored) {
widthEnabled.value = stored.toggle;
widthCache.value = stored.cache ? { ...stored.cache } : null;
if (stored.toggle) {
width.value = stored.value.value;
width.unit = stored.value.unit;
} else if (stored.cache) {
width.value = stored.cache.value;
width.unit = stored.cache.unit;
} else {
width.value = imageDefaults.width.value;
width.unit = imageDefaults.width.unit;
}
} else {
widthEnabled.value = false;
widthCache.value = null;
const cssVal = stylesheetStore.extractValue(selector.value, 'width');
if (cssVal && cssVal.value !== undefined) {
width.value = cssVal.value;
width.unit = cssVal.unit;
widthEnabled.value = true;
} else {
width.value = imageDefaults.width.value;
width.unit = imageDefaults.width.unit;
}
}
basePopup.value.open(event);
nextTick(() => { isUpdatingFromStore = false; });
};
// --- Close ---
const handleClose = () => {
saveState();
selector.value = '';
if (basePopup.value?.visible) basePopup.value.close();
emit('close');
};
defineExpose({ handleImageClick, close: () => basePopup.value?.close(), visible });
</script>
<style scoped>
.image-label {
background: var(--color-600);
color: white;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 600;
}
</style>

View file

@ -55,6 +55,7 @@
:key="block.id"
:is="getBlockComponent(block.type)"
:content="block.content"
v-bind="block.type === 'image' ? { blockId: block.id } : {}"
/>
</template>
</section>
@ -97,6 +98,7 @@
:key="block.id"
:is="getBlockComponent(block.type)"
:content="block.content"
v-bind="block.type === 'image' ? { blockId: block.id } : {}"
/>
</template>
</div>

View file

@ -1,6 +1,6 @@
<template>
<figure
class="block-image"
:class="['block-image', blockId ? `block-image--${blockId.slice(0, 8)}` : '']"
:style="figureStyle"
>
<img
@ -19,16 +19,16 @@ const props = defineProps({
content: {
type: Object,
required: true
},
blockId: {
type: String,
default: ''
}
});
const figureStyle = computed(() => {
const styles = {};
if (props.content.width && props.content.width !== '100%') {
styles.width = props.content.width;
}
if (props.content.position === 'center') {
styles.margin = '0 auto';
} else if (props.content.position === 'right') {

View file

@ -53,6 +53,7 @@
<div v-if="activeTab === 'document'" class="tab-panel">
<PageSettings />
<TextSettings />
<ImageSettings />
</div>
<div v-else-if="activeTab === 'code'" class="tab-panel">
@ -70,6 +71,7 @@
import { inject } from 'vue';
import PageSettings from './PageSettings.vue';
import TextSettings from './TextSettings.vue';
import ImageSettings from './ImageSettings.vue';
import StylesheetViewer from '../StylesheetViewer.vue';
const activeTab = inject('activeTab');

View file

@ -0,0 +1,79 @@
<template>
<section class="panel-settings__container" id="settings__container_image" data-color-type="image">
<div class="settings__header">
<div class="icon" v-html="imageIcon"></div>
<h2 class="title">Réglage des images par défaut</h2>
</div>
<div class="container">
<!-- Largeur -->
<div class="setting__section" data-setting="width">
<div class="setting__header">
<label class="label-with-tooltip" data-css="width">Largeur</label>
</div>
<div class="setting__body">
<InputWithUnit
v-model="width"
:units="['%', 'px']"
:min="1"
:max="width.unit === 'px' ? 2000 : 200"
showRange
/>
</div>
</div>
</div>
</section>
</template>
<script setup>
import { ref, watch, onMounted, nextTick } from 'vue';
import imageIcon from '/assets/svg/image.svg?raw';
import { IMAGE_DEFAULTS } from '../../utils/defaults';
import InputWithUnit from '../ui/InputWithUnit.vue';
import { useCssUpdater } from '../../composables/useCssUpdater';
import { useCssSync } from '../../composables/useCssSync';
import { useDebounce } from '../../composables/useDebounce';
import { useImageDefaults } from '../../composables/useImageDefaults';
const { updateStyle } = useCssUpdater();
const { extractNumericValue } = useCssSync();
const { debouncedUpdate } = useDebounce(500);
const imageDefaults = useImageDefaults();
// State initial value from defaults.js (overwritten by syncFromStore)
const width = ref({ ...IMAGE_DEFAULTS.width });
// Start true to block immediate watchers during setup
let isUpdatingFromStore = true;
watch(width, (val) => {
if (isUpdatingFromStore) return;
imageDefaults.width = { value: val.value, unit: val.unit };
debouncedUpdate(() => {
updateStyle('.block-image', 'width', `${val.value}${val.unit}`);
});
}, { deep: true, immediate: true });
const syncFromStore = () => {
isUpdatingFromStore = true;
if (imageDefaults._initialized) {
width.value = { value: imageDefaults.width.value, unit: imageDefaults.width.unit };
} else {
const widthVal = extractNumericValue('.block-image', 'width', ['%', 'px']);
if (widthVal) width.value = widthVal;
imageDefaults.width = { value: width.value.value, unit: width.value.unit };
imageDefaults._initialized = true;
}
nextTick(() => { isUpdatingFromStore = false; });
};
onMounted(() => {
syncFromStore();
});
</script>

View file

@ -3,6 +3,7 @@
v-if="visible"
class="settings-popup"
:style="{ top: position.y + 'px', left: position.x + 'px' }"
:data-color-type="colorType || undefined"
>
<div class="popup-header" @mousedown="startDrag">
<div class="header-left">
@ -100,6 +101,7 @@ const props = defineProps({
popupWidth: { type: Number, default: 800 },
popupHeight: { type: Number, default: 600 },
showInheritance: { type: Boolean, default: true },
colorType: { type: String, default: '' },
});
const emit = defineEmits(['close', 'css-input', 'toggle-inheritance']);

View file

@ -4,12 +4,14 @@ import { ref } from 'vue';
* Composable for managing interactions with pages and elements in the iframe
* Handles hover effects, labels, and click events for both pages and content elements
*/
export function useIframeInteractions({ elementPopup /*, pagePopup // DISABLED: page template styling feature */ }) {
export function useIframeInteractions({ elementPopup, imagePopup /*, pagePopup // DISABLED: page template styling feature */ }) {
// DISABLED: page template styling feature
// const hoveredPage = ref(null);
// const selectedPages = ref([]); // Pages with active border (when popup is open)
const hoveredElement = ref(null); // Currently hovered content element
const hoveredFigure = ref(null); // Currently hovered block-image figure
const selectedElement = ref(null); // Selected element (when popup is open)
const selectedFigure = ref(null); // Selected figure (when image popup is open)
// const EDGE_THRESHOLD = 30; // px from edge to trigger hover // DISABLED: page template styling feature
// Text elements that can trigger ElementPopup (excluding containers, images, etc.)
@ -119,6 +121,34 @@ export function useIframeInteractions({ elementPopup /*, pagePopup // DISABLED:
}
};
// Create and position image label on hover
const createImageLabel = (figure) => {
const doc = figure.ownerDocument;
const existing = doc.querySelector('.image-hover-label');
if (existing) existing.remove();
const uniqueClass = Array.from(figure.classList).find(cls => cls.startsWith('block-image--'));
if (!uniqueClass) return;
const rect = figure.getBoundingClientRect();
const scrollTop = doc.documentElement.scrollTop || doc.body.scrollTop;
const scrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft;
const label = doc.createElement('div');
label.className = 'image-hover-label';
label.textContent = `.${uniqueClass}`;
label.style.top = `${rect.top + scrollTop - 32}px`;
label.style.left = `${rect.left + scrollLeft}px`;
doc.body.appendChild(label);
};
// Remove image label
const removeImageLabel = (doc) => {
const label = doc.querySelector('.image-hover-label');
if (label) label.remove();
};
/* DISABLED: page template styling feature
// Create and position page label on hover
const createPageLabel = (page) => {
@ -153,6 +183,18 @@ export function useIframeInteractions({ elementPopup /*, pagePopup // DISABLED:
};
*/
// Find closest block-image figure ancestor (including self)
const getImageFigure = (element) => {
let current = element;
while (current && current.tagName !== 'BODY') {
if (current.tagName === 'FIGURE' && current.classList.contains('block-image')) {
return current;
}
current = current.parentElement;
}
return null;
};
// Check if element is a content element (or find closest content parent)
const getContentElement = (element) => {
let current = element;
@ -226,9 +268,23 @@ export function useIframeInteractions({ elementPopup /*, pagePopup // DISABLED:
}
*/
// Check for content element hover
const contentElement = getContentElement(event.target);
// Check for block-image figure hover
const imageFigure = getImageFigure(event.target);
const doc = event.target.ownerDocument;
if (imageFigure !== hoveredFigure.value) {
if (hoveredFigure.value && hoveredFigure.value !== selectedFigure.value) {
hoveredFigure.value.classList.remove('element-hovered');
}
removeImageLabel(doc);
if (imageFigure && imageFigure !== selectedFigure.value) {
imageFigure.classList.add('element-hovered');
createImageLabel(imageFigure);
}
hoveredFigure.value = imageFigure;
}
// Check for content element hover
const contentElement = imageFigure ? null : getContentElement(event.target);
if (contentElement !== hoveredElement.value) {
// Remove highlight from previous element (only if not selected)
@ -287,15 +343,45 @@ export function useIframeInteractions({ elementPopup /*, pagePopup // DISABLED:
const isInsidePage = element.closest('.pagedjs_page');
if (!isInsidePage) {
clearSelectedElement();
elementPopup.value.close();
if (elementPopup.value.visible) elementPopup.value.close();
imagePopup.value?.close();
return;
}
// Check if clicking a block-image figure
const imageFigure = getImageFigure(element);
if (imageFigure) {
clearSelectedElement();
if (elementPopup.value.visible) elementPopup.value.close();
const doc = event.target.ownerDocument;
removeElementLabel(doc);
removeImageLabel(doc);
if (imagePopup.value?.visible) {
// Deselect current figure
if (selectedFigure.value) {
selectedFigure.value.classList.remove('element-selected');
selectedFigure.value = null;
}
imagePopup.value.close();
return;
}
// Select the figure
imageFigure.classList.remove('element-hovered');
imageFigure.classList.add('element-selected');
hoveredFigure.value = null;
selectedFigure.value = imageFigure;
imagePopup.value?.handleImageClick(imageFigure, event);
return;
}
// Close image popup if open when clicking a text element
imagePopup.value?.close();
// Only show ElementPopup for content elements, not divs
const contentElement = getContentElement(element);
if (!contentElement) {
clearSelectedElement();
elementPopup.value.close();
if (elementPopup.value.visible) elementPopup.value.close();
return;
}
@ -342,6 +428,13 @@ export function useIframeInteractions({ elementPopup /*, pagePopup // DISABLED:
clearSelectedElement();
};
const handleImagePopupClose = () => {
if (selectedFigure.value) {
selectedFigure.value.classList.remove('element-selected');
selectedFigure.value = null;
}
};
return {
// State
// hoveredPage, // DISABLED: page template styling feature
@ -353,6 +446,7 @@ export function useIframeInteractions({ elementPopup /*, pagePopup // DISABLED:
handleIframeClick,
// handlePagePopupClose, // DISABLED: page template styling feature
handleElementPopupClose,
handleImagePopupClose,
// Utilities
// clearSelectedPages, // DISABLED: page template styling feature
clearSelectedElement,

View file

@ -0,0 +1,12 @@
import { reactive } from 'vue';
import { IMAGE_DEFAULTS } from '../utils/defaults';
// Singleton reactive — ImageSettings writes here, ImagePopup reads when toggle is disabled
const defaults = reactive({
width: { ...IMAGE_DEFAULTS.width },
_initialized: false,
});
export function useImageDefaults() {
return defaults;
}

View file

@ -39,12 +39,12 @@ export function usePopupPosition(popupWidth, popupHeight) {
else {
x = cursorX - OFFSET - popupWidth;
y = cursorY - OFFSET - popupHeight;
// Ensure it doesn't go off-screen
x = Math.max(10, x);
y = Math.max(10, y);
}
// Final clamp — ensure popup is always fully visible
x = Math.max(10, Math.min(viewportWidth - popupWidth - 10, x));
y = Math.max(10, Math.min(viewportHeight - popupHeight - 10, y));
return { x, y };
};

View file

@ -5,7 +5,7 @@ import * as cssComments from '../utils/css-comments';
import prettier from 'prettier/standalone';
import parserPostcss from 'prettier/plugins/postcss';
import { getCsrfToken } from '../utils/kirby-auth';
import { PAGE_DEFAULTS, TEXT_DEFAULTS, HEADING_DEFAULTS, INLINE_DEFAULTS, PARAGRAPH_CLASS_DEFAULTS } from '../utils/defaults';
import { PAGE_DEFAULTS, TEXT_DEFAULTS, IMAGE_DEFAULTS, HEADING_DEFAULTS, INLINE_DEFAULTS, PARAGRAPH_CLASS_DEFAULTS } from '../utils/defaults';
export const useStylesheetStore = defineStore('stylesheet', () => {
// Base state
@ -259,6 +259,9 @@ export const useStylesheetStore = defineStore('stylesheet', () => {
}
}
// Image defaults
set('.block-image', 'width', IMAGE_DEFAULTS.width.value, IMAGE_DEFAULTS.width.unit);
// Inline element defaults (em, i, strong, b, a)
for (const [tag, props] of Object.entries(INLINE_DEFAULTS)) {
if (props.fontStyle) set(tag, 'font-style', props.fontStyle);

View file

@ -126,6 +126,10 @@ export const PARAGRAPH_CLASS_DEFAULTS = Object.freeze({
}),
});
export const IMAGE_DEFAULTS = Object.freeze({
width: Object.freeze({ value: 100, unit: '%' }),
});
export const INLINE_DEFAULTS = Object.freeze({
em: Object.freeze({ fontStyle: 'italic' }),
i: Object.freeze({ fontStyle: 'italic' }),