とりあえず書きなぐる

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

HandsonTableでレスポンシブってみる

f:id:vzc00525:20190506165654p:plain
jQuery MobileのTable Widgetにはレスポンシブ機能があります
Column Toggleを適用するとウィンドウ幅に応じてカラムが増えたり減ったりします
HandsonTable Proにはカラム表示/非表示機能があるようですが、community editionにはありません
多少インチキですが同様の機能を作ってみます
理屈は簡単、MediaQueryを使用して指定幅毎にカラムヘッダを再設定します

/Mobileに2ページ目を作成して、そこにテーブルを表示します
1ページ目のツールバーボタンを変更して2ページ目に移動できるようにします
/Views/Mobile.vbhtml

<head>

    ...

  <link rel="stylesheet" href="@Url.Content("~/Content/handsontable/handsontable.full.min.css")" />

    ....

  <script type="text/javascript" src="@Url.Content("~/Scripts/handsontable/handsontable.full.min.js")"></script>

   ....

  <script type="text/javascript" src="@Url.Content("~/Src/ht.js")"></script>
  <script type="text/javascript" src="@Url.Content("~/Src/app.js")"></script>

</head>
<body class="ui-nodisc-icon ui-alt-icon">

  <div data-role="page" id="page1">
          @*<a href="#" class="ui-btn ui-btn-icon-notext ui-icon-star"></a>*@
          @* ↓変更 *@
          <a href="#page2" class="ui-btn ui-btn-icon-notext ui-icon-table-b"></a>
  </div>

  <div data-role="page" id="page2">
    <div data-role="header" data-position="fixed" data-fullscreen="false" data-tap-toggle="false">
      <h1>Table</h1>
      <a href="#page1" class="ui-btn ui-btn-icon-notext ui-icon-arrow-l ui-btn-left"></a>
    </div>
    <div role="main" class="ui-content ui-nopadding">
      <div id="list_table"></div>
    </div>
  </div>
</body>

MediaQueryはパネルの出し入れに使ったものを拡張します
/Src/mobile.ts

	namespace mq {
		let mm = window.matchMedia("(min-width:28em) and (min-height:28em)");
		mm.addListener(function (event: MediaQueryList) {
			update();
		});
		export function isMatch(): boolean {
			return mm.matches;
		}

		/*
		 * テーブル
		 */
		export namespace Table {
			let table1 = window.matchMedia("(min-width:50em)");		// 50em以上
			table1.addListener(function (event: MediaQueryList) {
				update();
			});

			let table2 = window.matchMedia("(min-width:70em)");		// 70em以上
			table2.addListener(function (event: MediaQueryList) {
				update();
			});

			export function mode(): number {
				if (table2.matches) {
					return 2;			// 70em以上
				}
				if (table1.matches) {
					return 1;			// 50em以上
				}
				return 0;				// 50em以下
			}
		}


		/*
		 * パネルL
		 */
		export namespace PanelL {
			let panelL = window.matchMedia("(min-width:55em)");
			panelL.addListener(function (event: MediaQueryList) {
				update();
			});

			export function isVisible(): boolean {
				return panelL.matches;
			}
		}

		/*
		 * パネルR
		 */
		export namespace PanelR {
			let panelR = window.matchMedia("(min-width:80em)");
			panelR.addListener(function (event: MediaQueryList) {
				update();
			});

			export function isVisible(): boolean {
				return panelR.matches;
			}
		}

		/*
		 * 更新
		 */
		export function update() {
			if (mm.matches) {
				$(".ui-header .ui-toolbar").resetDisplay();
				$(".ui-header .ui-toolbtn").hide();
			} else {
				$(".ui-header .ui-toolbar").hide();
				$(".ui-header .ui-toolbtn").resetDisplay();
			}
			$(document).trigger("mq_responsive");
		}
	}


Page2のみ縦スクロールなしに設定します
/Src/mobile.ts

    /*
     * 表示前
     */
    $(document).on("pagecontainerbeforeshow", function (event: Event, ui: any) {
        switch (cm.getToPageId(ui)) {
            case "page2":
                cm.hideBodyOverflowY(true);		// 縦スクロールなし
                break;
            default:
                cm.hideBodyOverflowY(false);
        }
		mq.update();
    });

Page2は基本的に前回と同じ
オプション設定が若干違っています
getColumns内でカラム表示/非表示を設定しています
/Src/mobile.ts

    namespace Page2 {
        let _ht: Handsontable = null;				// HandsonTable
        let _resizeDelay = new DelayTimer(500);	// リサイズ遅延タイマー

        $(document).on("mq_responsive", function (event: Event) {
            if (cm.getActivePageId() !== "page2") {
                return;
            }
            if (_ht) {
                _ht.updateSettings({ columns: HTable.getColumns() }, false);
            }
        });
        /*
         * ページ表示
         */
        $(document).on("pagecontainershow", function (event: Event, ui: any) {
            if (cm.getToPageId(ui) !== "page2") {
                return;
            }
            // テーブル設定
            if (!_ht) {
                HTable.build();
            }
            // データ読込
            {
                let dts = App.duildTableData();
                _ht.loadData(dts);
                _ht.updateSettings({ maxRows: dts.length }, false);	// データ数設定
                Ht.setFullSize(_ht);
            }
            _ht.render();			// 再描画
            mq.update();
        });

	/*
	 * リサイズ
	 */
        $(window).on("resize", function (event: Event) {
            if (cm.getActivePageId() !== "page2") {
                return;
            }
            // 一定時間変更がない場合のみテーブルサイズ設定
            _resizeDelay.timeout(function () {
                Ht.setFullSize(_ht);
            });
        });

	/*
	 * テーブル
	 */
        namespace HTable {
	    /*
	     * 作成
	     */
            export function build() {
                _ht = new Handsontable($("#list_table")[0], {
                    data: null
                    , columns: getColumns()					// 列設定
                    , colHeaders: true						// 列見出し
                    , rowHeaders: true						// 行見出し
                    , rowHeaderWidth: 30					// 行見出し幅
                    , manualColumnResize: false				// 列幅変更:変更不可
                    , multiSelect: false					// 複数選択
                    , stretchH: "all"						// 水平ストレッチ:均等
                    , autoColumnSize: false					// 自動サイズ調整
                    , wordWrap: true						// セル内折り返し:する
                    , outsideClickDeselects: false			// 選択を維持
                    , disableVisualSelection: "area"		// 範囲選択不可
                    , selectionMode: "single"               // 選択モード
                    , startRows: 0							// データ無時の行数
                    , trimWhitespace: false					// 前後の空白トリム
                    , currentRowClassName: "current-row"	// 選択列にクラス名付加
                    , rowHeights: function (row: number) {	// 行高さ
                        return 50;
                    }
                    , enterMoves: { row: 0, col: 0 }        // Enterキー移動先
                    , autoWrapCol: false					// 列移動ループ
                    , autoWrapRow: false					// 行移動ループ
                    , fillHandle: false                     // 選択範囲を埋める
                });
                Ht.setFullSize(_ht);	// 全面表示
            }

	    /*
	     * カラム情報
	     */
            export function getColumns(): Object[] {
                let cols: Object[] = [];

                cols.push({
                    type: "text"
                    , title: "番号"
                    , data: "data_no"
                    , readOnly: true
                    , colWidths: 90
                });

                if (mq.Table.mode() > 0) {	// レスポンシブモードで表示/非表示
                    cols.push({
                        type: "text"
                        , title: "状態"
                        , data: "data_state"
                        , readOnly: true
                        , colWidths: 100
                    });
                }

                cols.push({
                    type: "text"
                    , title: "日付"
                    , data: "data_date"
                    , readOnly: true
                    , colWidths: 80
                });

                {										// レスポンシブモードで幅変更
                    let w = 100;
                    if (mq.Table.mode() > 0) {
                        w = 200;
                    }
                    if (mq.Table.mode() > 1) {
                        w = 300;
                    }
                    cols.push({
                        type: "text"
                        , title: "名称"
                        , data: "data_name"
                        , readOnly: true
                        , colWidths: w
                    });
                }

                if (mq.Table.mode() > 0) {	// レスポンシブモードで表示/非表示
                    cols.push({
                        type: "text"
                        , title: "顧客名"
                        , data: "customer_name"
                        , readOnly: true
                        , colWidths: 100
                    });

                    cols.push({
                        type: "text"
                        , title: "担当者"
                        , data: "handle_name"
                        , readOnly: true
                        , colWidths: 100
                    });
                }

                cols.push({
                    type: "numeric"
                    , title: "金額"
                    , data: "price"
                    , numericFormat: {
                        pattern: "0,000"
                        , culture: "ja-JP"
                    }
                    , readOnly: true
                    , colWidths: 70
                });

                if (mq.Table.mode() > 1) {	// レスポンシブモードで表示/非表示
                    cols.push({
                        type: "text"
                        , title: "作成日"
                        , data: "create_date"
                        , readOnly: true
                        , colWidths: 150
                    });

                    cols.push({
                        type: "text"
                        , title: "作成者"
                        , data: "create_user"
                        , readOnly: true
                        , colWidths: 100
                    });

                    cols.push({
                        type: "text"
                        , title: "更新日"
                        , data: "modify_date"
                        , readOnly: true
                        , colWidths: 150
                    });

                    cols.push({
                        type: "text"
                        , title: "更新者"
                        , data: "modify_user"
                        , readOnly: true
                        , colWidths: 100
                    });
                }
                return cols;
            }
        }
    }

ウィンドウ幅に応じでカラム数が11→7→4と変化します
イレギュラーな使い方とは思いますが、意外と簡単