とりあえず書きなぐる

とりあえず、マイノリティーなプログラムを極力説明せずに書きなぐります

大きなボタンの数値入力を作ってみる

f:id:vzc00525:20190502141548p:plain
数値入力です
jQueryプラグインとして実装します
増減ボタンとスライダーを付加してみます

/Src/cm.ts

interface NumberSpinOptions {
    digits?: number;
    slider?: boolean;
    max?: number;
    min?: number;
    step?: number;
    width?: string;
}

interface JQuery {

    ....

    numberSpin(options: NumberSpinOptions): JQuery;
    numberSpin(): JQuery;
}

namespace JQuery {

    ....

    $.fn.numberSpin = function (options?: NumberSpinOptions): JQuery {

        if ($(this).length > 1) {
            for (let i = 0; i < $(this).length; i++) {
                $(this).eq(i).numberSpin(options);
            }
            return;
        }
        /* 初期化*/
        let container = $(this).closest(".ui-input-text");
        {
            container.css("padding-right", "").addClass("ui-input-has-spin");
            container.children(".ui-btn-spin-group").remove();
            let itm = container.next(".ui-slider");
            if (itm.children(".ui-slider-spin").length > 0) {
                itm.remove();
            }
        }
        let spinInput = container.children("input");

        //オプション
        let opts: NumberSpinOptions = {
            digits: 0
            , slider: false
            , max: 100
            , min: 0
            , step: 1
        };
        {
            // 少数桁
            if ((options) && (typeof options.digits !== "undefined") && (options.digits >= 0)) {
                opts.digits = options.digits;
            }
            // スライダー表示
            if ((options) && (typeof options.slider !== "undefined")) {
                opts.slider = options.slider;
            }
            // 最大値
            if ((options) && (typeof options.max !== "undefined")) {
                opts.max = options.max;
            } else {
                let attrMax = parseFloat(spinInput.attr("max"));
                if (isNaN(attrMax) === false) {
                    opts.max = attrMax;
                }
            }
            // 最小値
            if ((options) && (typeof options.min !== "undefined")) {
                opts.min = options.min;
            } else {
                let attrMin = parseFloat(spinInput.attr("min"));
                if (isNaN(attrMin) === false) {
                    opts.min = attrMin;
                }
            }
            // 増減値
            if ((options) && (typeof options.step !== "undefined")) {
                opts.step = options.step;
            } else {
                let attrStep = parseFloat(spinInput.attr("step"));
                if (isNaN(attrStep) === false) {
                    opts.step = attrStep;
                }
            }
            opts.step = Math.abs(opts.step);
            if ((options) && (options.width)) {
                container.css("max-width", options.width);
            }
        }
        spinInput.attr({
            "min": opts.min
            , "max": opts.max
            , "step": opts.step
        });
	/*
	 * 増減ボタン作成
	 */
        let spinGroup = $("<div class='ui-btn-spin-group'>");
        let spinPlus = $("<a href='#' tabindex='-1' title='plus'   class='ui-btn ui-btn-inline ui-btn-icon-notext ui-icon-plus  ui-btn-spin-plus'>");
        let spinMinus = $("<a href='#' tabindex='-1' title='miuns' class='ui-btn ui-btn-inline ui-btn-icon-notext ui-icon-minus ui-btn-spin-minus'>");
        {
            // グループ追加
            container.append(spinGroup);
            // ボタン追加
            spinGroup.append(spinMinus);
            spinGroup.append(spinPlus);

            let r = 1;
            if (container.hasClass("ui-input-has-clear")) {
                r = container.outerWidth() - container.width();
            }
            let h = spinInput.outerHeight() - 2;
            spinGroup.css({
                top: 1
                , height: h
                , right: r
            });
            spinGroup.children(".ui-btn").outerHeight(h);
            container.css({
                "padding-right": r + spinGroup.outerWidth() + 2
            });
        }
	/*
	 * スライダー作成
	 */
        let spinSlider: JQuery;
        if ((opts.slider) && (opts.min < opts.max)) {
            let content = "";
            content += "<input";
            content += " tabindex='-1'";
            content += " type='range'";
            content += " min='" + opts.min + "'";
            content += " max='" + opts.max + "'";
            content += " step='" + opts.step + "'";
            content += " class='ui-slider-spin'";
            content += ">";
            spinSlider = $(content);
            container.after(spinSlider);
            spinSlider
                .enhanceWithin()
                .slider()
                .slider("refresh")
                .off("change")
                .on("change", function (event: Event) {
                    let v = parseFloat(spinSlider.val());
                    spinInput.val(v);
                    updateValue(0, false);
                });
            spinSlider.closest(".ui-slider").find(".ui-btn").attr("tabindex", "-1");
        }
        updateValue(0, false);
	/*
	 * イベント
	 */
        {
            spinInput.off("blur").on("blur", function (event: Event) {
                updateValue(0, false);
            });
            spinInput.off("keydown").on("keydown", function (event: JQueryKeyEventObject) {
                switch (event.which) {
                    case 13:				// Enter
                        updateValue(0, true);	// 表示
                        return false;
                    case 38:				// up
                        updateValue(1, true);	// 増
                        return false;
                    case 40:				// down
                        updateValue(-1, true);	// 減
                        return false;
                }
            });
            spinPlus.off("click").on("click", function (event: Event) {
                updateValue(1, true);	// 増
            });
            spinMinus.off("click").on("click", function (event: Event) {
                updateValue(-1, true);	// 減
            });
            spinPlus.off("mousedown").on("mousedown", function (event: JQueryMouseEventObject) {
                setRepeatTimer(1);	// リピート増
            });
            spinPlus.off("mouseup").on("mouseup", function (event: JQueryMouseEventObject) {
                setRepeatTimer(0);	// リピート終了
            });
            spinPlus.off("mouseleave").on("mouseleave", function (event: JQueryMouseEventObject) {
                setRepeatTimer(0);	// リピート終了
            });

            spinMinus.off("mousedown").on("mousedown", function (event: JQueryMouseEventObject) {
                setRepeatTimer(-1);	// リピート減
            });
            spinMinus.off("mouseup").on("mouseup", function (event: JQueryMouseEventObject) {
                setRepeatTimer(0);	// リピート終了
            });
            spinMinus.off("mouseleave").on("mouseleave", function (event: JQueryMouseEventObject) {
                setRepeatTimer(0); // リピート終了
            });
        }
	/*
	 * 増減リピートタイマー設定
	 */
        let delayTitmeHandle = 0;	// リピート開始待ち
        let repeatTimerHandle = 0;	// リピート間隔
        function setRepeatTimer(off: number) {
            if (off) {
                // 設定
                setRepeatTimer(0);
                delayTitmeHandle = setTimeout(function () {
                    repeatTimerHandle = setInterval(function () {
                        updateValue(off, true);
                    }, 50);
                }, 1000);
            } else {
                // クリア
                if (delayTitmeHandle) {
                    clearTimeout(delayTitmeHandle);
                    delayTitmeHandle = 0;
                }
                if (repeatTimerHandle) {
                    clearInterval(repeatTimerHandle);
                    repeatTimerHandle = 0;
                }
            }
        }
	/*
    	 * 値更新
	 */
        function updateValue(off: number, sel: boolean) {
            if (off > 0) {
                off = opts.step;
            }
            if (off < 0) {
                off = -opts.step;
            }

            let prev = parseFloat(spinInput.val());
            let v = prev + off;
            if (isNaN(v)) {
                v = 0;
            }
            v = limitMax(v);
            v = limitMin(v);
            spinInput.val(v.toFixed(opts.digits));

            if (spinSlider) {
                spinSlider.val(v).slider("refresh");
            }

            if (sel) {
                spinInput.select();
            }

            if (prev !== v) {
                spinInput.change();
            }
        }
	/*
	 * 最小値取得
	 */
        function limitMin(value: number): number {
            if (opts.max <= opts.min) {
                return value;
            }
            return Math.max(value, opts.min);
        }
	/*
	 * 最大値取得
	 */
        function limitMax(value: number): number {
            if (opts.max <= opts.min) {
                return value;
            }
            return Math.min(value, opts.max);
        }

        return this;
    };
}

残念なことにもjQuery Mobile はHTML5を謳っていながらinputの一部typeに対応していないようです
無理やり矯正します
/Src/cm.ts

/*
 * Input HTML5対応
*/
$(document).on("focusout", ".ui-input-text", function (event: Event) {
    $(this).removeClass("ui-focus");
});

CSSで体裁を整えます
/Src/cm.css

/*
  Input
*/
.ui-input-has-spin > input,
.ui-input-has-clear > input {
  border-right: 1px solid #ddd;
  padding-right: 0.4em;
}
/*
  Input Spin
*/
.ui-input-text > .ui-btn-spin-group {
  position: absolute;
}
  .ui-input-text > .ui-btn-spin-group > .ui-btn {
    margin: 0;
    border-width: 0;
  }
.ui-input-text.ui-input-has-spin {
  position: relative;
}
.ui-input-text.ui-input-has-clear.ui-input-has-spin > .ui-btn-spin-group {
  border-right: 1px solid #ddd;
}
/* Input Spin Slider */
input.ui-slider-spin {
  display: none;
}
  input.ui-slider-spin + .ui-slider-track {
    margin-left: 15px;
  }

サンプル追加します
HomeのPage2に項目追加します

        <li>
          <div class="ui-field-contain em7">
            <label for="spin1">Spin input:</label>
            <div>
              <input name="spin1" id="spin1" type="number" value="" max="100" min="-100" step="2">
            </div>
          </div>
        </li>
        <li>
          <div class="ui-field-contain em7">
            <label for="spin2">Spin input clr:</label>
            <div>
              <input name="spin2" id="spin2" type="number" value="" max="100" min="0" step="0.5" data-clear-btn="true">
            </div>
          </div>
        </li>

プラグインを適用します

    namespace Page2 {

        $(document).on("pagecontainershow", function (event: Event, ui: any) {

            if (cm.getToPageId(ui) !== "page2") {
                return;
            }

            // 数値入力設定
            $("#spin2").numberSpin({ digits: 2, slider: true });
            $("#spin1").numberSpin();

        });

       ....

    }