This commit is contained in:
JokoPrasetio 2025-12-04 09:40:40 +07:00
parent ec7a1af1d0
commit 0fd0ee8929

View File

@ -119,30 +119,7 @@
height: 10px;
}
/* thumb */
.range-colored::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #0d6efd;
cursor: pointer;
position: relative;
z-index: 2;
border: 2px solid #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.range-colored::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #0d6efd;
cursor: pointer;
position: relative;
z-index: 2;
border: 2px solid #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.range-scale-item {
position: absolute;
@ -243,7 +220,6 @@
</div>
</div>
</div>
@if ($hal === $halPertama)
<div class="card border-0 shadow-sm mb-4" id="head_soal">
<div class="card-body">
@ -344,6 +320,8 @@
$rangeStep = 1;
$rangeDefault = null;
$rangeTicks = [];
$rangeSubTickCount = 4;
$rangeMajorStep = 10;
if ($useDualForm && $currentAnswer) {
if (preg_match('/([0-9]+)\\s*\\(Tahun\\)/i', $currentAnswer, $matchYear)) {
$dualYearOld = $matchYear[1];
@ -376,18 +354,74 @@
}
$rangeSpan = max($rangeMax - $rangeMin, 1);
$startTick = (int) ceil($rangeMin);
$endTick = (int) floor($rangeMax);
for ($v = $startTick; $v <= $endTick; $v++) {
$position = (($v - $rangeMin) / $rangeSpan) * 100;
$rangeTicks[] = [
'value' => $v, // nilai asli
'label' => ($v === 1 || $v % 10 === 0) ? $v : null, // label hanya 1 & kelipatan 10
'position' => max(0, min(100, $position)),
];
$majorAnchors = [$rangeMin, $rangeMax];
if ($rangeMin <= 1 && $rangeMax >= 1) {
$majorAnchors[] = 1;
}
$firstMajor = max($rangeMin, ($rangeMin <= 1 ? 10 : ceil($rangeMin / $rangeMajorStep) * $rangeMajorStep));
for ($v = $firstMajor; $v <= $rangeMax; $v += $rangeMajorStep) {
$majorAnchors[] = $v;
}
sort($majorAnchors);
$uniqueAnchors = [];
foreach ($majorAnchors as $anchor) {
if (empty($uniqueAnchors) || abs(end($uniqueAnchors) - $anchor) > 0.0001) {
$uniqueAnchors[] = $anchor;
}
}
$majorAnchors = array_values(array_filter($uniqueAnchors, function ($value) use ($rangeMin, $rangeMax) {
return $value >= $rangeMin && $value <= $rangeMax;
}));
$ticks = [];
$addTick = function ($value, $isMajor = false) use (&$ticks, $rangeMin, $rangeSpan, $rangeMajorStep) {
$position = $rangeSpan > 0 ? (($value - $rangeMin) / $rangeSpan) * 100 : 0;
$isOne = abs($value - 1) < 0.0001;
$isMultiple = $rangeMajorStep > 0 ? abs(fmod($value, $rangeMajorStep)) < 0.0001 : false;
$ticks[] = [
'value' => $value,
'label' => ($isOne || $isMultiple) ? (int) round($value) : null,
'position' => max(0, min(100, $position)),
'is_major' => $isMajor,
'is_one' => $isOne,
];
};
$countAnchors = count($majorAnchors);
if ($countAnchors === 0) {
$addTick($rangeMin, true);
} else {
for ($i = 0; $i < $countAnchors; $i++) {
$currentAnchor = $majorAnchors[$i];
$addTick($currentAnchor, true);
if ($i === $countAnchors - 1) {
continue;
}
$nextAnchor = $majorAnchors[$i + 1];
$segmentLength = $nextAnchor - $currentAnchor;
if ($segmentLength <= 0 || $rangeSubTickCount <= 0) {
continue;
}
for ($sub = 1; $sub <= $rangeSubTickCount; $sub++) {
$fraction = $sub / ($rangeSubTickCount + 1);
$value = $currentAnchor + ($segmentLength * $fraction);
$addTick($value, false);
}
}
}
usort($ticks, function ($a, $b) {
if ($a['value'] == $b['value']) {
return 0;
}
return ($a['value'] < $b['value']) ? -1 : 1;
});
$rangeTicks = $ticks;
}
}
$rangeMinDisplay = ($rangeMin !== null && floor($rangeMin) == $rangeMin) ? (int) $rangeMin : $rangeMin;
@ -481,10 +515,9 @@
<div class="range-scale" aria-hidden="true">
@foreach ($rangeTicks as $tick)
@php
$value = (int) $tick['value'];
$label = $tick['label']; // bisa null
$isMajor = ($value === 1) || ($value % 10 === 0);
$isOneTick = ($value === 1);
$label = $tick['label'] ?? null;
$isMajor = !empty($tick['is_major']);
$isOneTick = !empty($tick['is_one']);
@endphp
<div class="range-scale-item
@ -1015,57 +1048,57 @@
}
}
function setupRangeInputs() {
const rangeInputs = form.querySelectorAll('[data-range-input]');
function setupRangeInputs() {
const rangeInputs = form.querySelectorAll('[data-range-input]');
rangeInputs.forEach(function (input) {
const detailId = input.getAttribute('data-range-input');
const output = detailId ? form.querySelector('[data-range-output="' + detailId + '"]') : null;
const manualInput = detailId ? form.querySelector('[data-range-manual="' + detailId + '"]') : null;
rangeInputs.forEach(function (input) {
const detailId = input.getAttribute('data-range-input');
const output = detailId ? form.querySelector('[data-range-output="' + detailId + '"]') : null;
const manualInput = detailId ? form.querySelector('[data-range-manual="' + detailId + '"]') : null;
const minValue = parseFloat(input.dataset.rangeMinValue || input.min || '0');
const maxValue = parseFloat(input.dataset.rangeMaxValue || input.max || '100');
const stepValue = parseFloat(input.step || '1') || 1;
const minValue = parseFloat(input.dataset.rangeMinValue || input.min || '0');
const maxValue = parseFloat(input.dataset.rangeMaxValue || input.max || '100');
const stepValue = parseFloat(input.step || '1') || 1;
function clampAndSnap(rawVal) {
let v = parseFloat(rawVal);
if (isNaN(v)) v = minValue;
if (v < minValue) v = minValue;
if (v > maxValue) v = maxValue;
return snapToStep(v, minValue, stepValue);
}
function clampAndSnap(rawVal) {
let v = parseFloat(rawVal);
if (isNaN(v)) v = minValue;
if (v < minValue) v = minValue;
if (v > maxValue) v = maxValue;
return snapToStep(v, minValue, stepValue);
}
function updateBoth(fromManual) {
const current = fromManual && manualInput ? manualInput.value : input.value;
const value = clampAndSnap(current);
function updateBoth(fromManual) {
const current = fromManual && manualInput ? manualInput.value : input.value;
const value = clampAndSnap(current);
input.value = value;
input.value = value;
if (output) {
output.textContent = formatRangeDisplay(value);
}
if (manualInput && !fromManual) {
manualInput.value = value;
}
if (output) {
output.textContent = formatRangeDisplay(value);
}
if (manualInput && !fromManual) {
manualInput.value = value;
}
updateRangeFill(input, minValue, maxValue);
}
updateRangeFill(input, minValue, maxValue);
}
input.addEventListener('input', function () {
updateBoth(false);
input.addEventListener('input', function () {
updateBoth(false);
});
if (manualInput) {
manualInput.addEventListener('input', function () {
updateBoth(true);
});
}
// initial
updateBoth(false);
});
if (manualInput) {
manualInput.addEventListener('input', function () {
updateBoth(true);
});
}
// initial
updateBoth(false);
});
}
function formatRangeDisplay(value) {