Browse Source

function controls first part

Kirk Trombley 2 years ago
parent
commit
bcf6fd5665
3 changed files with 157 additions and 113 deletions
  1. 49 104
      index.html
  2. 103 9
      script.js
  3. 5 0
      styles.css

+ 49 - 104
index.html

@@ -33,7 +33,7 @@
           </div>
           <div>
             <label>
-              <input type="radio" name="metricKind" value="statistic" />
+              <input type="radio" name="metricKind" value="stat" />
               Set Statistics
             </label>
           </div>
@@ -50,7 +50,7 @@
             <option value="ch">Chebyshev (Ч)</option>
             <option value="lightnessDiff">Lightness (ℓ)</option>
           </select>
-          <select name="statistic" bind-to="statSelect" disabled>
+          <select name="stat" bind-to="statSelect" disabled>
             <option value="importance">Visual Importance (β)</option>
             <option value="inertia">Inertia (I)</option>
             <option value="variance">Variance (V)</option>
@@ -63,6 +63,53 @@
       </form>
     </template>
 
+    <template id="function-template">
+      <!-- TODO styling -->
+      <form bind-to="form" autocomplete="off">
+        <div class="flex col no-gap">
+          <label>
+            <input type="checkbox" name="sortOrder" role="button" />
+            <span bind-to="minmaxLabel"></span>
+          </label>
+          <span class="emphasis">[</span>
+          <div>
+            <label bind-to="useWholeImageLabel">
+              <input type="checkbox" name="useWholeImage" />
+              <span bind-to="metricSymbol"></span>(P)
+            </label>
+            ⋅
+            <label>
+              <input type="checkbox" name="useBestCluster" />
+              <span bind-to="metricSymbolCls"></span>
+            </label>
+            ⋅
+            <div class="fn-fraction | flex col no-gap">
+              <label>
+                <input type="checkbox" name="clusterSize" />
+                |<span bind-to="clusterName"></span>|
+              </label>
+              <label>
+                <input type="checkbox" name="invClusterSize" />
+                |<span bind-to="clusterNameInv"></span>|
+              </label>
+            </div>
+            ⋅
+            <div class="fn-fraction | flex col no-gap">
+              <label>
+                <input type="checkbox" name="totalSize" />
+                |P|
+              </label>
+              <label>
+                <input type="checkbox" name="invTotalSize" />
+                |P|
+              </label>
+            </div>
+          </div>
+          <span class="emphasis">]</span>
+        </div>
+      </form>
+    </template>
+
     <template id="pkmn-data-template">
       <div>α&nbsp;=&nbsp;<span bind-to="alpha"></span></div>
       <div>σ&nbsp;=&nbsp;<span bind-to="sigma"></span></div>
@@ -91,108 +138,6 @@
         <!-- TODO the actual data -->
       </div>
     </template>
-    <!-- 
-    <div class="main-container">
-      <form
-        action="javascript:void(0);"
-        id="sortFunction"
-        autocomplete="off"
-        onchange="updateSort()"
-      >
-        <div class="fn-group fn-group-outer">
-          <label class="fn-minmax">
-            <input type="checkbox" name="sortOrder" role="button" hidden />
-            <div class="fn-minmax-toggle">
-              <span>min</span>
-              <span>max</span>
-            </div>
-          </label>
-          <span class="fn-bracket">[</span>
-          <div
-            class="fn-group"
-            onchange="event.target.parentNode.classList.toggle('fn-part--disabled')"
-          >
-            <label class="fn-part">
-              <input type="checkbox" name="useWholeImage" checked />
-              <output name="metricSymbolP">α</output>(P)
-            </label>
-            ⋅
-            <label class="fn-part">
-              <input type="checkbox" name="totalSize" checked />
-              |P|
-            </label>
-            ⋅
-            <label class="fn-part fn-part--disabled">
-              <input type="checkbox" name="invTotalSize" />
-              |P|<sup>-1</sup>
-            </label>
-            ⋅
-            <label class="fn-part">
-              <input type="checkbox" name="useBestCluster" checked />
-              <output name="metricSymbolB">α</output>(B)
-            </label>
-            ⋅
-            <label class="fn-part fn-part--disabled">
-              <input type="checkbox" name="clusterSize" />
-              |B|
-            </label>
-            ⋅
-            <label class="fn-part">
-              <input type="checkbox" name="invClusterSize" checked />
-              |B|<sup>-1</sup>
-            </label>
-          </div>
-          <span class="fn-bracket">]</span>
-        </div>
-      </form>
-
-      <form
-        action="javascript:void(0);"
-        id="clusterFunction"
-        autocomplete="off"
-        onchange="updateSort()"
-      >
-        <div class="fn-group">
-          <span class="center">where B =&nbsp;</span>
-          <label class="fn-minmax fn-minmax--wide">
-            <input type="checkbox" name="sortOrder" role="button" checked hidden />
-            <div class="fn-minmax-toggle">
-              <span>argmin</span>
-              <span>argmax</span>
-            </div>
-          </label>
-          <span class="fn-bracket">[</span>
-          <div
-            class="fn-group"
-            onchange="event.target.parentNode.classList.toggle('fn-part--disabled')"
-          >
-            <span class="fn-part fn-part--fixed">
-              <output name="metricSymbol">β</output>(K)
-            </span>
-            ⋅
-            <label class="fn-part fn-part--disabled">
-              <input type="checkbox" name="clusterSize" />
-              |K|
-            </label>
-            ⋅
-            <label class="fn-part fn-part--disabled">
-              <input type="checkbox" name="invClusterSize" />
-              |K|<sup>-1</sup>
-            </label>
-            ⋅
-            <label class="fn-part fn-part--disabled">
-              <input type="checkbox" name="totalSize" />
-              |P|
-            </label>
-            ⋅
-            <label class="fn-part fn-part--disabled">
-              <input type="checkbox" name="invTotalSize" />
-              |P|<sup>-1</sup>
-            </label>
-          </div>
-          <span class="fn-bracket">]</span>
-        </div>
-      </form> -->
 
     <div id="sidebar" class="section | flow">
       <form id="colorSelect" class="flex col small-gap" autocomplete="off">

+ 103 - 9
script.js

@@ -1,3 +1,5 @@
+// ---- Color Controls ----
+
 const targetColor = U.obs();
 const randomizeTargetColor = () => {
   targetColor.value = d3
@@ -33,27 +35,29 @@ targetColor.subscribe((hex, { previous }) => {
 
 randomizeTargetColor();
 
-const addMetricForm = (legendText, initialKind, initialMetric) => {
+// ---- Metric Controls ----
+
+const addMetricForm = (formName, legendText, initialKind, initialMetric) => {
   const metricKind = U.obs(initialKind);
   let metric;
 
   U.template("metric-select-template", ({ form, legend }) => {
+    form.name = formName;
     legend.innerText = legendText;
     form.elements[initialKind].disabled = false;
-    // form.elements.sortMetric = form.elements[initialKind].value = initialMetric;
+    form.elements[initialKind].value = initialMetric;
     form.elements.metricKind.value = initialKind;
 
     U.reactive(() => {
       form.elements.whole.disabled = metricKind.value !== "whole";
       form.elements.mean.disabled = metricKind.value !== "mean";
-      form.elements.statistic.disabled = metricKind.value !== "statistic";
-      // metric.value = form.elements[metricKindObs.value].value;
+      form.elements.stat.disabled = metricKind.value !== "stat";
     });
 
     const metrics = U.obs([
       U.field(form.elements.whole),
       U.field(form.elements.mean),
-      U.field(form.elements.statistic),
+      U.field(form.elements.stat),
     ]);
 
     U.form(form, {
@@ -65,8 +69,8 @@ const addMetricForm = (legendText, initialKind, initialMetric) => {
     });
 
     metric = U.obs(() => {
-      const [whole, mean, statistic] = metrics.value;
-      return { whole, mean, statistic }[metricKind.value];
+      const [whole, mean, stat] = metrics.value;
+      return { whole, mean, stat }[metricKind.value];
     });
 
     return "sidebar";
@@ -75,5 +79,95 @@ const addMetricForm = (legendText, initialKind, initialMetric) => {
   return metric;
 };
 
-const sortMetric = addMetricForm("Sort Metric", "whole", "alpha");
-const clusterMetric = addMetricForm("Cluster Metric", "statistic", "importance");
+const sortMetric = addMetricForm("sortMetric", "Sort Metric", "whole", "alpha");
+const clsMetric = addMetricForm("clsMetric", "Cluster Metric", "stat", "importance");
+
+// ---- Sorting Function Controls ----
+
+// terrible hack
+const getMetricSymbol = (metric) =>
+  document.querySelector(`option[value=${metric}]`).textContent.at(-2);
+const sortMetricSymbol = U.obs(() => getMetricSymbol(sortMetric.value));
+const clsMetricSymbol = U.obs(() => getMetricSymbol(clsMetric.value));
+
+const setupMinMaxWithLabel = (checkboxField, labelElem, maxLabel, minLabel) => {
+  const sortOrderToggle = U.field(checkboxField);
+  const sortOrder = U.obs(() => (sortOrderToggle.value ? maxLabel : minLabel));
+  U.reactive(() => {
+    labelElem.innerText = sortOrder.value;
+  });
+  return sortOrder;
+};
+
+U.template(
+  "function-template",
+  ({ form, minmaxLabel, metricSymbol, metricSymbolCls, clusterName, clusterNameInv }) => {
+    form.name = "sortFunction";
+
+    form.elements.useWholeImage.checked = true;
+    form.elements.totalSize.checked = true;
+    form.elements.useBestCluster.checked = true;
+    form.elements.invClusterSize.checked = true;
+
+    const sortOrder = setupMinMaxWithLabel(
+      form.elements.sortOrder,
+      minmaxLabel,
+      "max",
+      "min"
+    );
+
+    // TODO field controls for the rest of the toggles
+
+    U.reactive(() => {
+      metricSymbol.innerText = sortMetricSymbol.value;
+      metricSymbolCls.innerText = `${sortMetricSymbol.value}(B)`;
+    });
+
+    clusterName.innerText = clusterNameInv.innerText = "B";
+
+    U.form(form);
+
+    return "sidebar";
+  }
+);
+
+U.template(
+  "function-template",
+  ({
+    form,
+    minmaxLabel,
+    useWholeImageLabel,
+    metricSymbolCls,
+    clusterName,
+    clusterNameInv,
+  }) => {
+    form.name = "clusterFunction";
+
+    form.elements.sortOrder.checked = true;
+
+    const sortOrder = setupMinMaxWithLabel(
+      form.elements.sortOrder,
+      minmaxLabel,
+      "argmax",
+      "argmin"
+    );
+
+    // TODO field controls for the rest of the toggles
+
+    // not needed for cluster function
+    useWholeImageLabel.nextSibling.remove();
+    useWholeImageLabel.remove();
+    // and this can't be disabled so just replace the field with the span
+    metricSymbolCls.parentElement.replaceWith(metricSymbolCls);
+
+    U.reactive(() => {
+      metricSymbolCls.innerText = `${clsMetricSymbol.value}(K)`;
+    });
+
+    clusterName.innerText = clusterNameInv.innerText = "K";
+
+    U.form(form);
+
+    return "sidebar";
+  }
+);

+ 5 - 0
styles.css

@@ -156,3 +156,8 @@ body > * + * {
 #sidebar select:disabled {
   display: none;
 }
+
+.fn-fraction > *:first-child {
+  padding-bottom: 2px;
+  border-block-end: 1px solid var(--highlight);
+}