0


Semantic UI带你飞——Modal

Semantic UI是一个逼格很高的前端框架,用了他,可以让你随时啪啪打设计经理、产品经理的脸。

但Semantic UI也是一个上手难度非常高的框架,尤其是他零散的className,而且这些className各自又能互相组合使用,所以,没有一定的耐性,可能会觉得他是一个巨啰嗦的框架。

虽然,Semantic UI也提供了很多Module View的套路样式封装,然而他的思想和Bootstrap、UIKit等完全不是一路的。这些套路都是又他的基本元素构造而成,你既可以单独将这些元素抽取出来用,也可以将不同的套路混搭使用,呃,真是五花八门。

最大的区别是,Semantic UI对空间、距离、字号,是使用em来处理的,这也是我2年前会看上他的一个重要因素。所以,本质上说,Semantic UI是通过字号来控制空间和距离的。当然其实你具体的去重载他的className,用具体的px,也是可以的。

好吧,多的就不啰嗦了,回到主题,Modal。

弹出层,是一个经常使用的东西,Semantic UI的Modal,样式做得很精致,包括弹出、消失的过度过程也非常流畅、充分,唯独有一样东西不好,就是控制太费解了,我看了老半天,都没闹明白他丫到底要干嘛,比如以下这个官网的例子:

$('.coupled.modal')
  .modal({
    allowMultiple: true
  })
;
// open second modal on first modal buttons
$('.second.modal')
  .modal('attach events', '.first.modal .button')
;// show first immediately
$('.first.modal')
  .modal('show')
;

attach events,好吧,我觉得他的设想可能很好,可是现实中应该不会有人去这么用吧。Modal的按钮点击,一般会直接跟具体的业务操作,比如发起ajax,操作别的什么元素之类的。虽然这里你可以附加一个闭包函数,可是我想吐槽的是,你能不用字符串做为接口或者属性名吗?这样真的让事情变复杂了。好吧我知道你还有API,不过我想说那个也让问题变得好好复杂。

官网的接口文档也没有直接给出如何能直接调用到Modal的实例的方法,只给了几个简单的配置参数,和modal方法访问的常用关键字字符……呃,实在忍不住了,扒拉了一下他的modal.js的源代码,呃,其实问题本来可以很简单,源代码封装得好好的,为啥就不把接口暴露出来呢?你也是够了,故作简易。

简单说,通过jQuery捕获到任意DOM对象,执行modal方法,可以理解为对这个modal进行初始化。

$('#hello_world').modal({
    // ...
});

只要你不调用modal(‘show’),他是不会乱来的。

那么如何取得他内部创建的这个Modal实例呢?其实他有一个默认的namespace,以module为前缀,modal访问名是:module-modal:

var modal = $('#hello_world').data('module-modal');
console.log(modal);

输出一下这个实例看看,其实里面的方法很完善,封装的好极了,可是为什么就是不暴露出来呢?

对于一般的modal的操作,只要知道:

modal.showModal(callback);
modal.hideModal(callback, keepDimmer);
modal.is.active();

其他的方法,说有用也有用,但上层应用一般也不会直接用到了。而他的Modal操作,我个人的看法是,就没必要用他的了,实在不好理解——关键是项目执行时间内,也没时间去理解他为啥要那么蹩脚。

所以,有了以下这么一个简单粗暴的封装:

define('module/UI', ['jquery', 'lodash'], function ($, _) {

   var Modal = function (options) {
      this.options = {
         prefix: 'modal_box_',
         namespace: 'semantic-modal',
         closable: true,
         cls: null,
         // default message
         icon: '',
         header: '',
         content: '',
         actions: {}
      };
      if (_.isObject(options))
         _.merge(this.options, options);
      this.id = _.uniqueId(this.options.prefix);
      this.body = false;
      this.modal = false;
      this.buttons = {};
      this.bodyActionsBox = null;
   };

   Modal.prototype.hasBody = function () {
      if (this.body !== false)
         return true;
      var el = document.getElementById(this.id);
      if (el) {
         this.body = $(el);
         return true;
      }
      return false;
   };

   Modal.prototype.initBody = function () {
      if ($('#' + this.id).get(0))
         return this;
      var closeIcon = !!this.options.closable ? '<i class="close icon"></i>' : '';
      this.body = $('<div id="' + this.id + '" class="ui modal">' + closeIcon + '<div class="header"><i class="icon"></i><span class="inner"></span></div><div class="content"></div></div>');
      this.body.modal(this.options);
      if (this.options.cls)
         this.body.addClass(this.options.cls);
      this.initActions(this.options.actions);
      // 先在内存中把整个modal初始化完成了,这样可以不用牺牲页面渲染的性能
      $('body').append(this.body);
      return this;
   };

   Modal.prototype.getBody = function () {
      if (!this.hasBody())
         this.initBody();
      return this.body;
   };

   Modal.prototype.getModal = function () {
      if (!this.modal) {
         this.modal = this.getBody().data('module-' + this.options.namespace);
      }
      return this.modal;
   };

   Modal.prototype.hasActions = function () {
      return !!this.bodyActionsBox;
   };

   Modal.prototype.removeActions = function () {
      if (this.bodyActionsBox) {
         this.bodyActionsBox.remove();
         this.bodyActionsBox = false;
      }
      return this;
   };

   Modal.prototype.initActions = function (actions) {
      if (_.size(actions) <= 0)
         return this.removeActions();
      var me = this;
      this.bodyActionsBox = $('<div class="actions"></div>');
      _.forOwn(actions, function (action, key) {
         me.initAction(key, action);
      });
      this.body.append(this.bodyActionsBox);
      return this;
   };

   Modal.prototype.initAction = function (name, action) {
      action = _.merge({
         text: null,
         cls: null,
         icon: null,
         click: null
      }, action || {});
      var me = this
         , text = action.text
         , icon = action.icon
         , cls = action.cls || '';
      if (!_.isString(text) || text === '')
         text = name;
      if (_.isString(icon) && icon !== '') {
         cls += ' icon labeled';
         text += '<i class="icon ' + icon + '"></i>';
      }
      var button = this.buttons[name] = $('<button type="button" class="ui button ' + cls + '">' + text + '</button>');
      button.prop('action', name);
      button.click(function () {
         me.action(button.prop('action'));
      });
      me.on(name, action.click);
      this.bodyActionsBox.append(button);
      return this;
   };

   Modal.prototype.on = function (event, fn, isOne) {
      var me = this, $me = $(this), handle = !!isOne ? $me.one : $me.on;
      if (_.isString(event)) {
         if (_.isFunction(fn))
            handle.call($me, event, fn);
         else if (_.isArray(fn) && fn.length > 0) {
            _.each(fn, function (item) {
               me.on(event, item, isOne);
            });
         }
      }
      else if (_.isObject(event)) {
         _.forOwn(event, function (value, name) {
            me.on(name, value, isOne);
         });
      }
      return this;
   };

   Modal.prototype.one = function (event, fn) {
      return this.on(event, fn, true);
   };

   Modal.prototype.action = function (name) {
      var isBreak = $(this).triggerHandler(name);
      if (isBreak !== false)
         this.hide();
      return this;
   };

   Modal.prototype.isShow = function () {
      return this.getModal().is.active();
   };

   Modal.prototype.isHide = function () {
      return !this.isShow();
   };

   Modal.prototype.show = function (message) {
      if (_.isFunction(message))
         message = {then: message};
      else if (typeof message !== 'undefined' && (_.isString(message) || _.isObject(message)))
         this.setMessage(message);
      if (this.isHide())
         this.getModal().showModal(message && message.then);
      return this;
   };

   Modal.prototype.hide = function (then, keepDimmer) {
      var me = this;
      if (this.isShow()) {
         this.getModal().hideModal(then, keepDimmer);
      }
      return this;
   };

   Modal.prototype.setMessage = function (message) {
      if (_.isString(message))
         message = {content: message};
      this.setIcon(message.icon || null);
      this.setHeader(message.header || null);
      this.setContent(message.content || null);
      if (message.actions)
         this.on(message.actions, null, true);
      return this;
   };

   Modal.prototype.setIcon = function (icon) {
      var item = this.getBody().find('.header .icon');
      if (!icon || icon == '')
         icon = this.options.icon;
      if (!icon || icon == '')
         item.css('display', 'none');
      else {
         item.css('display', '');
         item.addClass(icon);
      }
      return this;
   };

   Modal.prototype.setHeader = function (content) {
      var item = this.getBody().find('.header .inner');
      if (!content || content.length == '')
         content = this.options.header;
      item.html(content);
      return this;
   };

   Modal.prototype.setContent = function (content) {
      var item = this.getBody().find('.content');
      if (!content || content.length == '')
         content = this.options.content;
      item.html(content);
      return this;
   };

   var globalModals = {};

   var stdModalOptions = [
      {
         access: 'alert',
         closable: false,
         cls: 'small',
         icon: 'comment outline',
         header: '消息提示',
         actions: {
            'ok': {
               text: '好的',
               cls: 'green right',
               icon: 'checkmark'
            }
         }
      },
      {
         access: 'confirm',
         closable: false,
         cls: 'small',
         icon: 'warning',
         header: '请确认',
         content: '你确定吗?',
         actions: {
            'yes': {
               text: '我知道了',
               cls: 'green',
               icon: 'checkmark'
            },
            'no': {
               text: '容我想想',
               cls: 'red',
               icon: 'remove'
            }
         }
      }
   ];

   var exports = {};

   exports.ModalClass = Modal;

   var getModal = exports.modal = function (id, options) {
      if (!globalModals[id])
         globalModals[id] = new Modal(options);
      return globalModals[id];
   };

   _.each(stdModalOptions, function(options) {
      var name = options.access;
      exports[name] = function (message, show) {
         var modal = getModal(name, options).setMessage(message);
         if (typeof show === 'undefined')
            show = true;
         if (!!show)
            modal.show(show);
         return modal;
      }
   });

   return exports;
});

他依赖于jQuery和lodash作为基本。

首先,这个封装,公布了两个标准的Modal,分别为alert和confirm

UI.alert('你好');
UI.alert({
    content: '你不被授权访问以下页面',
    // ok为alert默认的action
    actions: {
        ok: function(event) {
            return false; // 将中断隐藏的操作
        }
    }
});
UI.confirm({
    icon: 'info', // 换一个标题如何
    header: '这是标题',
    content: '这是内容',
    // contrim默认的actions
    actions: {
        yes: [
            function(event) {
            },
            function(event) {
            }
        ],
        no: function(event) {
        }
    }
})
// 不直接显示
UI.confirm('你确定吗?', false).show();
// 绑定自己的action
UI.confirm('你确定吗?', false).on('yes', function() {
    return false;
}).show();

其次,暴露出ModalClass,和modal方法,通过ModalCalss你可以创建自己的modal实例,并且自行对实例进行管理。而modal则是实例托管的调用。

// my_modal是全局访问的名称
// 第二个参数是配置
UI.modal('my_modal', {
   closable: true, // 是否可任意关闭
   cls: 'large', // 给modal附加的class
   icon: 'warning', // 默认的header的icon
   header: '请确认', // 默认的header内容
   content: '你确定吗?', // 默认的content内容
   actions: {
      'confirm': {
         text: '确认', // 按钮显示文字
         cls: 'green', // 按钮附加样式
         icon: 'checkmark' // 按钮的icon
         click: function() { // 绑定初始的事件
         }
      },
      'wait': {
         text: '容我想想',
         cls: 'red',
         icon: 'remove'
      }
   }
});
// 调用实例
UI.modal('my_modal').show();

如果你已经注册了某个Modal,想直接执行他的某个action,可以:

UI.modal('my_modal').action('confirm');

这个函数实际上就是触发事件。

嗯,其实以前使用UIKit的时候,封装过一个类似的,除了上述的操作,还有一个自动加载Ajax或者图片的需求,不过暂时够用了。

忘记说了,Semantic的官网来说,从他的例子来看,是把多个Modal写好,输出在HTML页面中,然后直接捕获这个元素执行modal方法。不过我想说,真的够了,谁没事为了弹个提示写那么多html呢。所以这个Modal的封装,就不需要在页面写个Modal了,直接调用就可以了。

作者:曾建凯

原文地址:https://my.oschina.net/janpoem/blog/626219

喜欢  0   
标签: ui

本文转载自: https://blog.csdn.net/weixin_42363501/article/details/135959389
版权归原作者 楽月 所有, 如有侵权,请联系我们删除。

“Semantic UI带你飞——Modal”的评论:

还没有评论