LoginSignup
8
2

More than 1 year has passed since last update.

kintone ポータルイベントが追加されたので、スペースイベント作ってみた。

Last updated at Posted at 2019-08-05

2021/4 のアップデートからスペース表示イベントが追加されているのでこちらを使用しましょう。

:gear: 2019年7月アップデート

kintone 7月版からポータルイベントポータルの要素を取得する JS API が追加されました。

詳細:https://developer.cybozu.io/hc/ja/articles/360028388291

:gear: スペースのポータルには使えない…

pose_ochikomu_businessman.png

https://{subdomain}.cybozu.com/k/#/portal
に対して有効な JS API ですが、スペースのポータルでは発火しません。
Image 1.png

スペースは部署やグループごとに作成しますが、すべてのスペースの構成が一緒なので、ぱっと見どのスペースかわからなくなったりします。
チームの特色を出すために独自のポータルが欲しくなります。
Image 2.png

:gear: 本題

ポータルイベントはうれしい。
柔軟な kintone がさらに柔軟になり、
今まで kintone 界隈では下火だった React や Vue が使えるようになるかもしれないですね。
ただ、やっぱり…

スペースのポータルをカスタマイズしたい。

スペースカスタマイズしてやりましょう。
niyakeru_takuramu_ayashii_man.png

:gear: コード

不格好ながらポータルイベントを作成しました。
コメントとかまだ途中ですが、コメント付ける元気は今のところないのでこのまま公開します。
GitHub: https://github.com/Naoto00/kintone-space-portal

customize.js
(function() {
  'use strict';
  // スペースポータルのクラス名
  var CONFIG = {
    portalBodyId: 'contents-body-ocean', // ボディ
    ntfClass: 'gaia-argoui-page-space-show-left', // お知らせ
    threadClass: 'gaia-argoui-space-threadlistwidget', // スレッド
    appClass: 'gaia-argoui-space-applistwidget', // アプリ
    peopleClass: 'gaia-argoui-space-peoplelistwidget', // ピープル
    linkClass: 'gaia-argoui-space-relatedlinklistwidget', // 関連リンク
    spaceBodyClass: 'gaia-argoui-space-spacelayout-body', // スペースボディ
    rightBodyClass: 'gaia-argoui-page-space-show-right' // スペースの右側
  };

  window.SpaceCustomize = window.SpaceCustomize || {};

  /**
   * SpaceCustomize のインスタンス
   *
   * @param {Integer} spaceId
   * @param {String} domain
   */
  window.SpaceCustomize = function(spaceId, domain) {
    this.spaceId = spaceId;
    this.domain = domain + '.cybozu.com';
    this.spaceURL = 'https://' + this.domain + '/k/#/space/' + this.spaceId;
    this.setSpaceId = function(newID) {
      this.spaceId = newID;
    };
    this.getSpaceId = function() {
      return this.spaceId;
    };

    /**
     * イベントの定義
     *
     * @param {*} main
     * @param {*} deleteFlag
     */
    this.events = function(main, deleteFlag) {
      // ポータル body を取得
      var body = document.getElementById(CONFIG.portalBodyId);
      var self = this;
      // お知らせ、スレッド、アプリ、ピープルのウィジェットを監視
      var observer = new MutationObserver(function(MutationRecords) {
        // 各ウィジェット削除
        var $targetElm = document.getElementsByClassName(CONFIG.ntfClass);
        if (!$targetElm.length) return;
        if (deleteFlag) {
          self.deleteNtf();
          self.deleteThread();
          self.deleteApp();
          self.deletePeople();
        }
        // 関連リンクのみ描画タイミングが遅いので、別で監視
        var $bo = document.getElementsByClassName(CONFIG.rightBodyClass);
        observerLink.observe($bo[0], options);
        observer.disconnect();
      });
      // observer オプション定義
      var options = {
        childList: true
      };
      // 関連リンクのウィジェットを監視
      var observerLink = new MutationObserver(function(MutationRecord) {
        // 関連リンクウィジェット削除
        var $targetElm = document.getElementsByClassName(CONFIG.linkClass);
        if (!$targetElm.length) return;
        if (deleteFlag) {
          self.deleteLink();
        }
        // コールバック関数実行
        main();
        observerLink.disconnect();
      });

      // 対象のスペース URL で監視開始
      if (location.href === this.spaceURL) {
        observer.observe(body, options);
      }

      // ハッシュ値変更もバインド
      window.onhashchange = function() {
        if (location.href !== self.spaceURL) return;
        observer.observe(body, options);
      };
    };

    this.deleteNtf = function() {
      var $ntfDiv = document.getElementsByClassName(CONFIG.ntfClass);
      if ($ntfDiv.length) {
        $ntfDiv[0].parentNode.removeChild($ntfDiv[0]);
      }
    };
    this.getNtfWidget = function() {
      var $ntfDiv = document.getElementsByClassName(CONFIG.ntfClass);
      return $ntfDiv[0];
    };

    this.deleteThread = function() {
      var $threadDiv = document.getElementsByClassName(CONFIG.threadClass);
      if ($threadDiv.length) {
        $threadDiv[0].parentNode.removeChild($threadDiv[0]);
      }
    };
    this.getThreadWidget = function() {
      var $threadDiv = document.getElementsByClassName(CONFIG.threadClass);
      return $threadDiv[0];
    };

    this.deleteApp = function() {
      var $appDiv = document.getElementsByClassName(CONFIG.appClass);
      if ($appDiv.length) {
        $appDiv[0].parentNode.removeChild($appDiv[0]);
      }
    };
    this.getAppWidget = function() {
      var $appDiv = document.getElementsByClassName(CONFIG.appClass);
      return $appDiv[0];
    };

    this.deletePeople = function() {
      var $peopleDiv = document.getElementsByClassName(CONFIG.peopleClass);
      if ($peopleDiv.length) {
        $peopleDiv[0].parentNode.removeChild($peopleDiv[0]);
      }
    };
    this.getPeopleWidget = function() {
      var $peopleDiv = document.getElementsByClassName(CONFIG.peopleClass);
      return $peopleDiv[0];
    };

    this.deleteLink = function() {
      var $linkDiv = document.getElementsByClassName(CONFIG.linkClass);
      if ($linkDiv.length) {
        $linkDiv[0].parentNode.removeChild($linkDiv[0]);
      }
    };
    this.getLinkWidget = function() {
      var $linkDiv = document.getElementsByClassName(CONFIG.linkClass);
      return $linkDiv[0];
    };

    this.deleteAll = function() {
      this.deleteNtf();
      this.deleteThread();
      this.deleteApp();
      this.deletePeople();
      this.deleteLink();
    };

    this.getSpaceBody = function() {
      var $spaceBody = document.getElementsByClassName(CONFIG.spaceBodyClass);
      return $spaceBody[0];
    };
  };
})();

:gear: 動かしてみた

とりあえず使ってみましょう。

main.js
(function() {
  'use strict';
  // スペースID と サブドメイン
  var space = new SpaceCustomize(24, '{subdomain}');
  var domain = space.domain;
  var currentURL = location.href;

  // アプリの画面で読み込まないように変更
  if (!(currentURL.indexOf('https://' + domain + '/k/#/') === 0 || currentURL === 'https://' + domain + '/k/')) {
    return;
  }

  space.events(function() {
    console.log(space.getSpaceBody());
    alert('Hello Portal!');
  }, true);

})();

Image 3.png

やったーーー!!!!!!

:gear: 使い方

導入

まずは customize.js を全体 JS に適用しましょう。
次に main.js 、実際にイベント内の処理を記述するファイルでは、以下のように宣言します。
var space = new SpaceCustomize(24, '{subdomain}');
スペース ID とサブドメイン名を入れてあげるとイベントが使えるようになります。
一応汎用的に作ったので、どのスペースでも使えるかと。

イベント発火

kintone.events.on() みたいな感じで使います。
space.events(callback, true) でポータル描画後にコールバックが動きます。
第二引数は booleanで、true の場合、ポータルのお知らせなどのウィジェットがすべて消えます。
デフォルトは false です

ポータルの要素取得

space.getSpaceBody()
で ボディの Node が返ってきます。
お好きなポータルを作りましょう。

各ウィジェットの取得

ウィジェットそのままに、中身を DOM りたい人は以下使ってください。
・お知らせウィジェット
space.getNtfWidget()
・スレッドウィジェット
space.getThreadWidget()
・アプリウィジェット
space.getAppWidget()
・ピープルウィジェット
space.getPeopleWidget()
・関連リンクウィジェット
space.getLinkWidget()

:gear: まとめ

これでスペースのカスタマイズが捗りますね。

大変だったこと

通知画面のカスタマイズで、kintone の要素は JS によって描画されるということは理解していたので、初めは順調でした。
ただ、関連リンクのウィジェットだけほかのウィジェットと描画タイミングが違い、イベント発火のタイミングが難しかったです。。。
(一応ウィジェット消す関数も用意していますが、使用するとこれのせいで若干要素が描画されてしまいます。
第二引数に true で全消ししてくれれば描画されることはないはずです。)
あと、前回は jQuery 使っていましたが、今回はネイティブの JS のみで実装しました。
これもなかなか面倒だった。。。

何かバグとかあったらコメントか GitHub までお願いします。

ありがとうございました!

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2