Compare commits
7 commits
ea755a2dc6
...
d88758b226
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d88758b226 | ||
|
|
76274fff04 | ||
|
|
d9f3ede661 | ||
|
|
668d950518 | ||
|
|
681517db21 | ||
|
|
35c9ab1d3b | ||
|
|
ceaf318272 |
17 changed files with 320 additions and 96 deletions
|
|
@ -1,10 +1,8 @@
|
|||
.settings-section {
|
||||
|
||||
margin-top: 3em;
|
||||
margin: var(--space-m) 0;
|
||||
|
||||
|
||||
// .cons
|
||||
|
||||
|
||||
|
||||
h2 {
|
||||
|
|
@ -12,21 +10,10 @@
|
|||
|
||||
font-weight: 600;
|
||||
font-size: 1.4rem;
|
||||
// border-radius: var(--border-radius);
|
||||
// height: var(--input-h);
|
||||
// padding: 0 1ch;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
|
||||
// color: var(--color-interface-050);
|
||||
// background-color: var(--color-800);
|
||||
// display: inline-block;
|
||||
|
||||
border-bottom: 1px solid var(--color-200);
|
||||
color: var(--color-800);
|
||||
|
||||
// border-bottom: 2px solid currentColor;
|
||||
// margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.infos{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ input[type="number"] {
|
|||
border: 1px solid var(--color-interface-200);
|
||||
background-color: var(--color-interface-100);
|
||||
font-family: var(--sans-serif);
|
||||
color: var(--color-txt);
|
||||
font-size: 1rem;
|
||||
padding-left: 0.5ch;
|
||||
// min-width: var(--input-w);
|
||||
// width: 100%;
|
||||
// padding: 0 1ch;
|
||||
|
|
@ -85,10 +87,22 @@ input[type="number"] {
|
|||
label{
|
||||
font-weight: 400;
|
||||
margin-left: 0.75ch;
|
||||
color: var(--color-txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field-text-size{
|
||||
input[type="number"]{
|
||||
width: var(--input-w-small);
|
||||
padding-left: 0.75ch;
|
||||
}
|
||||
input[type="range"]{
|
||||
flex-grow: 2;
|
||||
flex-shrink: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.field-margin, .field-size{
|
||||
display: inline-grid;
|
||||
width: calc(50% - 1ch);
|
||||
|
|
@ -196,9 +210,13 @@ input[type="number"] {
|
|||
top: 0;
|
||||
|
||||
button{
|
||||
height: calc(var(--input-h)*0.6);
|
||||
height: calc(var(--input-h)*0.5);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
svg{
|
||||
width: 10px;
|
||||
height: auto;
|
||||
}
|
||||
svg path{
|
||||
fill: var(--color-interface-600);
|
||||
}
|
||||
|
|
@ -210,8 +228,9 @@ input[type="number"] {
|
|||
}
|
||||
.spinner-down{
|
||||
svg{
|
||||
position: relative;
|
||||
top: -2px;
|
||||
|
||||
// position: relative;
|
||||
// top: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,3 +25,7 @@ button {
|
|||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
|
||||
|
||||
--color-txt: var(--color-interface-800);
|
||||
--color-txt: var(--color-interface-900);
|
||||
--color-panel-bg: var(--color-interface-050);
|
||||
|
||||
--color-page-highlight: #ff8a50;
|
||||
|
|
@ -20,6 +20,8 @@
|
|||
--space-xs: 0.5rem;
|
||||
--space-s: 1rem;
|
||||
--space: 1.5rem;
|
||||
--space-m: 2rem;
|
||||
--space-big: 3em;
|
||||
|
||||
--curve: cubic-bezier(0.86, 0, 0.07, 1);
|
||||
|
||||
|
|
@ -34,4 +36,5 @@
|
|||
font-size: 14px;
|
||||
|
||||
--panel-w: 540px;
|
||||
--panel-nav-h: 60px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,10 +216,14 @@ button {
|
|||
border: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-browngray-050: #f5f3f0;
|
||||
--color-browngray-200: #d0c4ba;
|
||||
--color-txt: var(--color-interface-800);
|
||||
--color-txt: var(--color-interface-900);
|
||||
--color-panel-bg: var(--color-interface-050);
|
||||
--color-page-highlight: #ff8a50;
|
||||
--color-purple: #7136ff;
|
||||
|
|
@ -227,6 +231,8 @@ button {
|
|||
--space-xs: 0.5rem;
|
||||
--space-s: 1rem;
|
||||
--space: 1.5rem;
|
||||
--space-m: 2rem;
|
||||
--space-big: 3em;
|
||||
--curve: cubic-bezier(0.86, 0, 0.07, 1);
|
||||
--sans-serif: "DM Sans", sans-serif;
|
||||
--mono: "Inconsolata", monospace;
|
||||
|
|
@ -236,6 +242,7 @@ button {
|
|||
--label-w: 18ch;
|
||||
font-size: 14px;
|
||||
--panel-w: 540px;
|
||||
--panel-nav-h: 60px;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
@ -269,7 +276,9 @@ input[type=number] {
|
|||
border: 1px solid var(--color-interface-200);
|
||||
background-color: var(--color-interface-100);
|
||||
font-family: var(--sans-serif);
|
||||
color: var(--color-txt);
|
||||
font-size: 1rem;
|
||||
padding-left: 0.5ch;
|
||||
}
|
||||
|
||||
.field {
|
||||
|
|
@ -329,6 +338,16 @@ input[type=number] {
|
|||
.field-font .field-checkbox label {
|
||||
font-weight: 400;
|
||||
margin-left: 0.75ch;
|
||||
color: var(--color-txt);
|
||||
}
|
||||
|
||||
.field-text-size input[type=number] {
|
||||
width: var(--input-w-small);
|
||||
padding-left: 0.75ch;
|
||||
}
|
||||
.field-text-size input[type=range] {
|
||||
flex-grow: 2;
|
||||
flex-shrink: 2;
|
||||
}
|
||||
|
||||
.field-margin, .field-size {
|
||||
|
|
@ -417,23 +436,22 @@ input[type=number] {
|
|||
top: 0;
|
||||
}
|
||||
.number-input .spinner-buttons button {
|
||||
height: calc(var(--input-h) * 0.6);
|
||||
height: calc(var(--input-h) * 0.5);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
.number-input .spinner-buttons button svg {
|
||||
width: 10px;
|
||||
height: auto;
|
||||
}
|
||||
.number-input .spinner-buttons button svg path {
|
||||
fill: var(--color-interface-600);
|
||||
}
|
||||
.number-input .spinner-buttons button:hover svg path {
|
||||
fill: var(--color-interface-900);
|
||||
}
|
||||
.number-input .spinner-buttons .spinner-down svg {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-top: 3em;
|
||||
margin: var(--space-m) 0;
|
||||
}
|
||||
.settings-section h2 {
|
||||
margin-bottom: var(--space);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -24,5 +24,13 @@ h2 {
|
|||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
margin: 0mm 0mm 24mm 0mm;
|
||||
margin: 0mm 0mm 10mm 0mm;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
|
|
|||
2
public/vendor/composer/autoload_psr4.php
vendored
2
public/vendor/composer/autoload_psr4.php
vendored
|
|
@ -16,7 +16,7 @@ return array(
|
|||
'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
|
||||
'League\\ColorExtractor\\' => array($vendorDir . '/league/color-extractor/src'),
|
||||
'Laminas\\Escaper\\' => array($vendorDir . '/laminas/laminas-escaper/src'),
|
||||
'Kirby\\' => array($vendorDir . '/getkirby/composer-installer/src', $baseDir . '/kirby/src'),
|
||||
'Kirby\\' => array($baseDir . '/kirby/src', $vendorDir . '/getkirby/composer-installer/src'),
|
||||
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
|
||||
'Base32\\' => array($vendorDir . '/christian-riesen/base32/src'),
|
||||
);
|
||||
|
|
|
|||
4
public/vendor/composer/autoload_static.php
vendored
4
public/vendor/composer/autoload_static.php
vendored
|
|
@ -96,8 +96,8 @@ class ComposerStaticInit0b7fb803e22a45eb87e24172337208aa
|
|||
),
|
||||
'Kirby\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/getkirby/composer-installer/src',
|
||||
1 => __DIR__ . '/../..' . '/kirby/src',
|
||||
0 => __DIR__ . '/../..' . '/kirby/src',
|
||||
1 => __DIR__ . '/..' . '/getkirby/composer-installer/src',
|
||||
),
|
||||
'Composer\\Semver\\' =>
|
||||
array (
|
||||
|
|
|
|||
12
public/vendor/composer/installed.php
vendored
12
public/vendor/composer/installed.php
vendored
|
|
@ -1,9 +1,9 @@
|
|||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'getkirby/plainkit',
|
||||
'pretty_version' => '5.1.4',
|
||||
'version' => '5.1.4.0',
|
||||
'reference' => null,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '76274fff04c54514230ad2bb0aca362139618411',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
@ -65,9 +65,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'getkirby/plainkit' => array(
|
||||
'pretty_version' => '5.1.4',
|
||||
'version' => '5.1.4.0',
|
||||
'reference' => null,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '76274fff04c54514230ad2bb0aca362139618411',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@
|
|||
import { ref, computed, watch } from 'vue';
|
||||
import { useStylesheetStore } from '../stores/stylesheet';
|
||||
import { usePopupPosition } from '../composables/usePopupPosition';
|
||||
import { useDebounce } from '../composables/useDebounce';
|
||||
import NumberInput from './ui/NumberInput.vue';
|
||||
import Coloris from '@melloware/coloris';
|
||||
import '@melloware/coloris/dist/coloris.css';
|
||||
|
|
@ -307,7 +308,7 @@ const colorInput = ref(null);
|
|||
const backgroundInput = ref(null);
|
||||
|
||||
let isUpdatingFromStore = false;
|
||||
let updateTimer = null;
|
||||
const { debouncedUpdate } = useDebounce(500);
|
||||
|
||||
// Style properties
|
||||
const fontFamily = ref({ value: 'Alegreya Sans' });
|
||||
|
|
@ -320,11 +321,6 @@ const background = ref({ value: 'transparent' });
|
|||
const marginOuter = ref({ value: 0, unit: 'mm' });
|
||||
const paddingInner = ref({ value: 0, unit: 'mm' });
|
||||
|
||||
const debouncedUpdate = (callback) => {
|
||||
clearTimeout(updateTimer);
|
||||
updateTimer = setTimeout(callback, 1000);
|
||||
};
|
||||
|
||||
const immediateUpdate = (callback) => {
|
||||
callback();
|
||||
};
|
||||
|
|
@ -373,9 +369,81 @@ const elementCss = computed(() => {
|
|||
return stylesheetStore.extractBlock(selector.value) || '';
|
||||
});
|
||||
|
||||
// Generate a preview CSS block from current field values
|
||||
const generatePreviewCss = () => {
|
||||
if (!selector.value) return '';
|
||||
|
||||
const properties = [];
|
||||
|
||||
if (fontFamily.value.value) {
|
||||
properties.push(` font-family: ${fontFamily.value.value};`);
|
||||
}
|
||||
if (fontStyle.value.italic) {
|
||||
properties.push(` font-style: italic;`);
|
||||
}
|
||||
if (fontWeight.value.value) {
|
||||
properties.push(` font-weight: ${fontWeight.value.value};`);
|
||||
}
|
||||
if (fontSize.value.value) {
|
||||
properties.push(` font-size: ${fontSize.value.value}${fontSize.value.unit};`);
|
||||
}
|
||||
if (textAlign.value.value) {
|
||||
properties.push(` text-align: ${textAlign.value.value};`);
|
||||
}
|
||||
if (color.value.value && color.value.value !== 'rgb(0, 0, 0)') {
|
||||
properties.push(` color: ${color.value.value};`);
|
||||
}
|
||||
if (background.value.value && background.value.value !== 'transparent') {
|
||||
properties.push(` background: ${background.value.value};`);
|
||||
}
|
||||
if (marginOuter.value.value) {
|
||||
properties.push(` margin: ${marginOuter.value.value}${marginOuter.value.unit};`);
|
||||
}
|
||||
if (paddingInner.value.value) {
|
||||
properties.push(` padding: ${paddingInner.value.value}${paddingInner.value.unit};`);
|
||||
}
|
||||
|
||||
if (properties.length === 0) return '';
|
||||
|
||||
return `${selector.value} {\n${properties.join('\n')}\n}`;
|
||||
};
|
||||
|
||||
const displayedCss = computed(() => {
|
||||
if (!selector.value) return '';
|
||||
|
||||
// If unlocked, show the actual CSS block from stylesheet
|
||||
if (!inheritanceLocked.value) {
|
||||
return elementCss.value || generatePreviewCss();
|
||||
}
|
||||
|
||||
// If locked, show commented preview of what would be applied
|
||||
const preview = generatePreviewCss();
|
||||
if (!preview) return '';
|
||||
|
||||
return '/* Héritage verrouillé - déverrouiller pour appliquer */\n/* ' +
|
||||
preview.split('\n').join('\n ') +
|
||||
' */';
|
||||
});
|
||||
|
||||
// Remove the element-specific CSS block to restore inheritance
|
||||
const removeElementBlock = () => {
|
||||
if (!selector.value) return;
|
||||
|
||||
const block = stylesheetStore.extractBlock(selector.value);
|
||||
if (block) {
|
||||
// Escape special regex characters in selector
|
||||
const escaped = selector.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
// Remove the block and any surrounding whitespace
|
||||
stylesheetStore.content = stylesheetStore.content.replace(
|
||||
new RegExp(`\\n?${escaped}\\s*\\{[^}]*\\}\\n?`),
|
||||
'\n'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const highlightedCss = computed(() => {
|
||||
if (!elementCss.value) return '';
|
||||
return hljs.highlight(elementCss.value, { language: 'css' }).value;
|
||||
if (!displayedCss.value) return '';
|
||||
return hljs.highlight(displayedCss.value, { language: 'css' }).value;
|
||||
});
|
||||
|
||||
// Update functions for each property
|
||||
|
|
@ -424,6 +492,21 @@ const updatePaddingInner = () => {
|
|||
stylesheetStore.updateProperty(selector.value, 'padding', paddingInner.value.value, paddingInner.value.unit);
|
||||
};
|
||||
|
||||
// Apply all current field values to create/update the CSS block
|
||||
const applyAllStyles = () => {
|
||||
if (!selector.value) return;
|
||||
|
||||
updateFontFamily();
|
||||
updateFontStyle();
|
||||
updateFontWeight();
|
||||
updateFontSize();
|
||||
updateTextAlign();
|
||||
updateColor();
|
||||
updateBackground();
|
||||
updateMarginOuter();
|
||||
updatePaddingInner();
|
||||
};
|
||||
|
||||
// Watch for changes
|
||||
watch(() => fontFamily.value.value, () => {
|
||||
if (isUpdatingFromStore) return;
|
||||
|
|
@ -605,6 +688,9 @@ const open = (element, event, count = null) => {
|
|||
// Store instance count if provided, otherwise calculate it
|
||||
elementInstanceCount.value = count !== null ? count : getInstanceCount(selector.value);
|
||||
|
||||
// Read inheritance state from element's data attribute
|
||||
inheritanceLocked.value = element.dataset.inheritanceUnlocked !== 'true';
|
||||
|
||||
// Load values from stylesheet
|
||||
loadValuesFromStylesheet();
|
||||
|
||||
|
|
@ -668,8 +754,25 @@ const handleIframeClick = (event, targetElement = null, elementCount = null) =>
|
|||
};
|
||||
|
||||
const toggleInheritance = () => {
|
||||
const wasLocked = inheritanceLocked.value;
|
||||
inheritanceLocked.value = !inheritanceLocked.value;
|
||||
// TODO: Implement CSS priority logic when unlocked
|
||||
|
||||
// Store the inheritance state in the element's data attribute
|
||||
if (selectedElement.value) {
|
||||
if (inheritanceLocked.value) {
|
||||
delete selectedElement.value.dataset.inheritanceUnlocked;
|
||||
} else {
|
||||
selectedElement.value.dataset.inheritanceUnlocked = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
if (inheritanceLocked.value && !wasLocked) {
|
||||
// Re-locking: remove the element-specific CSS block to restore inheritance
|
||||
removeElementBlock();
|
||||
} else if (!inheritanceLocked.value && wasLocked) {
|
||||
// Unlocking: apply all current field values to create the CSS block
|
||||
applyAllStyles();
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ handleIframeClick, close, visible });
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@
|
|||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { useStylesheetStore } from '../stores/stylesheet';
|
||||
import { usePopupPosition } from '../composables/usePopupPosition';
|
||||
import { useDebounce } from '../composables/useDebounce';
|
||||
import NumberInput from './ui/NumberInput.vue';
|
||||
import Coloris from '@melloware/coloris';
|
||||
import '@melloware/coloris/dist/coloris.css';
|
||||
|
|
@ -312,7 +313,7 @@ const inheritanceLocked = ref(true);
|
|||
const backgroundColorInput = ref(null);
|
||||
|
||||
let isUpdatingFromStore = false;
|
||||
let updateTimer = null;
|
||||
const { debouncedUpdate } = useDebounce(500);
|
||||
|
||||
const margins = ref({
|
||||
top: { value: 0, unit: 'mm' },
|
||||
|
|
@ -328,11 +329,6 @@ const background = ref({
|
|||
|
||||
const pattern = ref('');
|
||||
|
||||
const debouncedUpdate = (callback) => {
|
||||
clearTimeout(updateTimer);
|
||||
updateTimer = setTimeout(callback, 1000);
|
||||
};
|
||||
|
||||
const immediateUpdate = (callback) => {
|
||||
callback();
|
||||
};
|
||||
|
|
@ -387,9 +383,9 @@ const removeTemplateBlock = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const updateMargins = () => {
|
||||
// Only update if inheritance is unlocked
|
||||
if (inheritanceLocked.value) return;
|
||||
const updateMargins = (force = false) => {
|
||||
// Only update if inheritance is unlocked (unless forced)
|
||||
if (!force && inheritanceLocked.value) return;
|
||||
|
||||
const marginValue = `${margins.value.top.value}${margins.value.top.unit} ${margins.value.right.value}${margins.value.right.unit} ${margins.value.bottom.value}${margins.value.bottom.unit} ${margins.value.left.value}${margins.value.left.unit}`;
|
||||
|
||||
|
|
@ -419,9 +415,9 @@ const updateMargins = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const updateBackground = () => {
|
||||
// Only update if inheritance is unlocked
|
||||
if (inheritanceLocked.value) return;
|
||||
const updateBackground = (force = false) => {
|
||||
// Only update if inheritance is unlocked (unless forced)
|
||||
if (!force && inheritanceLocked.value) return;
|
||||
if (!background.value.value) return;
|
||||
|
||||
const currentBlock = getOrCreateTemplateBlock();
|
||||
|
|
@ -448,6 +444,12 @@ const updateBackground = () => {
|
|||
}
|
||||
};
|
||||
|
||||
// Apply all current field values to create/update the CSS block
|
||||
const applyAllStyles = () => {
|
||||
updateMargins(true);
|
||||
updateBackground(true);
|
||||
};
|
||||
|
||||
// Watch margin values (number inputs) with debounce
|
||||
watch(
|
||||
() => [
|
||||
|
|
@ -536,8 +538,8 @@ const open = (pageElement, event, count = 1) => {
|
|||
// Extract template name from data-page-type attribute
|
||||
templateName.value = pageElement.getAttribute('data-page-type') || '';
|
||||
|
||||
// Reset inheritance state when opening
|
||||
inheritanceLocked.value = true;
|
||||
// Read inheritance state from page element's data attribute
|
||||
inheritanceLocked.value = pageElement.dataset.inheritanceUnlocked !== 'true';
|
||||
|
||||
// Load values from stylesheet (@page block)
|
||||
loadValuesFromStylesheet();
|
||||
|
|
@ -587,12 +589,22 @@ const toggleInheritance = () => {
|
|||
const wasLocked = inheritanceLocked.value;
|
||||
inheritanceLocked.value = !inheritanceLocked.value;
|
||||
|
||||
// Store the inheritance state in the page element's data attribute
|
||||
if (selectedPageElement.value) {
|
||||
if (inheritanceLocked.value) {
|
||||
delete selectedPageElement.value.dataset.inheritanceUnlocked;
|
||||
} else {
|
||||
selectedPageElement.value.dataset.inheritanceUnlocked = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
if (inheritanceLocked.value && !wasLocked) {
|
||||
// Re-locking: remove the template-specific block
|
||||
// Fields keep their values, but preview returns to @page defaults
|
||||
removeTemplateBlock();
|
||||
} else if (!inheritanceLocked.value && wasLocked) {
|
||||
// Unlocking: apply all current field values to create the CSS block
|
||||
applyAllStyles();
|
||||
}
|
||||
// When unlocking: fields already have values, block will be created on first edit
|
||||
};
|
||||
|
||||
const pageCss = computed(() => {
|
||||
|
|
@ -604,9 +616,47 @@ const pageCss = computed(() => {
|
|||
return stylesheetStore.extractBlock('@page') || '';
|
||||
});
|
||||
|
||||
// Generate a preview CSS block from current field values
|
||||
const generatePreviewCss = () => {
|
||||
if (!templateName.value) return '';
|
||||
|
||||
const properties = [];
|
||||
|
||||
const marginValue = `${margins.value.top.value}${margins.value.top.unit} ${margins.value.right.value}${margins.value.right.unit} ${margins.value.bottom.value}${margins.value.bottom.unit} ${margins.value.left.value}${margins.value.left.unit}`;
|
||||
properties.push(` margin: ${marginValue};`);
|
||||
|
||||
if (background.value.value) {
|
||||
properties.push(` background: ${background.value.value};`);
|
||||
}
|
||||
|
||||
if (properties.length === 0) return '';
|
||||
|
||||
return `@page ${templateName.value} {\n${properties.join('\n')}\n}`;
|
||||
};
|
||||
|
||||
const displayedCss = computed(() => {
|
||||
// If unlocked, show the actual CSS block from stylesheet
|
||||
if (!inheritanceLocked.value) {
|
||||
return pageCss.value;
|
||||
}
|
||||
|
||||
// If locked, show commented preview of what would be applied
|
||||
if (!templateName.value) {
|
||||
// For base @page, just show it normally
|
||||
return pageCss.value;
|
||||
}
|
||||
|
||||
const preview = generatePreviewCss();
|
||||
if (!preview) return pageCss.value;
|
||||
|
||||
return '/* Héritage verrouillé - déverrouiller pour appliquer */\n/* ' +
|
||||
preview.split('\n').join('\n ') +
|
||||
' */';
|
||||
});
|
||||
|
||||
const highlightedCss = computed(() => {
|
||||
if (!pageCss.value) return '';
|
||||
return hljs.highlight(pageCss.value, { language: 'css' }).value;
|
||||
if (!displayedCss.value) return '';
|
||||
return hljs.highlight(displayedCss.value, { language: 'css' }).value;
|
||||
});
|
||||
|
||||
let cssDebounceTimer = null;
|
||||
|
|
@ -619,6 +669,7 @@ const handleCssInput = (event) => {
|
|||
}
|
||||
|
||||
cssDebounceTimer = setTimeout(() => {
|
||||
// Get the actual CSS block (not the commented preview)
|
||||
const oldBlock = pageCss.value;
|
||||
if (oldBlock) {
|
||||
stylesheetStore.content = stylesheetStore.content.replace(
|
||||
|
|
|
|||
|
|
@ -119,11 +119,11 @@ nav {
|
|||
position: relative;
|
||||
left: calc(var(--panel-w)*-1);
|
||||
|
||||
padding: 4rem 0;
|
||||
|
||||
background-color: var(--color-panel-bg);
|
||||
box-shadow: -5px 0px 12px;
|
||||
|
||||
|
||||
transition: left 0.3s var(--curve);
|
||||
}
|
||||
|
||||
|
|
@ -132,10 +132,10 @@ nav {
|
|||
}
|
||||
|
||||
.tab-panel {
|
||||
height: 100%;
|
||||
height: calc(100% - var(--panel-nav-h)*2);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 0 2em;
|
||||
// padding-left: 1em;
|
||||
margin-top: var(--panel-nav-h);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -288,16 +288,17 @@
|
|||
<script setup>
|
||||
import { ref, computed, watch, onMounted, inject } from 'vue';
|
||||
import { useStylesheetStore } from '../../stores/stylesheet';
|
||||
import { useDebounce } from '../../composables/useDebounce';
|
||||
import Coloris from '@melloware/coloris';
|
||||
import NumberInput from '../ui/NumberInput.vue';
|
||||
import '@melloware/coloris/dist/coloris.css';
|
||||
|
||||
const stylesheetStore = useStylesheetStore();
|
||||
const { debouncedUpdate } = useDebounce(500);
|
||||
const backgroundColorInput = ref(null);
|
||||
const activeTab = inject('activeTab', ref('document'));
|
||||
|
||||
let isUpdatingFromStore = false;
|
||||
let updateTimer = null;
|
||||
|
||||
const pageFormat = ref('A4');
|
||||
|
||||
|
|
@ -328,11 +329,6 @@ const pattern = ref('');
|
|||
const pageNumbers = ref(false);
|
||||
const runningTitle = ref(false);
|
||||
|
||||
const debouncedUpdate = (callback) => {
|
||||
clearTimeout(updateTimer);
|
||||
updateTimer = setTimeout(callback, 1000);
|
||||
};
|
||||
|
||||
const immediateUpdate = (callback) => {
|
||||
callback();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
<!-- Taille du texte -->
|
||||
<div class="settings-subsection">
|
||||
<div class="field">
|
||||
<div class="field field-text-size">
|
||||
<label for="text-size-range" class="label-with-tooltip" data-css="font-size">Taille du texte</label>
|
||||
<InputWithUnit
|
||||
v-model="fontSize"
|
||||
|
|
@ -130,9 +130,11 @@ import InputWithUnit from '../ui/InputWithUnit.vue';
|
|||
import MarginEditor from '../ui/MarginEditor.vue';
|
||||
import { useCssUpdater } from '../../composables/useCssUpdater';
|
||||
import { useCssSync } from '../../composables/useCssSync';
|
||||
import { useDebounce } from '../../composables/useDebounce';
|
||||
|
||||
const { updateStyle, setMargin, setDetailedMargins, setPadding, setDetailedPadding } = useCssUpdater();
|
||||
const { extractValue, extractNumericValue, extractSpacing } = useCssSync();
|
||||
const { debouncedUpdate } = useDebounce(500);
|
||||
|
||||
// Constants
|
||||
const fonts = ['Alegreya Sans', 'Arial', 'Georgia', 'Helvetica', 'Times New Roman'];
|
||||
|
|
@ -203,26 +205,32 @@ watch(weight, (val) => {
|
|||
|
||||
watch(fontSize, (val) => {
|
||||
if (isUpdatingFromStore) return;
|
||||
debouncedUpdate(() => {
|
||||
updateStyle('p', 'font-size', `${val.value}${val.unit}`);
|
||||
});
|
||||
}, { deep: true });
|
||||
|
||||
// Margin/Padding handlers
|
||||
const handleMarginOuterChange = ({ type, simple, detailed }) => {
|
||||
if (isUpdatingFromStore) return;
|
||||
debouncedUpdate(() => {
|
||||
if (type === 'simple') {
|
||||
setMargin('p', simple.value, simple.unit);
|
||||
} else {
|
||||
setDetailedMargins('p', detailed.top, detailed.right, detailed.bottom, detailed.left);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleMarginInnerChange = ({ type, simple, detailed }) => {
|
||||
if (isUpdatingFromStore) return;
|
||||
debouncedUpdate(() => {
|
||||
if (type === 'simple') {
|
||||
setPadding('p', simple.value, simple.unit);
|
||||
} else {
|
||||
setDetailedPadding('p', detailed.top, detailed.right, detailed.bottom, detailed.left);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Sync from store
|
||||
|
|
|
|||
15
src/composables/useDebounce.js
Normal file
15
src/composables/useDebounce.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Composable for debounced updates
|
||||
* @param {number} delay - Debounce delay in milliseconds (default: 500ms)
|
||||
* @returns {Function} debouncedUpdate function
|
||||
*/
|
||||
export function useDebounce(delay = 500) {
|
||||
let timer = null;
|
||||
|
||||
const debouncedUpdate = (callback) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(callback, delay);
|
||||
};
|
||||
|
||||
return { debouncedUpdate };
|
||||
}
|
||||
|
|
@ -16,20 +16,32 @@ const extractCssValue = (css, selector, property) => {
|
|||
|
||||
const updateCssValue = ({ css, selector, property, value, unit }) => {
|
||||
const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const formattedValue = unit === '' || unit === undefined ? value : `${value}${unit}`;
|
||||
|
||||
if (unit === '') {
|
||||
const regex = new RegExp(
|
||||
`(${escaped}\\s*{[^}]*${property}:\\s*)[^;]+`,
|
||||
'gi'
|
||||
);
|
||||
return css.replace(regex, `$1${value}`);
|
||||
// Check if selector exists
|
||||
const selectorRegex = new RegExp(`${escaped}\\s*{([^}]*)}`, 'i');
|
||||
const selectorMatch = css.match(selectorRegex);
|
||||
|
||||
if (!selectorMatch) {
|
||||
// Selector doesn't exist, create it
|
||||
return css + `\n${selector} {\n ${property}: ${formattedValue};\n}\n`;
|
||||
}
|
||||
|
||||
const regex = new RegExp(
|
||||
`(${escaped}\\s*{[^}]*${property}:\\s*)[\\d.]+(px|rem|em|mm|cm|in)`,
|
||||
'gi'
|
||||
);
|
||||
return css.replace(regex, `$1${value}${unit}`);
|
||||
const blockContent = selectorMatch[1];
|
||||
|
||||
// Check if property exists in the block
|
||||
const propertyRegex = new RegExp(`(${property}\\s*:\\s*)([^;]+)(;?)`, 'i');
|
||||
const propertyMatch = blockContent.match(propertyRegex);
|
||||
|
||||
if (!propertyMatch) {
|
||||
// Property doesn't exist, add it to the block
|
||||
const newBlockContent = blockContent.trimEnd() + `\n ${property}: ${formattedValue};`;
|
||||
return css.replace(selectorRegex, `${selector} {${newBlockContent}}`);
|
||||
}
|
||||
|
||||
// Property exists, replace the entire value
|
||||
const newBlockContent = blockContent.replace(propertyRegex, `$1${formattedValue}$3`);
|
||||
return css.replace(selectorRegex, `${selector} {${newBlockContent}}`);
|
||||
};
|
||||
|
||||
const cssParsingUtils = { extractCssBlock, extractCssValue, updateCssValue };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue