@@ -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 @@
@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
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) {