Ext JS 勉強会 ー Explorer 風ファイラーの実現

Ext JS 勉強会 ー Explorer 風ファイラーの実現 »
Yasunao TAKANO

Ext JS 勉強会
   Explorer 風ファイラーの実現
氏名:高野 保真 (TAKANO Yasunao)
        (takano@coma-systems.com,
         @tyasunao)
自己紹介
所属:コマ・システムズ,MNBI,
        電気通信大学大学院
Javascript 歴:1年半ぐらい
Ext JS 歴:今回のファイラーが初めて
高野保真
2010 年 4 月 21 日
Ext JS を使えば,初心者でも
かなりのものが作れる!!

ただし,調べるのは結構大変
Link Station
リクエスト
Ajax RPC
ファイルの情報
認証
ユーザ情報
NAS (Network Attached Storage)のフロントエンド
Ext JS 2.3.0 を利用
デモ
Ext.menu.Menu
Ext.History を利用
Ext.Toolbar
Ext.Toolbar.Button
Ext.Toolbar.Separator
Ext.Toolbar
Ext.DataView
Ext.grid.EditorGridPanel
Ext.data.Record
Ext.tree.TreePanel
Ext.tree.TreeEditor
Ext.tree.TreeLoader
Ext.menu.Separator
Ext.Panel
Ext.Toolbar.Button
Ext.form.TextField
Ext.Toolbar.Button
Ext.form.TextField
メニューバー
アイコンバー
ナビゲーションバー
アドレスパネル
検索パネル
ログインボタン
メインパネル
ツリーパネル
詳細パネル
フッターパネル
Ext.BoxComponent
メインパネル
カードレイアウト
複数のパネルを重ねておいて,1 つだけアクティブ
ところが
パフォーマンス的に,別々に(表示形式が変更される毎に)レンダリングしたいという要望
実は
   Ext.layout.CardLayout.deferedRender 
で行けたかも.
「1000 以上のファイルがあるフォルダも表示したい」
とりあえず,手動で中身を入れ換えるように実装
ただ,deferedRender は遅延してレンダリングするだけなので,レンダリングした後はエレメントが残る.
なにかと面倒かも.
「IE での動作が重い.フリーズしたようになり,表示に数分かかるときもある」
IE ではコンポーネント等の width, height を設定しないと表示が崩れる
Ext.GridView(Ext.grid.EditorGridPanel)や Ext.DataView の限界
本当はスクロールしたらレンダリングするということをやるといいのだが,簡単には「ページング」を使えば良い
mainPanel = new Ext.Panel({
    id: 'main-panel',
    layout: 'card',
    items: [
      view_detail, view_icon_small, 
      view_icon_medium, view_icon_large,
      view_side
});
mainPanel.layout.setActiveItem(i);
API Document からだけでは分からない機能がある
API にないプロパティがある.
      Ext.tree.TreeEditor.selectOnFocus
API で詳しく書かれていない場合がある.
      Ext.tree.TreeLoader.on の第 4 引数
IE では modal が効かない?
マウスでファイル/フォルダを選択したい
Example にそんな感じのがあったなぁ
Data View Example
data-view-plugins.js を参考に実装
// DataView ではなくて周りの Panel に Plugin を設定する
Ext.Panel.DragSelector = function(cfg) {
    var rs, bodyRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
    ...

    function onBeforeStart(e) {
        // scroll bar の上だったら,ドラッグをスタートしない.ちょっといいかげん
        return e.xy[0] < getWindowWidth() - 30;
    }

    function onStart(e) {
        view.clearSelections();                // view で現在選択されているアイテムの選択を解除

        panel.on('containerclick', cancelClick, panel, {single:true});
        if (!proxy) {
            proxy = panel.el.createChild({cls:'x-view-selector'});
        } else{
            proxy.setDisplayed('block');
        }
        fillRegions();
    }

    function fillRegions() {
        rs = [];
        view.all.each(function(el) {                     // DataView の全アイテムについて 
            rs[rs.length] = el.getRegion();            // el.getRegion() で範囲を求めて,取っておく
        });
        bodyRegion = panel.el.getRegion();          // パネルの全体も
    }

    // ドラッグ中
    function onDrag(e) {
        contextMenu.hide();

        var startXY = tracker.startXY;
        var xy = tracker.getXY();

        var x = Math.min(startXY[0], xy[0]);
        var y = Math.min(startXY[1], xy[1]);
        var w = Math.abs(startXY[0] - xy[0]);
        var h = Math.abs(startXY[1] - xy[1]);

        dragRegion.left = x-5;                             // ドラッグの範囲
        dragRegion.top = y-5;
        dragRegion.right = x+w+5;
        dragRegion.bottom = y+h+5;

        dragRegion.constrainTo(bodyRegion);           // パネル全体に限定する 
        proxy.setRegion(dragRegion);                     // 選択範囲を表示する

        for (var i = 0, len = rs.length; i < len; i++) {        // onStart で保持しておいたアイテム全部について
            var r = rs[i], sel = dragRegion.intersect(r);
            if (sel && !r.selected) {                       // 選択が重なっていたら選択する
                r.selected = true;
                view.select(i, true);
            } else if (!sel && r.selected) {              // 重なっていないものは選択を外す
                r.selected = false;
                view.deselect(i);
            }
        }
    }

    function onEnd(e) {
        if (proxy) {
            proxy.setDisplayed(false);         // 範囲表示を外す
        }
    }

    function onRender(panel){
        tracker = new Ext.dd.DragTracker({
            onBeforeStart: onBeforeStart,
            onStart: onStart,
            onDrag: onDrag,
            onEnd: onEnd
        });

        tracker.initEl(panel.el);
    }
};
// EditorGridPanel についても同様
Ext.GridDragSelector = function(cfg) {
    var rs, bodyRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
    ...

    this.getTracker = function() {
        return tracker;
    };

    function fillRegions() {
        rs = [];
        var rows = view.getView().getRows();                // 全行について
        var top = view.getView().el.getTop() + view.getView().getHeaderCell(0).clientHeight;
        var left = view.getView().el.getLeft();
        for (var i = 0; i < rows.length; i++) {
            rs[i] = getRowClientRect(rows[i], top, left);       // 各行の範囲をとる
            top += rows[i].clientHeight;
        }

        bodyRegion = view.el.getRegion();
    }

    ...

    function onDrag(e) {
        contextMenu.hide();

        var startXY = tracker.startXY;
        var xy = tracker.getXY();

        var x = Math.min(startXY[0], xy[0]);
        var y = Math.min(startXY[1], xy[1]);
        var w = Math.abs(startXY[0] - xy[0]);
        var h = Math.abs(startXY[1] - xy[1]);

        dragRegion.left = x;
        dragRegion.top = y;
        dragRegion.right = x+w;
        dragRegion.bottom = y+h;

        dragRegion.constrainTo(bodyRegion);
        proxy.setRegion(dragRegion);

        var rows = new Array();
        for (var i = 0; i < rs.length; i++) {    // onStart で保持しておいたアイテム全部について
            var r = rs[i], sel = dragRegion.intersect(r);
            if(sel && !r.selected) {                       // 選択が重なっていたら選択する
                view.getSelectionModel().selectRow(i, true);     // 行の選択
            } else {
                view.getSelectionModel().deselectRow(i);
            }
        }
    }

    ...
};
そのまま使えるとは限らないが,
Example には有益ものが多い
Layout Browser はオススメ
MyDropZone = function(el) {
    new Ext.dd.DropTarget(el,
  {
      ddGroup : 'groupDD',
      containerScroll: true,

      // ドロップされたときに呼ばれる
      notifyDrop: function(source, e, data) {
          var activePanel = mainPanel.getComponent(0);

          var draggedRecords = activePanel.getSelectedRecords();   // ドラッグされているファイルを取得
          var destination = activePanel.getDroppedRecords();         // ドロップ先のフォルダを取得
          if (destination && !destination.leaf) {
              移動できれば, 移動のための RPC を発行する
 
              return true;
          } else {
              return false;
          }
      },
      notifyOver : function(source, e, data) {
          return this.dropAllowed;
      }
  });
};
ツリーパネル,メインパネルそれぞれが
ドラッグ元,ドロップ先になる必要がある
ツリーパネルからツリーパネル
メインパネルからメインパネル
ツリーパネルからメインパネル
メインパネルからツリーパネル
メインパネルへドロップされたとき,どのアイコンの上か知りたい
アイコン毎,リストの行毎に Ext.dd.DropZone を
設定できれば楽そうなのだが..
注) 今回リスト表示に使った Ext.grid.EditorGridPanel は,
通常の Ext.grid.GridPanel と違って,trackMouseOver の
デフォルトが false になっている.(Firefox 2 の互換性のため?)
Panel 全体を DropZone に
// リスト表示の場合
getDroppedRecords : function() {
  // 座標から,その下にあるアイコンを取るのは困難なので,ドロップされたときに,
  // マウスオーバされているものを取ることにしている.
  var dest = document.getElementsByClassName("x-grid3-row-over")[0];
  if (dest) {
      return store.getAt(dest.rowIndex);
  } else {
      return null;
  }
},
それでもファイル/フォルダ数が増えるとパフォーマンスはイマイチ
Web ブラウザ
サムネイル生成
Tips
まとめ
クロスブラウザ
function printProperties(obj) {
    var properties = '';

    for (var prop in obj) {
        properties += prop + "=" + obj[prop] + "\n";
    }

    alert(properties);
}
on( String eventName, Function handler, [Object scope], [Object options] ) : void

Parameters:
  eventName : String     The type of event to listen for
  handler : Function      The method the event invokes
  scope : Object     (optional) The scope in which to execute the handler function. 
                          The handler function's "this" context.
  options : Object  (optional)
私のデバッグ法
結局,ExtJS からはみ出たことをやろうとすると,Javascript のクロスブラウザの問題が出てくる
  Event まわり,innerText/textContent,(CSS)
iPhone
リクエスト
Ajax RPC
ファイルの情報
認証
ユーザ情報
iPhone / iPod Touch
http://www.extjs.com/products/js/build/
メモリが限られるので,custom ext.js 
をビルド
感想
情報を調べるのは結構大変


見栄えがよいものを作れる



高機能であるため,デスクトップアプリケーションと勘違いされてしまう
複雑なものを作るときには,有料サポートをお勧めします
アニメーションなどは勝手につけてくれる
要望が高くなってしまう
テーマをいじってまでカスタマイズするのは大変
ExtJS を使えば,初心者でも品質の高いものが作れます

今後もバージョンアップしていく予定です

ぜひ,LinkStation を買ってお試しください
複数の表示形式を切り換えるには?
範囲選択
ドラッグ&ドロップ
(「それはそうなんですが..Web アプリケーションなんですよ」)
One more thing...

Loading comments...

Please log in to add your comment.

Report abuse