Release 2.16.0
This commit is contained in:
1 parent
821593dd8f
commit
3514d514a3
36 files changed
+2329
-346
No files matched your search
@@ -20,6 +20,8 @@
|
||||
: (Array.isArray(templateData) ? templateData : []);
|
||||
var stage = document.getElementById('designer-stage');
|
||||
var overlay = document.getElementById('designer-overlay');
|
||||
var snapInput = document.getElementById('template-layout-snap');
|
||||
var alignmentGuides = [];
|
||||
var previewCard = document.getElementById('template-preview-card');
|
||||
var previewIntroButton = document.getElementById('template-preview-intro');
|
||||
var previewOutButton = document.getElementById('template-preview-out');
|
||||
@@ -557,104 +559,19 @@
|
||||
}
|
||||
|
||||
function getAnimationConfig(card) {
|
||||
var hiddenAnimationInput = card.querySelector('[name="region_animation_json[]"]');
|
||||
var animationConfig = normalizeAnimationConfig(hiddenAnimationInput ? hiddenAnimationInput.value || '{}' : '{}');
|
||||
var introPresetInput = card.querySelector('[data-animation-preset="intro"]');
|
||||
var outroPresetInput = card.querySelector('[data-animation-preset="outro"]');
|
||||
var loopPresetInput = card.querySelector('[data-animation-preset="loop"]');
|
||||
|
||||
if (introPresetInput && introPresetInput.dataset.animationPresetCustom !== '1' && introPresetInput.selectedIndex >= 0) {
|
||||
animationConfig.intro.preset = normalizePreviewAnimation(introPresetInput.value);
|
||||
}
|
||||
if (outroPresetInput && outroPresetInput.dataset.animationPresetCustom !== '1' && outroPresetInput.selectedIndex >= 0) {
|
||||
animationConfig.outro.preset = normalizePreviewAnimation(outroPresetInput.value);
|
||||
}
|
||||
if (loopPresetInput && loopPresetInput.dataset.animationPresetCustom !== '1' && loopPresetInput.selectedIndex >= 0) {
|
||||
animationConfig.loop.preset = normalizePreviewAnimation(loopPresetInput.value);
|
||||
}
|
||||
|
||||
return animationConfig;
|
||||
}
|
||||
|
||||
function syncAnimationPresetCustomState(selectInput, resetButton, preset) {
|
||||
if (!selectInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedPreset = normalizePreviewAnimation(preset);
|
||||
var hasMatchingOption = Array.prototype.some.call(selectInput.options || [], function (option) {
|
||||
return option && option.value === normalizedPreset;
|
||||
});
|
||||
|
||||
if (hasMatchingOption) {
|
||||
delete selectInput.dataset.animationPresetCustom;
|
||||
selectInput.hidden = false;
|
||||
if (resetButton) {
|
||||
resetButton.hidden = true;
|
||||
}
|
||||
} else {
|
||||
selectInput.dataset.animationPresetCustom = '1';
|
||||
selectInput.hidden = true;
|
||||
if (resetButton) {
|
||||
resetButton.hidden = false;
|
||||
resetButton.textContent = 'Reset to basic';
|
||||
}
|
||||
}
|
||||
return window.pulseRegionAnimationControls.read(card);
|
||||
}
|
||||
|
||||
function writeAnimationConfig(card, animationConfig) {
|
||||
var normalized = normalizeAnimationConfig(animationConfig);
|
||||
var hiddenAnimationInput = card.querySelector('[name="region_animation_json[]"]');
|
||||
var introPresetInput = card.querySelector('[data-animation-preset="intro"]');
|
||||
var outroPresetInput = card.querySelector('[data-animation-preset="outro"]');
|
||||
var loopPresetInput = card.querySelector('[data-animation-preset="loop"]');
|
||||
var introResetButton = card.querySelector('[data-animation-reset-button="intro"]');
|
||||
var outroResetButton = card.querySelector('[data-animation-reset-button="outro"]');
|
||||
var loopResetButton = card.querySelector('[data-animation-reset-button="loop"]');
|
||||
|
||||
if (hiddenAnimationInput) {
|
||||
hiddenAnimationInput.value = JSON.stringify(normalized, null, 2);
|
||||
}
|
||||
if (introPresetInput) {
|
||||
introPresetInput.value = normalized.intro.preset;
|
||||
syncAnimationPresetCustomState(introPresetInput, introResetButton, normalized.intro.preset);
|
||||
}
|
||||
if (outroPresetInput) {
|
||||
outroPresetInput.value = normalized.outro.preset;
|
||||
syncAnimationPresetCustomState(outroPresetInput, outroResetButton, normalized.outro.preset);
|
||||
}
|
||||
if (loopPresetInput) {
|
||||
loopPresetInput.value = normalized.loop.preset;
|
||||
syncAnimationPresetCustomState(loopPresetInput, loopResetButton, normalized.loop.preset);
|
||||
}
|
||||
|
||||
var normalized = window.pulseRegionAnimationControls.write(card, animationConfig);
|
||||
updatePreviewCardVisibility();
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function setAnimationPreset(card, stepName, preset) {
|
||||
var config = getAnimationConfig(card);
|
||||
if (config[stepName]) {
|
||||
config[stepName].preset = normalizePreviewAnimation(preset);
|
||||
config[stepName].duration_ms = null;
|
||||
config[stepName].delay_ms = null;
|
||||
config[stepName].iterations = null;
|
||||
config[stepName].easing = '';
|
||||
config[stepName].fill_mode = '';
|
||||
writeAnimationConfig(card, config);
|
||||
if (activeAnimationCard === card) {
|
||||
populateAnimationModal(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAnimationModalField(stepName, fieldName) {
|
||||
if (!animationAdvancedModal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return animationAdvancedModal.querySelector('[data-animation-modal-step="' + stepName + '"][data-animation-modal-field="' + fieldName + '"]');
|
||||
window.pulseRegionAnimationControls.setPreset(card, stepName, preset);
|
||||
updatePreviewCardVisibility();
|
||||
if (activeAnimationCard === card) populateAnimationModal(card);
|
||||
}
|
||||
|
||||
function populateAnimationModal(card) {
|
||||
@@ -670,20 +587,7 @@
|
||||
title.textContent = (label ? label.textContent : 'Region') + ' animation settings';
|
||||
}
|
||||
|
||||
['intro', 'outro', 'loop'].forEach(function (stepName) {
|
||||
var step = config[stepName] || normalizeAnimationStep({}, 'none');
|
||||
var presetInput = getAnimationModalField(stepName, 'preset');
|
||||
var durationInput = getAnimationModalField(stepName, 'duration_ms');
|
||||
var delayInput = getAnimationModalField(stepName, 'delay_ms');
|
||||
|
||||
if (presetInput) { presetInput.value = step.preset; }
|
||||
if (durationInput) { durationInput.value = String(getAnimationDurationPercent(step.duration_ms)); }
|
||||
if (delayInput) { delayInput.value = step.delay_ms === null || step.delay_ms === undefined ? '0' : String(step.delay_ms); }
|
||||
if (stepName === 'loop') {
|
||||
var iterationsInput = getAnimationModalField(stepName, 'iterations');
|
||||
if (iterationsInput) { iterationsInput.value = step.iterations === null || step.iterations === undefined ? '1' : String(Math.min(MAX_ANIMATION_REPEAT_COUNT, Math.max(1, Number(step.iterations) || 1))); }
|
||||
}
|
||||
});
|
||||
window.pulseRegionAnimationControls.populateAdvanced(animationAdvancedModal, config);
|
||||
}
|
||||
|
||||
function readNumberField(value) {
|
||||
@@ -719,45 +623,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getAnimationDurationPercent(durationMs) {
|
||||
var value = Number(durationMs);
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
return Math.min(200, Math.max(1, Math.round(value / 10)));
|
||||
}
|
||||
|
||||
function getAnimationDurationMs(durationPercent) {
|
||||
var percent = Math.min(200, Math.max(1, readNumberField(durationPercent) || 100));
|
||||
return percent * 10;
|
||||
}
|
||||
|
||||
function readAnimationModalConfig() {
|
||||
if (!animationAdvancedModal) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var config = getAnimationConfig(activeAnimationCard) || { intro: {}, outro: {}, loop: {} };
|
||||
|
||||
['intro', 'outro', 'loop'].forEach(function (stepName) {
|
||||
var presetInput = getAnimationModalField(stepName, 'preset');
|
||||
var durationInput = getAnimationModalField(stepName, 'duration_ms');
|
||||
var delayInput = getAnimationModalField(stepName, 'delay_ms');
|
||||
|
||||
var nextStep = Object.assign({}, config[stepName] || {});
|
||||
nextStep.preset = presetInput ? presetInput.value : nextStep.preset;
|
||||
nextStep.duration_ms = getAnimationDurationMs(durationInput ? durationInput.value : '');
|
||||
nextStep.delay_ms = readNumberField(delayInput ? delayInput.value : '') || 0;
|
||||
if (stepName === 'loop') {
|
||||
var iterationsInput = getAnimationModalField(stepName, 'iterations');
|
||||
nextStep.iterations = Math.min(MAX_ANIMATION_REPEAT_COUNT, Math.max(1, readNumberField(iterationsInput ? iterationsInput.value : '') || 1));
|
||||
}
|
||||
|
||||
config[stepName] = normalizeAnimationStep(nextStep, 'none');
|
||||
});
|
||||
|
||||
return config;
|
||||
return window.pulseRegionAnimationControls.readAdvanced(animationAdvancedModal, getAnimationConfig(activeAnimationCard));
|
||||
}
|
||||
|
||||
function saveAnimationModal() {
|
||||
@@ -1475,6 +1346,9 @@
|
||||
var selected = index === selectedNow ? ' selected' : '';
|
||||
return '<div class="designer-rect' + selected + '" data-index="' + index + '" style="--designer-overlay-scale:1;left:' + renderedBox.left + 'px;top:' + renderedBox.top + 'px;width:' + renderedBox.width + 'px;height:' + renderedBox.height + 'px;"><div class="designer-rect-label">' + escapeHtml(region.label || region.region_key || 'Region') + '</div><span class="resize-handle nw" data-dir="nw"></span><span class="resize-handle ne" data-dir="ne"></span><span class="resize-handle sw" data-dir="sw"></span><span class="resize-handle se" data-dir="se"></span></div>';
|
||||
}).join('');
|
||||
alignmentGuides.forEach(function (guide) {
|
||||
overlay.innerHTML += '<div class="layout-alignment-guide layout-alignment-guide--' + guide.axis + '" style="' + (guide.axis === 'x' ? 'left' : 'top') + ':' + (guide.position / canvasSize[guide.axis === 'x' ? 'width' : 'height'] * 100) + '%;"></div>';
|
||||
});
|
||||
if (draft) {
|
||||
var rect = overlayRect;
|
||||
var size = canvasSize;
|
||||
@@ -1633,6 +1507,15 @@
|
||||
document.addEventListener('mouseup', upHandler);
|
||||
}
|
||||
|
||||
function snapRegion(region, index, event, direction, aspect) {
|
||||
var size = getCanvasSize();
|
||||
var scale = getOverlayRect().width / size.width;
|
||||
var peers = getCards().filter(function (_card, peerIndex) { return peerIndex !== index; }).map(readCard);
|
||||
var snapped = window.pulseRegionLayout.snapBox(region, peers, size, snapInput.checked && !event.altKey ? 6 / Math.max(0.01, scale) : -1, direction, aspect && aspect.ratio);
|
||||
alignmentGuides = snapped.guides;
|
||||
return snapped.box;
|
||||
}
|
||||
|
||||
function startMove(index, event) {
|
||||
if (previewState.active) {
|
||||
return;
|
||||
@@ -1649,11 +1532,13 @@
|
||||
var maxY = Math.max(0, size.height - startRegion.height);
|
||||
var nextX = clamp(startRegion.x + dx, 0, maxX);
|
||||
var nextY = clamp(startRegion.y + dy, 0, maxY);
|
||||
writeCard(cardAt(index), { x: nextX, y: nextY, width: startRegion.width, height: startRegion.height });
|
||||
writeCard(cardAt(index), snapRegion({ x: nextX, y: nextY, width: startRegion.width, height: startRegion.height }, index, moveEvent));
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
}
|
||||
function upHandler() {
|
||||
alignmentGuides = [];
|
||||
requestOverlayRender();
|
||||
document.removeEventListener('mousemove', moveHandler);
|
||||
document.removeEventListener('mouseup', upHandler);
|
||||
}
|
||||
@@ -1755,11 +1640,14 @@
|
||||
if (next.width < 1) { if (dir.indexOf('w') !== -1) { next.x -= 1 - next.width; } next.width = 1; }
|
||||
if (next.height < 1) { if (dir.indexOf('n') !== -1) { next.y -= 1 - next.height; } next.height = 1; }
|
||||
next = fitRegionToCanvas(next, aspect);
|
||||
next = snapRegion(next, index, moveEvent, dir, aspect);
|
||||
writeCard(cardAt(index), { x: next.x, y: next.y, width: next.width, height: next.height });
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
}
|
||||
function upHandler() {
|
||||
alignmentGuides = [];
|
||||
requestOverlayRender();
|
||||
document.removeEventListener('mousemove', moveHandler);
|
||||
document.removeEventListener('mouseup', upHandler);
|
||||
}
|
||||
@@ -1931,12 +1819,14 @@
|
||||
canvasHeightInput.addEventListener('input', render);
|
||||
regionSelect.addEventListener('change', function () { setSelected(Number(regionSelect.value || 0)); });
|
||||
overlay.addEventListener('mousedown', function (event) {
|
||||
if (event.button !== 0) return;
|
||||
var rect = event.target.closest('.designer-rect');
|
||||
if (rect) {
|
||||
var index = Number(rect.getAttribute('data-index'));
|
||||
var handle = event.target.closest('.resize-handle');
|
||||
event.preventDefault();
|
||||
setSelected(index);
|
||||
overlay.focus({ preventScroll: true });
|
||||
if (handle) {
|
||||
resizeFromHandle(index, handle.getAttribute('data-dir'), event);
|
||||
} else {
|
||||
@@ -1950,6 +1840,21 @@
|
||||
event.preventDefault();
|
||||
setSelected(-1);
|
||||
});
|
||||
overlay.addEventListener('keydown', function (event) {
|
||||
if (previewState.active || selectedIndex < 0 || !cardAt(selectedIndex)) return;
|
||||
var movements = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] };
|
||||
var movement = movements[event.key];
|
||||
if (!movement) return;
|
||||
event.preventDefault();
|
||||
var region = readCard(cardAt(selectedIndex));
|
||||
var size = getCanvasSize();
|
||||
var step = event.shiftKey ? 10 : 1;
|
||||
region.x = clamp(region.x + movement[0] * step, 0, Math.max(0, size.width - region.width));
|
||||
region.y = clamp(region.y + movement[1] * step, 0, Math.max(0, size.height - region.height));
|
||||
writeCard(cardAt(selectedIndex), region);
|
||||
markTemplateFormDirty();
|
||||
requestOverlayRender();
|
||||
});
|
||||
if (templateForm) {
|
||||
templateForm.addEventListener('invalid', function (event) {
|
||||
var target = event.target;
|
||||
|
||||
Reference in new issue
Block a user