Widget:ConvertCalc2: Difference between revisions

From WyrdeWiki
No edit summary
Tag: Manual revert
No edit summary
Line 1: Line 1:
<noinclude>__NOTOC__
<noinclude>__NOTOC__
This is a universal two-unit converter widget using a single slider.
This widget converts with one neutral slider N and two editable fields.


=Usage=
=Usage=
{{#widget:ConvertCalc2
{{#widget:ConvertCalc
  | id=cc1
  | id=cc1
  | TopLabel=Years
  | TopLabel=years
  | TopFactor=1.3125
  | TopFactor=1.3125
  | BottomLabel=Cycles
  | BottomLabel=Cycles
Line 12: Line 12:
  | SliderMax=100
  | SliderMax=100
  | SliderStep=0.1
  | SliderStep=0.1
| Decimals=1
| NumStep=0.1
}}
}}
{| class="wikitable"
{| class="wikitable"
|-
|-
! param !! meaning !! default
! param !! meaning !! default
|-
|-
| TopLabel || text label at the top || years
| TopLabel || label for the top unit || years
|-
|-
| TopFactor || multiplicative factor for the top value || 1.3125
| TopFactor || multiplier for top value (Top = N * TopFactor) || 1.3125
|-
|-
| BottomLabel || text label at the bottom || Cycles
| BottomLabel || label for the bottom unit || Cycles
|-
|-
| BottomFactor || multiplicative factor for the bottom value || 1
| BottomFactor || multiplier for bottom value (Bottom = N * BottomFactor) || 1
|-
|-
| SliderMin || minimum value N || 1
| SliderMin / SliderMax / SliderStep || slider range & step for N || 1 / 100 / 0.1
|-
|-
| SliderMax || maximum value N || 100
| Decimals || fixed decimals to display || 1
|-
|-
| SliderStep || slider step || 0.1
| NumStep || step on the number inputs || 0.1
|}
|}
</noinclude><includeonly>
</noinclude><includeonly>
<div class="wh-convert" style="max-width:32rem;margin:1rem 0;">
  <!-- Top label -->
  <div style="font-weight:700;margin-bottom:.25rem;" id="<!--{$id|default:'cc'|escape:'html'}-->-topLbl">
    <!--{$TopLabel|default:'years'|escape:'html'}-->
  </div>


<div class="wh-convertcalc2" style="width:768px;margin:1rem 0;">
  <!-- Top value (editable) -->
  <strong><!--{$TopLabel|default:'years'|escape:'html'}--></strong>
  <div style="text-align:center;margin-bottom:.4rem;">
    <input id="<!--{$id|default:'cc'|escape:'html'}-->-topVal" type="number"
          step="<!--{$NumStep|default:0.1|escape:'html'}-->" inputmode="decimal"
          style="font:600 1.4rem/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
                  width:12ch;padding:.15rem .35rem;text-align:right;">  
    <span id="<!--{$id|default:'cc'|escape:'html'}-->-topUnit"
          style="font-weight:600;margin-left:.25rem;">
      <!--{$TopLabel|default:'years'|escape:'html'}-->
    </span>
  </div>


   <div id="<!--{$id|default:'cc'|escape:'html'}-->-topval"
  <!-- Slider row -->
      style="text-align:center;margin:.3rem 0;font-size:1.3rem;font-weight:600;">
   <div style="display:flex;align-items:center;gap:.5rem;margin:.25rem 0;">
    —
    <input id="<!--{$id|default:'cc'|escape:'html'}-->-N" type="range"
          min="<!--{$SliderMin|default:1|escape:'html'}-->"
          max="<!--{$SliderMax|default:100|escape:'html'}-->"
          step="<!--{$SliderStep|default:0.1|escape:'html'}-->"
          value="<!--{$SliderMin|default:1|escape:'html'}-->"
          style="flex:1;">
    <button id="<!--{$id|default:'cc'|escape:'html'}-->-swap"
            title="Swap units"
            style="font-size:.95rem;padding:.2rem .5rem;cursor:pointer;">⇅</button>
   </div>
   </div>


   <input id="<!--{$id|default:'cc'|escape:'html'}-->-slider"
   <!-- Bottom value (editable) -->
        type="range"
  <div style="text-align:center;margin:.4rem 0;">
        min="<!--{$SliderMin|default:1|escape:'html'}-->"
    <input id="<!--{$id|default:'cc'|escape:'html'}-->-botVal" type="number"
        max="<!--{$SliderMax|default:100|escape:'html'}-->"
          step="<!--{$NumStep|default:0.1|escape:'html'}-->" inputmode="decimal"
        step="<!--{$SliderStep|default:0.1|escape:'html'}-->"
          style="font:600 1.4rem/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
        value="<!--{$SliderMin|default:1|escape:'html'}-->"
                  width:12ch;padding:.15rem .35rem;text-align:right;">  
        style="width:100%;">
    <span id="<!--{$id|default:'cc'|escape:'html'}-->-botUnit"
          style="font-weight:600;margin-left:.25rem;">
      <!--{$BottomLabel|default:'Cycles'|escape:'html'}-->
    </span>
  </div>


   <div id="<!--{$id|default:'cc'|escape:'html'}-->-botval"
  <!-- Bottom label -->
      style="text-align:center;margin:.3rem 0;font-size:1.3rem;font-weight:600;">
   <div style="font-weight:700;margin-top:.25rem;" id="<!--{$id|default:'cc'|escape:'html'}-->-botLbl">
    —
    <!--{$BottomLabel|default:'Cycles'|escape:'html'}-->
   </div>
   </div>
  <strong><!--{$BottomLabel|default:'Cycles'|escape:'html'}--></strong>
</div>
</div>


Line 62: Line 89:
   function ge(s){ return document.getElementById(ID + s); }
   function ge(s){ return document.getElementById(ID + s); }


   var TopLabel   = "<!--{$TopLabel|default:'years'|escape:'js'}-->";
  // Params
   var TopFactor = parseFloat("<!--{$TopFactor|default:1.3125|escape:'js'}-->");
   var TopLabel     = "<!--{$TopLabel|default:'years'|escape:'js'}-->";
   var BottomLabel= "<!--{$BottomLabel|default:'Cycles'|escape:'js'}-->";
  var BottomLabel  = "<!--{$BottomLabel|default:'Cycles'|escape:'js'}-->";
   var BottomFactor=parseFloat("<!--{$BottomFactor|default:1|escape:'js'}-->");
   var TopFactor   = parseFloat("<!--{$TopFactor|default:1.3125|escape:'js'}-->") || 1.3125;
    
   var BottomFactor = parseFloat("<!--{$BottomFactor|default:1|escape:'js'}-->") || 1;
   function fmt(x){
  var MIN  = parseFloat("<!--{$SliderMin|default:1|escape:'js'}-->") || 1;
     if(x >= 1000) return x.toLocaleString();
  var MAX  = parseFloat("<!--{$SliderMax|default:100|escape:'js'}-->") || 100;
    return x.toFixed(2).replace(/\.00$/,'');
   var STEP = parseFloat("<!--{$SliderStep|default:0.1|escape:'js'}-->") || 0.1;
  var DEC  = parseInt("<!--{$Decimals|default:1|escape:'js'}-->") || 1;
 
   // Elements
   var el = {
    topLbl: ge("-topLbl"),
    botLbl: ge("-botLbl"),
    topUnit: ge("-topUnit"),
    botUnit: ge("-botUnit"),
    topVal: ge("-topVal"),
    botVal: ge("-botVal"),
    N: ge("-N"),
     swap: ge("-swap")
  };
 
  // Format helpers
  function clamp(v,min,max){ return Math.max(min, Math.min(max, v)); }
  function fix(v){ return Number(v).toFixed(DEC); }
 
  // Core updates (N drives both)
  function setFromN(n){
    n = clamp(n, MIN, MAX);
    el.N.value = n;
    el.topVal.value = fix(n * TopFactor);
    el.botVal.value = fix(n * BottomFactor);
   }
   }


   function ready(fn){
  // When top changes, derive N from top
     if(document.readyState === "complete" || document.readyState === "interactive"){
   function setFromTop(x){
      setTimeout(fn,0);
     var n = x / TopFactor;
    } else {
    setFromN(n);
      document.addEventListener("DOMContentLoaded", fn, {once:true});
  }
     }
  // When bottom changes, derive N from bottom
  function setFromBot(x){
    var n = x / BottomFactor;
     setFromN(n);
   }
   }


   ready(function init(){
   // Bind events (with light debouncing via rAF)
    var slider = ge("-slider");
  var raf = null;
    var topval = ge("-topval");
  function queue(fn){ if(raf) return; raf = requestAnimationFrame(function(){ raf=null; fn(); }); }
    var botval = ge("-botval");


    if(!slider || !topval || !botval) return;
  el.N.addEventListener("input", function(){ queue(function(){ setFromN(parseFloat(el.N.value)); }); });
  el.topVal.addEventListener("input", function(){ queue(function(){ setFromTop(parseFloat(el.topVal.value)); }); });
  el.botVal.addEventListener("input", function(){ queue(function(){ setFromBot(parseFloat(el.botVal.value)); }); });


    function update(){
  // Swap labels & factors but preserve the visible numbers by keeping N constant
      var N = parseFloat(slider.value) || 0;
  el.swap.addEventListener("click", function(){
    // swap labels
    var tl = TopLabel, bl = BottomLabel;
    TopLabel = bl; BottomLabel = tl;
    el.topLbl.textContent = TopLabel;
    el.topUnit.textContent = TopLabel;
    el.botLbl.textContent = BottomLabel;
    el.botUnit.textContent = BottomLabel;


      topval.textContent = fmt(N * TopFactor) + " " + TopLabel;
    // swap factors
      botval.textContent = fmt(N * BottomFactor) + " " + BottomLabel;
    var tf = TopFactor; TopFactor = BottomFactor; BottomFactor = tf;
    }


     slider.addEventListener("input", update);
     // recompute from current N
     update();
     setFromN(parseFloat(el.N.value));
   });
   });
  // Init starting state
  setFromN(MIN);
})();
})();
</script>
</script>
</includeonly>
</includeonly>

Revision as of 22:59, 11 November 2025

This widget converts with one neutral slider N and two editable fields.

Usage

param meaning default
TopLabel label for the top unit years
TopFactor multiplier for top value (Top = N * TopFactor) 1.3125
BottomLabel label for the bottom unit Cycles
BottomFactor multiplier for bottom value (Bottom = N * BottomFactor) 1
SliderMin / SliderMax / SliderStep slider range & step for N 1 / 100 / 0.1
Decimals fixed decimals to display 1
NumStep step on the number inputs 0.1