feat: align Beam settings controls
This commit is contained in:
parent
68e2160ac5
commit
be2294d3fa
|
|
@ -376,6 +376,7 @@ header {
|
||||||
}
|
}
|
||||||
|
|
||||||
.inspector-collapse-btn {
|
.inspector-collapse-btn {
|
||||||
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
width: 28px;
|
width: 28px;
|
||||||
|
|
@ -388,13 +389,25 @@ header {
|
||||||
color: var(--modal-btn-primary, var(--accent));
|
color: var(--modal-btn-primary, var(--accent));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 18px;
|
font-size: 0;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: background 140ms ease, transform 100ms ease;
|
transition: background 140ms ease, transform 100ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-collapse-btn::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 38%;
|
||||||
|
width: 11px;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: currentColor;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
.inspector-collapse-btn:hover {
|
.inspector-collapse-btn:hover {
|
||||||
background: var(--nodedc-glass-control-hover);
|
background: var(--nodedc-glass-control-hover);
|
||||||
}
|
}
|
||||||
|
|
@ -1183,17 +1196,23 @@ header {
|
||||||
|
|
||||||
.settings-panel {
|
.settings-panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
--env-control-h: 46px;
|
||||||
|
--env-button-radius: 17px;
|
||||||
|
--env-pill-radius: 999px;
|
||||||
|
--env-glass-control-bg: var(--nodedc-glass-control-bg);
|
||||||
|
--env-glass-control-hover: var(--nodedc-glass-control-hover);
|
||||||
|
--env-glass-control-shadow: var(--nodedc-glass-control-shadow);
|
||||||
left: var(--settings-left, 72px);
|
left: var(--settings-left, 72px);
|
||||||
bottom: var(--settings-bottom, 12px);
|
bottom: var(--settings-bottom, 12px);
|
||||||
background: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
|
background: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
|
||||||
border: 1px solid var(--modal-border);
|
border: 1px solid var(--modal-border);
|
||||||
border-radius: var(--modal-radius, 14px);
|
border-radius: var(--modal-radius, 14px);
|
||||||
padding: 8px;
|
padding: 12px;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 6px;
|
gap: 10px;
|
||||||
width: var(--settings-width, 320px);
|
width: var(--settings-width, 350px);
|
||||||
min-height: 300px;
|
min-height: 420px;
|
||||||
max-height: 520px;
|
max-height: min(720px, calc(100vh - 72px));
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
|
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
|
||||||
z-index: 22;
|
z-index: 22;
|
||||||
|
|
@ -1201,37 +1220,68 @@ header {
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-panel .settings-header {
|
.settings-panel .settings-header {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) var(--env-control-h);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
gap: 10px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
padding-bottom: 2px;
|
min-height: var(--env-control-h);
|
||||||
|
padding: 0 0 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-panel .settings-title {
|
.settings-panel .settings-title {
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-panel .settings-close {
|
.settings-panel .settings-close {
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 7px;
|
|
||||||
border: 1px solid var(--modal-border);
|
|
||||||
background: color-mix(in srgb, var(--modal-focus) var(--modal-focus-alpha, 100%), transparent);
|
|
||||||
color: var(--text);
|
|
||||||
cursor: pointer;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
width: var(--env-control-h);
|
||||||
|
height: var(--env-control-h);
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--env-button-radius);
|
||||||
|
background: var(--env-glass-control-bg);
|
||||||
|
color: rgba(245, 245, 250, 0.78);
|
||||||
|
box-shadow: var(--env-glass-control-shadow);
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1;
|
||||||
|
outline: none;
|
||||||
|
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
transition: background 140ms ease, color 140ms ease, transform 100ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .settings-close svg {
|
||||||
|
display: block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .settings-close:hover {
|
||||||
|
background: var(--env-glass-control-hover);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .settings-close:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .settings-close:focus,
|
||||||
|
.settings-panel .settings-close:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-panel .settings-section {
|
.settings-panel .settings-section {
|
||||||
border: 1px solid var(--modal-border);
|
border: 1px solid var(--modal-border);
|
||||||
border-radius: var(--modal-radius, 10px);
|
border-radius: var(--modal-radius, 10px);
|
||||||
padding: 6px;
|
padding: 8px;
|
||||||
background: color-mix(in srgb, var(--modal-focus) var(--modal-focus-alpha, 100%), transparent);
|
background: color-mix(in srgb, var(--modal-focus) var(--modal-focus-alpha, 100%), transparent);
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 4px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-panel .section-title {
|
.settings-panel .section-title {
|
||||||
|
|
@ -1242,10 +1292,82 @@ header {
|
||||||
|
|
||||||
.settings-panel .color-row {
|
.settings-panel .color-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 140px 1fr 1fr;
|
grid-template-columns: minmax(110px, 1fr) minmax(150px, 168px);
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 6px 4px 6px;
|
min-height: var(--env-control-h);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .color-row > :first-child {
|
||||||
|
min-width: 0;
|
||||||
|
color: rgba(235, 235, 245, 0.62);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: var(--env-control-h);
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 5px 10px 5px 7px;
|
||||||
|
border-radius: var(--env-pill-radius);
|
||||||
|
background: var(--env-glass-control-bg);
|
||||||
|
box-shadow: var(--env-glass-control-shadow);
|
||||||
|
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field__swatch {
|
||||||
|
position: relative;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
min-height: 26px;
|
||||||
|
flex: 0 0 26px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 999px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field__preview {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field__swatch input[type="color"] {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field__value {
|
||||||
|
height: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field__value input {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: rgba(255, 255, 255, 0.66);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider-row {
|
.slider-row {
|
||||||
|
|
@ -1256,6 +1378,116 @@ header {
|
||||||
padding: 4px 6px 4px 6px;
|
padding: 4px 6px 4px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control.slider-row {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: var(--env-control-h);
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--env-pill-radius);
|
||||||
|
background: var(--env-glass-control-bg);
|
||||||
|
box-shadow: var(--env-glass-control-shadow);
|
||||||
|
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control.slider-row::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 auto 0 0;
|
||||||
|
z-index: 1;
|
||||||
|
width: var(--slider-percent, 0%);
|
||||||
|
border-radius: inherit;
|
||||||
|
background: var(--modal-btn-primary, var(--accent));
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-label,
|
||||||
|
.settings-panel .env-range-control .slider-value {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 760;
|
||||||
|
line-height: 1;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-label {
|
||||||
|
left: 24px;
|
||||||
|
max-width: calc(100% - 132px);
|
||||||
|
overflow: hidden;
|
||||||
|
color: rgba(255, 255, 255, 0.92);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-value {
|
||||||
|
right: 20px;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-input {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 4;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: transparent;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-input::-webkit-slider-runnable-track {
|
||||||
|
height: var(--env-control-h);
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-input::-webkit-slider-thumb {
|
||||||
|
width: 24px;
|
||||||
|
height: var(--env-control-h);
|
||||||
|
margin-top: 0;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-input::-moz-range-track {
|
||||||
|
height: var(--env-control-h);
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-input::-moz-range-thumb {
|
||||||
|
width: 24px;
|
||||||
|
height: var(--env-control-h);
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-fill-text {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
clip-path: inset(0 calc(100% - var(--slider-percent, 0%)) 0 0);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-range-control .slider-fill-text .slider-label,
|
||||||
|
.settings-panel .env-range-control .slider-fill-text .slider-value {
|
||||||
|
color: var(--modal-btn-primary-contrast, #15170f);
|
||||||
|
}
|
||||||
|
|
||||||
.slider-label {
|
.slider-label {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
@ -1434,6 +1666,28 @@ header {
|
||||||
padding: 5px 6px;
|
padding: 5px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field .env-color-field__value input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: rgba(255, 255, 255, 0.66);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-align: center;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel .env-color-field .env-color-field__value input[type="text"]:focus {
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
.log {
|
.log {
|
||||||
background: color-mix(in srgb, var(--modal-element) var(--modal-focus-alpha, 100%), transparent);
|
background: color-mix(in srgb, var(--modal-element) var(--modal-focus-alpha, 100%), transparent);
|
||||||
border: 1px solid var(--modal-element-outline, var(--modal-element));
|
border: 1px solid var(--modal-element-outline, var(--modal-element));
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ const SDK_URL = "./lib/dc-camera-context.es.js";
|
||||||
import { createLogo } from "./src/ui/logo.js";
|
import { createLogo } from "./src/ui/logo.js";
|
||||||
import { toolbarConfig, applyToolbarVars } from "./src/ui/toolbar.js";
|
import { toolbarConfig, applyToolbarVars } from "./src/ui/toolbar.js";
|
||||||
import { createTemplatesMenu } from "./src/ui/templatesMenu.js";
|
import { createTemplatesMenu } from "./src/ui/templatesMenu.js";
|
||||||
import { createSettingsMenu } from "./src/ui/settingsMenu.js";
|
import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=2";
|
||||||
import { createMeasurementModal } from "./src/ui/measurementModal.js";
|
import { createMeasurementModal } from "./src/ui/measurementModal.js";
|
||||||
import { createGlassSelect } from "./src/ui/glassSelect.js";
|
import { createGlassSelect } from "./src/ui/glassSelect.js";
|
||||||
import { createSliderControl } from "./src/ui/controls/slider.js";
|
import { createSliderControl } from "./src/ui/controls/slider.js";
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="./dcViewer.css?v=11">
|
<link rel="stylesheet" href="./dcViewer.css?v=12">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|
@ -251,6 +251,6 @@
|
||||||
<input id="fileInput" class="hidden" type="file"
|
<input id="fileInput" class="hidden" type="file"
|
||||||
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
|
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
|
||||||
|
|
||||||
<script type="module" src="./dcViewer.js?v=16"></script>
|
<script type="module" src="./dcViewer.js?v=17"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
const isHexColor = (value) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || "").trim());
|
||||||
|
|
||||||
|
export function createColorControl({
|
||||||
|
value = "#ffffff",
|
||||||
|
previewValue = value,
|
||||||
|
ariaLabel = "Выбрать цвет",
|
||||||
|
valueLabel = "Значение цвета",
|
||||||
|
onChange,
|
||||||
|
}) {
|
||||||
|
const root = document.createElement("div");
|
||||||
|
root.className = "env-color-field";
|
||||||
|
|
||||||
|
const swatch = document.createElement("label");
|
||||||
|
swatch.className = "env-color-field__swatch";
|
||||||
|
|
||||||
|
const preview = document.createElement("span");
|
||||||
|
preview.className = "env-color-field__preview";
|
||||||
|
|
||||||
|
const picker = document.createElement("input");
|
||||||
|
picker.type = "color";
|
||||||
|
picker.setAttribute("aria-label", ariaLabel);
|
||||||
|
|
||||||
|
const valueWrap = document.createElement("div");
|
||||||
|
valueWrap.className = "env-color-field__value";
|
||||||
|
|
||||||
|
const valueInput = document.createElement("input");
|
||||||
|
valueInput.type = "text";
|
||||||
|
valueInput.setAttribute("aria-label", valueLabel);
|
||||||
|
valueWrap.appendChild(valueInput);
|
||||||
|
|
||||||
|
const update = (nextValue, { emit = false } = {}) => {
|
||||||
|
const safeValue = String(nextValue ?? "");
|
||||||
|
const safePreview = isHexColor(safeValue)
|
||||||
|
? safeValue
|
||||||
|
: (isHexColor(previewValue) ? previewValue : "#000000");
|
||||||
|
|
||||||
|
valueInput.value = safeValue;
|
||||||
|
picker.value = isHexColor(safeValue) ? safeValue : safePreview;
|
||||||
|
preview.style.background = safePreview;
|
||||||
|
|
||||||
|
if (emit && typeof onChange === "function") {
|
||||||
|
onChange(safeValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
picker.addEventListener("input", () => update(picker.value, { emit: true }));
|
||||||
|
valueInput.addEventListener("change", () => update(valueInput.value, { emit: true }));
|
||||||
|
|
||||||
|
swatch.appendChild(preview);
|
||||||
|
swatch.appendChild(picker);
|
||||||
|
root.appendChild(swatch);
|
||||||
|
root.appendChild(valueWrap);
|
||||||
|
|
||||||
|
update(value);
|
||||||
|
|
||||||
|
root.setValue = (nextValue) => update(nextValue);
|
||||||
|
root.getValue = () => valueInput.value;
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { createSliderControl } from "./controls/slider.js";
|
import { createSliderControl } from "./controls/slider.js";
|
||||||
|
import { createColorControl } from "./controls/color.js";
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
themeVersion: "v4-beam-lime",
|
themeVersion: "v4-beam-lime",
|
||||||
|
|
@ -186,7 +187,12 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
|
||||||
header.innerHTML = `<span class="settings-title">Настройки</span>`;
|
header.innerHTML = `<span class="settings-title">Настройки</span>`;
|
||||||
const closeBtn = document.createElement("button");
|
const closeBtn = document.createElement("button");
|
||||||
closeBtn.className = "settings-close";
|
closeBtn.className = "settings-close";
|
||||||
closeBtn.textContent = "—";
|
closeBtn.setAttribute("aria-label", "Закрыть настройки");
|
||||||
|
closeBtn.innerHTML = `
|
||||||
|
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
|
||||||
|
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
closeBtn.addEventListener("click", hide);
|
closeBtn.addEventListener("click", hide);
|
||||||
header.appendChild(closeBtn);
|
header.appendChild(closeBtn);
|
||||||
panel.appendChild(header);
|
panel.appendChild(header);
|
||||||
|
|
@ -202,31 +208,53 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
|
||||||
};
|
};
|
||||||
|
|
||||||
const addColor = (parent, label, key) => {
|
const addColor = (parent, label, key) => {
|
||||||
const isHex = (val) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test((val || "").trim());
|
|
||||||
const row = document.createElement("div");
|
const row = document.createElement("div");
|
||||||
row.className = "color-row";
|
row.className = "color-row";
|
||||||
const lbl = document.createElement("label");
|
const lbl = document.createElement("div");
|
||||||
lbl.textContent = label;
|
lbl.textContent = label;
|
||||||
const input = document.createElement("input");
|
const control = createColorControl({
|
||||||
input.type = "color";
|
value: state[key],
|
||||||
input.value = isHex(state[key]) ? state[key] : "#ffffff";
|
onChange: (nextValue) => {
|
||||||
const hex = document.createElement("input");
|
setVal(key, nextValue);
|
||||||
hex.type = "text";
|
},
|
||||||
hex.value = state[key];
|
|
||||||
input.addEventListener("change", (e) => {
|
|
||||||
hex.value = e.target.value;
|
|
||||||
setVal(key, e.target.value);
|
|
||||||
});
|
|
||||||
hex.addEventListener("change", (e) => {
|
|
||||||
input.value = e.target.value;
|
|
||||||
setVal(key, e.target.value);
|
|
||||||
});
|
});
|
||||||
row.appendChild(lbl);
|
row.appendChild(lbl);
|
||||||
row.appendChild(input);
|
row.appendChild(control);
|
||||||
row.appendChild(hex);
|
|
||||||
parent.appendChild(row);
|
parent.appendChild(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addSettingsSlider = ({
|
||||||
|
parent,
|
||||||
|
label,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
min = 0,
|
||||||
|
max = 100,
|
||||||
|
step = 1,
|
||||||
|
formatValue = (nextValue) => String(nextValue),
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
const slider = createSliderControl({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
step,
|
||||||
|
className: "env-range-control",
|
||||||
|
showValue: true,
|
||||||
|
formatValue,
|
||||||
|
onChange: (nextValue) => {
|
||||||
|
if (typeof onChange === "function") {
|
||||||
|
onChange(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setVal(key, nextValue);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
parent.appendChild(slider);
|
||||||
|
return slider;
|
||||||
|
};
|
||||||
|
|
||||||
const font = section("Фон");
|
const font = section("Фон");
|
||||||
addColor(font, "Фон внешний", "bgOuter");
|
addColor(font, "Фон внешний", "bgOuter");
|
||||||
addColor(font, "Фон холста верх", "canvasTop");
|
addColor(font, "Фон холста верх", "canvasTop");
|
||||||
|
|
@ -235,65 +263,59 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
|
||||||
addColor(font, "Фон загрузки низ", "loadingBottom");
|
addColor(font, "Фон загрузки низ", "loadingBottom");
|
||||||
panel.appendChild(font);
|
panel.appendChild(font);
|
||||||
|
|
||||||
const buttons = section("Кнопки");
|
const buttons = section("Кнопки");
|
||||||
addColor(buttons, "Плюс", "plus");
|
addColor(buttons, "Плюс", "plus");
|
||||||
addColor(buttons, "Кнопки (заливка)", "templateFill");
|
addColor(buttons, "Кнопки (заливка)", "templateFill");
|
||||||
addColor(buttons, "Кнопки (бордер)", "templateOutline");
|
addColor(buttons, "Кнопки (бордер)", "templateOutline");
|
||||||
addColor(buttons, "Кнопки (outline hover)", "templateOutlineHover");
|
addColor(buttons, "Кнопки (outline hover)", "templateOutlineHover");
|
||||||
const brightnessSlider = createSliderControl({
|
addSettingsSlider({
|
||||||
label: "Яркость подсветки",
|
parent: buttons,
|
||||||
value: Number(state.templateGlow ?? 80),
|
label: "Яркость подсветки",
|
||||||
min: 0,
|
key: "templateGlow",
|
||||||
max: 100,
|
value: Number(state.templateGlow ?? 80),
|
||||||
step: 1,
|
min: 0,
|
||||||
onChange: (val) => setVal("templateGlow", val),
|
max: 100,
|
||||||
});
|
step: 1,
|
||||||
buttons.appendChild(brightnessSlider);
|
formatValue: (val) => `${Math.round(val)}%`,
|
||||||
panel.appendChild(buttons);
|
});
|
||||||
|
panel.appendChild(buttons);
|
||||||
|
|
||||||
const projectButtons = section("Кнопки проектов");
|
const projectButtons = section("Кнопки проектов");
|
||||||
addColor(projectButtons, "Проекты (заливка)", "projectBtnFill");
|
addColor(projectButtons, "Проекты (заливка)", "projectBtnFill");
|
||||||
addColor(projectButtons, "Проекты (бордер)", "projectBtnBorder");
|
addColor(projectButtons, "Проекты (бордер)", "projectBtnBorder");
|
||||||
addColor(projectButtons, "Проекты (hover)", "projectBtnHover");
|
addColor(projectButtons, "Проекты (hover)", "projectBtnHover");
|
||||||
panel.appendChild(projectButtons);
|
panel.appendChild(projectButtons);
|
||||||
|
|
||||||
const modalButtons = section("Кнопки модалок");
|
const modalButtons = section("Кнопки модалок");
|
||||||
addColor(modalButtons, "Модальная primary", "modalBtnPrimary");
|
addColor(modalButtons, "Модальная primary", "modalBtnPrimary");
|
||||||
addColor(modalButtons, "Модальная secondary", "modalBtnSecondary");
|
addColor(modalButtons, "Модальная secondary", "modalBtnSecondary");
|
||||||
addColor(modalButtons, "Лоудер", "loaderColor");
|
addColor(modalButtons, "Лоудер", "loaderColor");
|
||||||
panel.appendChild(modalButtons);
|
panel.appendChild(modalButtons);
|
||||||
|
|
||||||
const toolbar = section("Toolbar");
|
const toolbar = section("Toolbar");
|
||||||
addColor(toolbar, "Фон", "toolbarBg");
|
addColor(toolbar, "Фон", "toolbarBg");
|
||||||
addColor(toolbar, "Актив", "toolbarBgActive");
|
addColor(toolbar, "Актив", "toolbarBgActive");
|
||||||
addColor(toolbar, "Бордер", "toolbarBorder");
|
addColor(toolbar, "Бордер", "toolbarBorder");
|
||||||
addColor(toolbar, "Outline", "toolbarOutline");
|
addColor(toolbar, "Outline", "toolbarOutline");
|
||||||
panel.appendChild(toolbar);
|
panel.appendChild(toolbar);
|
||||||
|
|
||||||
const navCube = section("NavCube");
|
const navCube = section("NavCube");
|
||||||
addColor(navCube, "Грани", "navCubeBase");
|
addColor(navCube, "Грани", "navCubeBase");
|
||||||
addColor(navCube, "Hover", "navCubeHover");
|
addColor(navCube, "Hover", "navCubeHover");
|
||||||
addColor(navCube, "Текст", "navCubeText");
|
addColor(navCube, "Текст", "navCubeText");
|
||||||
addColor(navCube, "Рёбра", "navCubeEdge");
|
addColor(navCube, "Рёбра", "navCubeEdge");
|
||||||
const edgeAlphaRow = document.createElement("div");
|
addSettingsSlider({
|
||||||
edgeAlphaRow.className = "color-row";
|
parent: navCube,
|
||||||
const edgeAlphaLbl = document.createElement("label");
|
label: "Прозрачность рёбер",
|
||||||
edgeAlphaLbl.textContent = "Прозрачность рёбер (0-1)";
|
value: Math.round(Math.max(0, Math.min(1, Number(state.navCubeEdgeAlpha ?? 0.6))) * 100),
|
||||||
const edgeAlphaInput = document.createElement("input");
|
min: 0,
|
||||||
edgeAlphaInput.type = "number";
|
max: 100,
|
||||||
edgeAlphaInput.step = "0.05";
|
step: 5,
|
||||||
edgeAlphaInput.min = "0";
|
formatValue: (val) => `${Math.round(val)}%`,
|
||||||
edgeAlphaInput.max = "1";
|
onChange: (val) => {
|
||||||
edgeAlphaInput.value = state.navCubeEdgeAlpha;
|
setVal("navCubeEdgeAlpha", Math.max(0, Math.min(1, Number(val) / 100)));
|
||||||
edgeAlphaInput.addEventListener("change", () => {
|
},
|
||||||
const v = Math.min(1, Math.max(0, parseFloat(edgeAlphaInput.value) || 0));
|
|
||||||
edgeAlphaInput.value = v;
|
|
||||||
setVal("navCubeEdgeAlpha", v);
|
|
||||||
});
|
});
|
||||||
edgeAlphaRow.appendChild(edgeAlphaLbl);
|
|
||||||
edgeAlphaRow.appendChild(edgeAlphaInput);
|
|
||||||
edgeAlphaRow.appendChild(document.createElement("div"));
|
|
||||||
navCube.appendChild(edgeAlphaRow);
|
|
||||||
panel.appendChild(navCube);
|
panel.appendChild(navCube);
|
||||||
|
|
||||||
const measure = section("Линейка");
|
const measure = section("Линейка");
|
||||||
|
|
@ -325,33 +347,35 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
|
||||||
const modals = section("Модальные окна");
|
const modals = section("Модальные окна");
|
||||||
addColor(modals, "Основной фон", "modalBg");
|
addColor(modals, "Основной фон", "modalBg");
|
||||||
addColor(modals, "Фокусный фон", "modalFocus");
|
addColor(modals, "Фокусный фон", "modalFocus");
|
||||||
const modalBgAlphaRow = createSliderControl({
|
addSettingsSlider({
|
||||||
|
parent: modals,
|
||||||
label: "Прозрачность основного фона",
|
label: "Прозрачность основного фона",
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
step: 0.05,
|
step: 0.05,
|
||||||
value: parseFloat(state.modalBgAlpha ?? 1),
|
value: parseFloat(state.modalBgAlpha ?? 1),
|
||||||
|
formatValue: (val) => `${Math.round(Number(val) * 100)}%`,
|
||||||
onChange: (val) => {
|
onChange: (val) => {
|
||||||
const num = Math.max(0, Math.min(1, parseFloat(val)));
|
const num = Math.max(0, Math.min(1, parseFloat(val)));
|
||||||
setVal("modalBgAlpha", num);
|
setVal("modalBgAlpha", num);
|
||||||
document.documentElement.style.setProperty("--modal-bg-alpha", `${num * 100}%`);
|
document.documentElement.style.setProperty("--modal-bg-alpha", `${num * 100}%`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
modals.appendChild(modalBgAlphaRow);
|
|
||||||
|
|
||||||
const modalFocusAlphaRow = createSliderControl({
|
addSettingsSlider({
|
||||||
|
parent: modals,
|
||||||
label: "Прозрачность фокусного фона",
|
label: "Прозрачность фокусного фона",
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
step: 0.05,
|
step: 0.05,
|
||||||
value: parseFloat(state.modalFocusAlpha ?? 1),
|
value: parseFloat(state.modalFocusAlpha ?? 1),
|
||||||
|
formatValue: (val) => `${Math.round(Number(val) * 100)}%`,
|
||||||
onChange: (val) => {
|
onChange: (val) => {
|
||||||
const num = Math.max(0, Math.min(1, parseFloat(val)));
|
const num = Math.max(0, Math.min(1, parseFloat(val)));
|
||||||
setVal("modalFocusAlpha", num);
|
setVal("modalFocusAlpha", num);
|
||||||
document.documentElement.style.setProperty("--modal-focus-alpha", `${num * 100}%`);
|
document.documentElement.style.setProperty("--modal-focus-alpha", `${num * 100}%`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
modals.appendChild(modalFocusAlphaRow);
|
|
||||||
|
|
||||||
addColor(modals, "Аутлайн", "modalBorder");
|
addColor(modals, "Аутлайн", "modalBorder");
|
||||||
addColor(modals, "Фон элемента", "modalElement");
|
addColor(modals, "Фон элемента", "modalElement");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue