Skip to content

Instantly share code, notes, and snippets.

@otakustay
Last active December 28, 2015 13:13
Show Gist options
  • Save otakustay/1226822d1daa533d0e55 to your computer and use it in GitHub Desktop.
Save otakustay/1226822d1daa533d0e55 to your computer and use it in GitHub Desktop.
yuri demo
import {Engine} form './Engine';
import {tpl} from 'text!./tpl/dialog.tpl';
let engine = new Engine('dialog');
engine.compile(tpl);
export default class Dialog extends Control {
constructor(...args) {
super(...args);
// 这个方法事实上会拿出一个继承自`engine`的模板引擎实例,用于做模板的重写
this.setTemplateEngine(engine);
}
/**
* 处理数据变化时对应的UI更新逻辑
*
* @protected
* @override
*/
initializeUIUpdate() {
// 基类实现任何属性变化时重绘
this.enableAutoRepaint();
// 有些属性变化时不需要重绘,给去掉,主要为了性能
this.supressRepaintForProperty(['left', 'top', 'initialDraggingPosition']);
// `left`和`top`这种对主元素生效的,默认的重绘逻辑无用,得自己处理,这里用FRP来做
this.uiModel.on(
'update',
({changes}) => {
if (changes.hasOwnProperty('top') || changes.hasOwnProperty('left')) {
$(this.main).css(this.uiModel.pick('left', 'top');
}
}
);
// 如果支持FRP,也可以用FRP来写
// import {hasAnyProperty} from './reactive-update';
// 筛选出变化集中包含`left`或`top`属性的事件流
// let moveStream = hasAnyChange('left', 'top');
// 把事件流注册到`uiModel`上去,添加自理函数
// moveStream.connect(
// this.uiModel,
// () => $(this.main).css(this.uiModel.pick('left', 'top'))
// );
// 如果FRP可被接受的话,更建议用FRP来实现这一块的逻辑
}
/**
* 绑定事件
*
* @protected
* @override
* @return {Object}
*/
bindEvents() {
super.bindEvents();
// 可以自己写逻辑绑定事件
//
// `query`方法会翻译选择器,其中的`#foo`会被翻译为part id,而`.foo`会被翻译为part class
//
// 所有对控件的修改都通过某个属性的变化来实现,如此处关闭对话框用的是`isVisible`属性的修改
$(this.query('.header')).on(
'mousedown',
(e) => {
this.set('isDragging', true)
this.set('initialDraggingPosition', {x: e.clientX, y: e.clientY});
let move = (e) => {
let initialPosition = this.get('initialDraggingPosition');
let diff = {
x: e.clientX - initialPosition.x,
y: e.clientY - initialPosition.y
};
// 此类临时的操作与控件的状态无关,不用修改控件的属性
${this.main}.css('transform', `translate(${diff.x}, ${diff.y})`);
};
let doc = $(document);
doc.on('mousemove', move);
doc.on(
'mouseup',
(e) => {
let initialPosition = this.get('initialDraggingPosition');
let diff = {
x: e.clientX - initialPosition.x,
y: e.clientY - initialPosition.y
};
$(this.main).css('transform', '');
// 会影响到控件状态的就要通过修改属性来实现
let command = {
initialDraggingPosition: ${set: null},
left: {$set: this.get('left') + diff.x},
top: {$set: this.get('top') + diff.y}
};
this.update(command);
}
);
}
);
$(this.query('#close')).on('click', () => this.set('isVisible', false));
// 简单的事件也可以直接返回一个对象,对象的key是事件名和选择器的组合,选择器对应`query`方法,value是一个函数
return {
'click #close': () => this.set('isVisible', false);
};
}
// 可以直接使用decorator关联事件
//
// 第一个参数是一个选择器,对应`query`方法
//
// 所有事件会绑定在主元素上,使用事件代理实现
//
// 事件可以是一个函数,也可以是一个字符串指定某个方法名
//
// 所有事件调用时的`this`是当前控件实例
@event('.button', 'click')
[Symbol()](e) {
// `e`是DOMEvent对象
this.fire('buttonclick', {tag: $(e.target).data('tag')});
}
}
<!-- target: main -->
<!-- import: header -->
<!-- import: body -->
<!-- import: foot -->
<!-- target: header -->
<header id="${id.header}" class="${class.header}">
<!-- import: headerContent -->
</header>
<!-- target: headerContent -->
<h3 id="${id.title}">
<!-- import: title -->
</h3>
<!-- import: closeButton -->
<!-- target: title -->
${title}
<!-- target: closeButton -->
<i id="${id.close}" class="ui-icon ui-icon-times ${class.close}">关闭</i>
<!-- target: body -->
<div id="${id.body}" class="${class.body}">
<!-- import: bodyContent -->
</div>
<!-- target: bodyContent -->
<!-- if: ${encodeBody} -->
${content}
<!-- else -->
${content|raw}
<!-- /if -->
<!-- target: foot -->
<!-- if: ${hasFoot}
<footer id="${id.foot}" class="${class.foot}">
<!-- import: footContent -->
</footer>
<!-- /if -->
<!-- target: footContent -->
<!-- if: ${footContent} -->
<!-- if: ${encodeFoot} -->
${footContent}
<!-- else -->
${footContent|raw}
<!-- /if -->
<!-- elif: ${buttons} -->
<!-- for: ${buttons} as ${button} -->
<!-- use: button(button = ${button}) -->
<!-- /for -->
<!-- /if -->
<!-- target: button -->
<button type="button" class="${class.button} ${class.button}-${button.tag}" data-tag="${button.tag}">${button.text}</button>
<!-- 没想好怎么解决子控件问题 -->
<!-- <esui-button type="button" variants="${button.tag}" text="${button.text}"></esui-button> -->
<!-- {{foo}}为JavaScript表达式 -->
<!-- {=foo}为双向绑定 -->
<!-- {-foo}为单向绑定 -->
<!-- {foo}为一次性绑定 -->
<esui-dialog id="warning" is-visible="{{false}}" title="{=warnTitle}" content="{=warnMessage}">
<!-- 重写掉title这个target -->
<script type="text/yuri-template" for="title">
[警告]${title}
</script>
</eusi-dialog>
<script>
require(
['emc/Model', 'esui'],
function (Model, esui) {
var model = new Model();
model.set('warnTitle', '你完了');
model.set('warnMessage', '你真的完了');
esui.render(document.body, model);
model.set('warnTitle', '好像得救了');
}
);
</script>
<script>
require(
['emc/Model', 'esui/Dialog'],
function (Model, Dialog) {
var model = new Model();
model.set('warnTitle', '你完了');
model.set('message', '你真的完了');
var options = {
// `config`表示一次性的值,直接给出值,没有绑定功能
config: {
hasFoot: false
},
// 如果`autoBinding`为`true`,则默认按同名原则进行**双向**绑定,默认是`true`的
autoBinding: true,
// `bindings`表示绑定,在`autoBinding`的基础上可以额外加一些映射,与HTML的方式相同
bindings: {
title: '=warnTitle'
}
}
var dialog = new Dialog(model, options);
dialog.appendTo('body');
// 通过操作控件属性变化其UI
dialog.set('title', '好像得救了');
// 会获取`"好像得救了"`,因为是双向绑定
console.log(model.get('warnTitle'));
// 同样会改变控件的UI,不推荐两种方式混用
model.set('message', '快跑吧');
}
);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment