-
Основное поле
-
diff --git a/script.js b/script.js
index 04cef53..38470a2 100644
--- a/script.js
+++ b/script.js
@@ -286,14 +286,103 @@
$('cssOutput').value = toMatrix3dCSS(H);
}
- // ===== Input events =====
- $('inpFieldW').addEventListener('input', () => {
- const v = parseInt($('inpFieldW').value);
- if (v > 0) { state.fieldW = v; updateAll(); }
+ // ===== Field editing =====
+ let fieldEditing = false;
+ let fieldEditW = state.fieldW;
+ let fieldEditH = state.fieldH;
+ let keepAspect = true;
+
+ function updateChainIcon() {
+ const locked = $('chainLink').querySelectorAll('.chain-locked');
+ const unlocked = $('chainLink').querySelectorAll('.chain-unlocked');
+ locked.forEach(el => el.style.display = keepAspect ? '' : 'none');
+ unlocked.forEach(el => el.style.display = keepAspect ? 'none' : '');
+ $('chainLink').classList.toggle('unlocked', !keepAspect);
+ }
+
+ function setFieldEditing(editing) {
+ fieldEditing = editing;
+ const inpW = $('inpFieldW');
+ const inpH = $('inpFieldH');
+ const options = $('fieldEditOptions');
+ const btnEdit = $('btnEditField');
+ const chain = $('chainLink');
+
+ if (editing) {
+ fieldEditW = state.fieldW;
+ fieldEditH = state.fieldH;
+ inpW.disabled = false;
+ inpH.disabled = false;
+ options.classList.add('visible');
+ btnEdit.style.display = 'none';
+ chain.classList.add('visible');
+ updateChainIcon();
+ } else {
+ inpW.value = state.fieldW;
+ inpW.disabled = true;
+ inpH.value = state.fieldH;
+ inpH.disabled = true;
+ options.classList.remove('visible');
+ btnEdit.style.display = '';
+ chain.classList.remove('visible');
+ }
+ }
+
+ $('chainLink').addEventListener('click', () => {
+ keepAspect = !keepAspect;
+ updateChainIcon();
});
+
+ $('btnEditField').addEventListener('click', () => {
+ setFieldEditing(true);
+ $('inpFieldW').focus();
+ });
+
+ $('btnCancelField').addEventListener('click', () => {
+ setFieldEditing(false);
+ });
+
+ $('inpFieldW').addEventListener('input', () => {
+ if (!fieldEditing) return;
+ const newW = parseInt($('inpFieldW').value);
+ if (newW <= 0) return;
+ if (keepAspect) {
+ fieldEditH = Math.round(newW * state.fieldH / state.fieldW);
+ $('inpFieldH').value = fieldEditH;
+ }
+ fieldEditW = newW;
+ });
+
$('inpFieldH').addEventListener('input', () => {
- const v = parseInt($('inpFieldH').value);
- if (v > 0) { state.fieldH = v; updateAll(); }
+ if (!fieldEditing) return;
+ const newH = parseInt($('inpFieldH').value);
+ if (newH <= 0) return;
+ if (keepAspect) {
+ fieldEditW = Math.round(newH * state.fieldW / state.fieldH);
+ $('inpFieldW').value = fieldEditW;
+ }
+ fieldEditH = newH;
+ });
+
+ $('btnSaveField').addEventListener('click', () => {
+ const newW = parseInt($('inpFieldW').value);
+ const newH = parseInt($('inpFieldH').value);
+ if (newW <= 0 || newH <= 0) return;
+
+ if ($('chkRescaleCoords').checked && (newW !== state.fieldW || newH !== state.fieldH)) {
+ const sx = newW / state.fieldW;
+ const sy = newH / state.fieldH;
+ state.corners.forEach(c => {
+ c.x = Math.round(c.x * sx);
+ c.y = Math.round(c.y * sy);
+ });
+ }
+
+ state.fieldW = newW;
+ state.fieldH = newH;
+ setFieldEditing(false);
+ buildCornerInputs();
+ updateAll();
});
$('inpWidgetW').addEventListener('input', () => {
const v = parseInt($('inpWidgetW').value);
@@ -324,6 +413,7 @@
// ===== Reset =====
$('resetBtn').addEventListener('click', () => {
+ setFieldEditing(false);
state.fieldW = defaultState.fieldW;
state.fieldH = defaultState.fieldH;
state.widgetW = defaultState.widgetW;
diff --git a/style.css b/style.css
index 0520aa9..dfae87b 100644
--- a/style.css
+++ b/style.css
@@ -107,6 +107,16 @@ main {
margin-bottom: 12px;
}
+.panel-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12px;
+}
+.panel-header h2 {
+ margin-bottom: 0;
+}
+
.left-col {
display: flex;
flex-direction: column;
@@ -275,6 +285,145 @@ main {
background: #484f58;
}
+.btn-edit-field {
+ padding: 4px 12px;
+ border: 1px solid var(--accent);
+ border-radius: 4px;
+ background: transparent;
+ color: var(--accent);
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.15s, color 0.15s;
+}
+.btn-edit-field:hover {
+ background: rgba(88, 166, 255, 0.1);
+}
+
+.btn-save-field {
+ padding: 4px 12px;
+ border: none;
+ border-radius: 4px;
+ background: var(--btn-bg);
+ color: white;
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.15s;
+}
+.btn-save-field:hover {
+ background: var(--btn-hover);
+}
+
+.btn-cancel-field {
+ padding: 4px 12px;
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ background: transparent;
+ color: var(--text);
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.15s;
+}
+.btn-cancel-field:hover {
+ background: var(--border);
+}
+
+.field-size-stack {
+ display: flex;
+ align-items: stretch;
+ gap: 8px;
+}
+
+.chain-link {
+ display: none;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ min-height: 56px;
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ background: var(--input-bg);
+ color: var(--accent);
+ cursor: pointer;
+ flex-shrink: 0;
+ transition: background 0.15s, color 0.15s;
+}
+.chain-link:hover {
+ background: rgba(88, 166, 255, 0.1);
+}
+.chain-link.unlocked {
+ color: #8b949e;
+}
+.chain-link.visible {
+ display: flex;
+}
+
+.field-stack-inputs {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ flex: 1;
+}
+.field-stack-inputs label {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 13px;
+ color: #8b949e;
+}
+.field-stack-inputs label span {
+ min-width: 14px;
+}
+.field-stack-inputs input {
+ width: 100%;
+ padding: 6px 8px;
+ background: var(--input-bg);
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ color: var(--heading);
+ font-size: 14px;
+ font-family: monospace;
+ outline: none;
+}
+.field-stack-inputs input:focus {
+ border-color: var(--accent);
+}
+.field-stack-inputs input:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.field-edit-options {
+ display: none;
+ margin-top: 10px;
+ flex-direction: column;
+ gap: 8px;
+}
+.field-edit-options.visible {
+ display: flex;
+}
+
+.field-save-row {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 8px;
+}
+
+.checkbox-label {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 12px;
+ color: #8b949e;
+ cursor: pointer;
+}
+.checkbox-label input[type="checkbox"] {
+ accent-color: var(--accent);
+}
+
#fieldContainer {
width: 100%;
overflow: hidden;