mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
migrate modification list to bindings
This commit is contained in:
parent
8c05990e8d
commit
4af15290fc
6 changed files with 163 additions and 41 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
.DS_Store
|
||||
*.class
|
||||
/target/
|
||||
/out/
|
||||
|
|
|
|||
|
|
@ -1,26 +1,144 @@
|
|||
import {sprintf} from 'sprintf'
|
||||
|
||||
export function Bind(dom, data, policy) {
|
||||
export const BINDING_CALLBACK = 'OnBind';
|
||||
|
||||
export function Bind(node, data, policy) {
|
||||
if (!policy) policy = DEFAULT_POLICY;
|
||||
const props = Object.getOwnPropertyNames(data);
|
||||
const scope = getScope(node);
|
||||
for (let prop of props) {
|
||||
const node = $(dom).find('[data-bind="'+prop+'"]');
|
||||
if (node.length == 0) continue;
|
||||
if (prop == BINDING_CALLBACK) continue;
|
||||
let value = data[prop];
|
||||
if (!policy.hideEmptyValue || value || value === 0) {
|
||||
var format = node.attr('bind-format');
|
||||
if (format) {
|
||||
value = sprintf(format, value);
|
||||
}
|
||||
node.text(value);
|
||||
node.show();
|
||||
let bindFunc, subNode;
|
||||
if (Array.isArray(value)) {
|
||||
bindFunc = BindArray;
|
||||
subNode = scope.nestedScopes[prop];
|
||||
} else if (typeof value === 'object') {
|
||||
bindFunc = Bind;
|
||||
subNode = scope.nestedScopes[prop];
|
||||
} else {
|
||||
node.text('');
|
||||
node.hide();
|
||||
bindFunc = BindContent;
|
||||
subNode = scope.bindings[prop];
|
||||
}
|
||||
if (!subNode) continue;
|
||||
bindFunc(subNode, value, policy);
|
||||
}
|
||||
var callback = data[BINDING_CALLBACK];
|
||||
if (callback) {
|
||||
callback(node, data, policy)
|
||||
}
|
||||
}
|
||||
|
||||
export function BindArray(node, array, policy, path) {
|
||||
let template = node.data("BindingTemplate");
|
||||
if (!template) {
|
||||
template = node.children();
|
||||
template.detach();
|
||||
node.data("BindingTemplate", template);
|
||||
}
|
||||
let scope = getScope(node);
|
||||
node.children().detach();
|
||||
clearScope(node);
|
||||
|
||||
for (let value of array) {
|
||||
let child = scope.nestedScopes[value.id];
|
||||
if (!child) {
|
||||
child = template.clone();
|
||||
} else {
|
||||
delete scope.nestedScopes[value.id];
|
||||
}
|
||||
child.attr('data-bind-scope', value.id);
|
||||
Bind(child, value, policy);
|
||||
node.append(child);
|
||||
}
|
||||
for (let toDelete of Object.getOwnPropertyNames(scope.nestedScopes)) {
|
||||
scope.nestedScopes[toDelete].remove();
|
||||
}
|
||||
}
|
||||
|
||||
export function BindContent(node, value, policy) {
|
||||
if (!policy.hideEmptyValue || value || value === 0) {
|
||||
var format = node.attr('data-bind-format');
|
||||
if (format == '') {
|
||||
format = node.text();
|
||||
node.attr('data-bind-format', format);
|
||||
}
|
||||
if (format) {
|
||||
value = sprintf(format, value);
|
||||
}
|
||||
node.text(value);
|
||||
node.show();
|
||||
} else {
|
||||
node.text('');
|
||||
node.hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function clearScope(dom) {
|
||||
dom.removeData('BindingScope');
|
||||
}
|
||||
|
||||
function getScope(dom) {
|
||||
let scope = dom.data('BindingScope');
|
||||
if (!scope) {
|
||||
scope = index(dom);
|
||||
dom.data('BindingScope', scope);
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
function index(dom) {
|
||||
const scope = new Scope();
|
||||
//do bfs
|
||||
const queue = [];
|
||||
function advance(node) {
|
||||
var binding = node.attr('data-bind');
|
||||
if (binding) {
|
||||
scope.bindings[binding] = node;
|
||||
}
|
||||
node.children().each((i, e) => queue.push($(e)))
|
||||
}
|
||||
advance(dom);
|
||||
while (queue.length != 0) {
|
||||
let node = queue.shift();
|
||||
var nestedScope = node.attr('data-bind-scope');
|
||||
if (nestedScope) {
|
||||
scope.nestedScopes[nestedScope] = node;
|
||||
} else {
|
||||
advance(node);
|
||||
}
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
const DEFAULT_POLICY = {
|
||||
hideEmptyValue: true
|
||||
};
|
||||
|
||||
export function Scope() {
|
||||
this.bindings = {};
|
||||
this.nestedScopes = {};
|
||||
}
|
||||
|
||||
function example(dom) {
|
||||
let initState = {
|
||||
title : 'this is title',
|
||||
users : [
|
||||
{id: 1, name: 'Peach', email: 'Peach@ooo.com'},
|
||||
{id: 2, name: 'Melon', email: 'Melon@ooo.com'},
|
||||
{id: 3, name: 'Berry', email: 'Berry@ooo.com'},
|
||||
{id: 4, name: 'Apple', email: 'Apple@ooo.com'},
|
||||
{id: 5, name: 'Banana', email: 'Banana@ooo.com'}
|
||||
]
|
||||
};
|
||||
|
||||
Bind(dom, initState);
|
||||
//reordering, removing, updating provided attributes
|
||||
Bind(dom, {users: [ {id:3}, {id:1, name: 'Peach-Beach'}, {id:2} ]});
|
||||
//only content update
|
||||
Bind(dom, {users: {
|
||||
'3' : {name: 'updated', email: 'light@update.com'}
|
||||
}});
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ import {PlaneWizard} from '../wizards/plane'
|
|||
import {BoxWizard} from '../wizards/box'
|
||||
import {SphereWizard} from '../wizards/sphere'
|
||||
import {TransformWizard} from '../wizards/transform'
|
||||
import {LoadTemplate} from './utils'
|
||||
import {BindArray} from './bind'
|
||||
|
||||
function UI(app) {
|
||||
this.app = app;
|
||||
|
|
@ -20,10 +22,11 @@ function UI(app) {
|
|||
$('#right-panel').append(mainBox.root);
|
||||
var modelFolder = new tk.Folder("Model");
|
||||
var modificationsFolder = new tk.Folder("Modifications");
|
||||
var modificationsDom = $(LoadTemplate('modifications')({}));
|
||||
|
||||
tk.add(mainBox, modelFolder);
|
||||
tk.add(mainBox, modificationsFolder);
|
||||
var modificationsListComp = new tk.List();
|
||||
tk.add(modificationsFolder, modificationsListComp);
|
||||
modificationsFolder.content.append(modificationsDom);
|
||||
|
||||
var toolbarVertOffset = 10; //this.mainBox.root.position().top;
|
||||
|
||||
|
|
@ -51,7 +54,7 @@ function UI(app) {
|
|||
var craft = ui.app.craft;
|
||||
var historyEditMode = craft.historyPointer != craft.history.length;
|
||||
if (historyEditMode) {
|
||||
var rows = modificationsListComp.root.find('.tc-row');
|
||||
var rows = modificationsDom.find('.tc-row');
|
||||
rows.removeClass('history-selected');
|
||||
rows.eq(craft.historyPointer).addClass('history-selected');
|
||||
var op = craft.history[craft.historyPointer];
|
||||
|
|
@ -63,20 +66,20 @@ function UI(app) {
|
|||
}
|
||||
|
||||
this.app.bus.subscribe("craft", function() {
|
||||
modificationsListComp.root.empty();
|
||||
for (var i = 0; i < app.craft.history.length; i++) {
|
||||
var op = app.craft.history[i];
|
||||
var row = modificationsListComp.addRow(ui.getInfoForOp(op));
|
||||
var icon = UI.getIconForOp(op);
|
||||
if (icon != null) {
|
||||
tk.List.setIconForRow(row, icon);
|
||||
}
|
||||
(function(i) {
|
||||
row.click(function () {
|
||||
ui.app.craft.historyPointer = i;
|
||||
})
|
||||
})(i);
|
||||
let modifications = [];
|
||||
for (let i = 0; i < app.craft.history.length; i++) {
|
||||
let op = app.craft.history[i];
|
||||
let m = {
|
||||
id : i,
|
||||
info: ui.getInfoForOp(op),
|
||||
OnBind : (dom, data) => {
|
||||
dom.css('background-image', 'url('+ UI.getIconForOp(op)+')');
|
||||
dom.click(() => ui.app.craft.historyPointer = data.id);
|
||||
}
|
||||
};
|
||||
modifications.push(m);
|
||||
}
|
||||
BindArray(modificationsDom, modifications);
|
||||
updateHistoryPointer();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="action-info">
|
||||
<div class="action-info-hint" data-bind="hint"></div>
|
||||
<div class="action-info-info" data-bind="info"></div>
|
||||
<div class="action-info-hotkey" data-bind="hotKey" bind-format="hotkey: %s"></div>
|
||||
<div class="action-info-hotkey" data-bind="hotKey" data-bind-format>hotkey: %s</div>
|
||||
</div>
|
||||
|
|
@ -172,15 +172,12 @@ export function ButtonRow(captions, actions) {
|
|||
}
|
||||
|
||||
export function List() {
|
||||
this.root = $('<div/>', {'class': 'tc-tree'});
|
||||
this.root = $('<div/>', {'class': 'tc-list'});
|
||||
}
|
||||
|
||||
List.prototype.addRow = function(name) {
|
||||
var row = $('<div/>', {
|
||||
text: name, 'class': 'tc-row tc-pseudo-btn',
|
||||
css: {
|
||||
'margin-left': '10px'
|
||||
}
|
||||
text: name, 'class': 'tc-row tc-pseudo-btn'
|
||||
});
|
||||
this.root.append(row);
|
||||
return row;
|
||||
|
|
@ -188,12 +185,7 @@ List.prototype.addRow = function(name) {
|
|||
|
||||
List.setIconForRow = function(row, icon) {
|
||||
row.css({
|
||||
'background-image' : 'url('+icon+')',
|
||||
'background-position-y': 'center',
|
||||
'background-position-x': '5px',
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-size': '16px 16px',
|
||||
'padding-left' : '25px'
|
||||
'background-image' : 'url('+icon+')'
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -176,3 +176,11 @@
|
|||
color: #aaa;
|
||||
background-color: #888;
|
||||
}
|
||||
|
||||
.tc-list .tc-row {
|
||||
background-position-y: center;
|
||||
background-position-x: 5px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 16px 16px;
|
||||
padding-left : 25px;
|
||||
}
|
||||
Loading…
Reference in a new issue