Merge branch 'master' into feature-conditions
This commit is contained in:
@@ -24,6 +24,10 @@
|
||||
<iron-icon icon="settings" item-icon></iron-icon>
|
||||
Settings
|
||||
</paper-icon-item>
|
||||
<paper-icon-item id="characterExport">
|
||||
<iron-icon icon="content-copy" item-icon></iron-icon>
|
||||
Export to Improved Initiative
|
||||
</paper-icon-item>
|
||||
</paper-menu>
|
||||
</paper-menu-button>
|
||||
{{/if}}
|
||||
|
||||
@@ -203,4 +203,11 @@ Template.characterSheet.events({
|
||||
element: event.currentTarget.parentElement.parentElement,
|
||||
});
|
||||
},
|
||||
"click #characterExport": function(event, instance){
|
||||
pushDialogStack({
|
||||
data: this,
|
||||
template: "exportDialog",
|
||||
element: event.currentTarget.parentElement.parentElement,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -20,26 +20,28 @@
|
||||
<div class="effectEdit layout horizontal flex">
|
||||
<dicecloud-selector class="statMenu flex" selected={{stat}} selectable="paper-item" style="height: 100%; overflow-y: auto;">
|
||||
{{#each statGroups}}
|
||||
<div style="font-weight: bold; margin-top: 16px; padding-left: 8px;">
|
||||
<div class="statGroupTitle clickable" style="font-weight: bold; margin-top: 16px; padding-left: 8px;">
|
||||
{{this}}
|
||||
</div>
|
||||
{{#each stats}}
|
||||
<paper-item name={{stat}}>{{name}}</paper-item>
|
||||
{{/each}}
|
||||
<iron-collapse opened={{isGroupSelected this ../stat}}>
|
||||
{{#each stats}}
|
||||
<paper-item name={{stat}} class="clickable">{{name}}</paper-item>
|
||||
{{/each}}
|
||||
</iron-collapse>
|
||||
{{/each}}
|
||||
</dicecloud-selector>
|
||||
{{#if operations}}
|
||||
<dicecloud-selector class="operationMenu flex" selected={{operation}} style="height: 100%; overflow-y: auto;">
|
||||
{{#each operations}}
|
||||
<paper-item name={{operation}}>{{name}}</paper-item>
|
||||
<paper-item name={{operation}} class="clickable">{{name}}</paper-item>
|
||||
{{/each}}
|
||||
</dicecloud-selector>
|
||||
{{else}} {{#if showMultiplierOperations}}
|
||||
<dicecloud-selector class="multiplierMenu flex"
|
||||
selected={{value}}>
|
||||
<paper-item name="0.5">Resistance</paper-item>
|
||||
<paper-item name="2">Vulnerability</paper-item>
|
||||
<paper-item name="0">Immunity</paper-item>
|
||||
<paper-item name="0.5" class="clickable">Resistance</paper-item>
|
||||
<paper-item name="2" class="clickable">Vulnerability</paper-item>
|
||||
<paper-item name="0" class="clickable">Immunity</paper-item>
|
||||
</dicecloud-selector>
|
||||
{{else}}
|
||||
<div class="flex" style="height: 100%;"></div>
|
||||
|
||||
@@ -149,47 +149,71 @@ Template.effectEdit.helpers({
|
||||
effectValue: function(){
|
||||
return this.calculation || this.value;
|
||||
},
|
||||
isGroupSelected: function(groupName, statName){
|
||||
var stat = statsDict[statName]
|
||||
return stat && (stat.group === groupName);
|
||||
},
|
||||
});
|
||||
|
||||
var setStat = function(statName, effectId){
|
||||
var setter = {stat: statName};
|
||||
var effect = Effects.findOne(this._id);
|
||||
var group = statsDict[statName].group;
|
||||
if (group === "Saving Throws" || group === "Skills"){
|
||||
// Skills must have a valid skill operation
|
||||
if (!_.contains(
|
||||
_.map(skillOperations, ao => ao.operation),
|
||||
effect.operation
|
||||
)){
|
||||
setter.operation = "add";
|
||||
}
|
||||
} else if (group !== "Weakness/Resistance"){
|
||||
// Attributes must have a valid attribute operation
|
||||
if (!_.contains(
|
||||
_.map(attributeOperations, ao => ao.operation),
|
||||
effect.operation
|
||||
)){
|
||||
setter.operation = "base";
|
||||
}
|
||||
} else {
|
||||
// Weakness/Resistance must have a mul operation and value
|
||||
if (effect.operation !== "mul"){
|
||||
setter.operation = "mul";
|
||||
}
|
||||
if (!_.contains([0, 0.5, 2], effect.value)){
|
||||
setter.value = 0.5;
|
||||
}
|
||||
}
|
||||
Effects.update(effectId, {$set: setter});
|
||||
};
|
||||
|
||||
var scrollAnimationId;
|
||||
var scrollElementToView = element => {
|
||||
var scrollFunction = function(){
|
||||
element.scrollIntoView();
|
||||
scrollAnimationId = requestAnimationFrame(scrollFunction);
|
||||
};
|
||||
return scrollFunction;
|
||||
}
|
||||
|
||||
Template.effectEdit.events({
|
||||
"click #deleteButton": function(event, instance){
|
||||
Effects.softRemoveNode(instance.data.id);
|
||||
GlobalUI.deletedToast(instance.data.id, "Effects", "Effect");
|
||||
popDialogStack();
|
||||
},
|
||||
"click .statGroupTitle": function(event, instance){
|
||||
var groupName = this.toString();
|
||||
var firstStat = statGroups[groupName][0].stat;
|
||||
setStat(firstStat, instance.data.id);
|
||||
scrollAnimationId = requestAnimationFrame(scrollElementToView(event.target));
|
||||
_.delay(() => cancelAnimationFrame(scrollAnimationId), 300);
|
||||
},
|
||||
"iron-select .statMenu": function(event){
|
||||
var detail = event.originalEvent.detail;
|
||||
var statName = detail.item.getAttribute("name");
|
||||
if (statName == this.stat) return;
|
||||
var setter = {stat: statName};
|
||||
var group = Blaze.getData(detail.item).group;
|
||||
var effect = Effects.findOne(this._id);
|
||||
if (group === "Saving Throws" || group === "Skills"){
|
||||
// Skills must have a valid skill operation
|
||||
if (!_.contains(
|
||||
_.map(skillOperations, ao => ao.operation),
|
||||
effect.operation
|
||||
)){
|
||||
setter.operation = "add";
|
||||
}
|
||||
} else if (group !== "Weakness/Resistance"){
|
||||
// Attributes must have a valid attribute operation
|
||||
if (!_.contains(
|
||||
_.map(attributeOperations, ao => ao.operation),
|
||||
effect.operation
|
||||
)){
|
||||
setter.operation = "base";
|
||||
}
|
||||
} else {
|
||||
// Weakness/Resistance must have a mul operation and value
|
||||
if (effect.operation !== "mul"){
|
||||
setter.operation = "mul";
|
||||
}
|
||||
if (!_.contains([0, 0.5, 2], effect.value)){
|
||||
setter.value = 0.5;
|
||||
}
|
||||
}
|
||||
Effects.update(this._id, {$set: setter});
|
||||
setStat(statName, this._id);
|
||||
},
|
||||
"iron-select .operationMenu": function(event){
|
||||
var detail = event.originalEvent.detail;
|
||||
|
||||
@@ -33,7 +33,7 @@ Template.effectsEditList.events({
|
||||
template: "effectEdit",
|
||||
data: {id: effectId},
|
||||
element: event.currentTarget,
|
||||
returnElement: instance.find(`tr.effect[data-id='${effectId}']`),
|
||||
returnElement: () => instance.find(`tr.effect[data-id='${effectId}']`),
|
||||
});
|
||||
},
|
||||
"tap .edit-effect": function(event, template){
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.exportDialog .iiexport {
|
||||
overflow-y: auto;
|
||||
width: 100% !important;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<template name="exportDialog">
|
||||
<div class="exportDialog fit layout vertical">
|
||||
{{#with character}}
|
||||
<app-header fixed effects="waterfall">
|
||||
<app-toolbar>
|
||||
<div main-title>Export Character to Improved Initiative</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div class="form flex layout vertical">
|
||||
<paper-toggle-button id="exportFeatures" checked={{settings.exportFeatures}}>
|
||||
Features
|
||||
</paper-toggle-button>
|
||||
<paper-toggle-button id="exportAttacks" checked={{settings.exportAttacks}}>
|
||||
Attacks
|
||||
</paper-toggle-button>
|
||||
<paper-toggle-button id="exportDescription" checked={{settings.exportDescription}}>
|
||||
Description
|
||||
</paper-toggle-button>
|
||||
<div class="paper-font-title padded">JSON</div>
|
||||
<textarea class="flex iiexport">{{improvedInitiativeJson}}</textarea>
|
||||
<paper-button id="copyExportButton" class="red-button" raised>
|
||||
<iron-icon icon="content-copy"></iron-icon>
|
||||
Copy to Clipboard
|
||||
</paper-button>
|
||||
</div>
|
||||
<div class="buttons layout horizontal end-justified">
|
||||
<paper-button class="doneButton"> Done </paper-button>
|
||||
</div>
|
||||
{{/with}}
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,60 @@
|
||||
Template.exportDialog.helpers({
|
||||
character: function(){
|
||||
return Characters.findOne(this._id);
|
||||
},
|
||||
improvedInitiativeJson: function(){
|
||||
var options = {
|
||||
features: this.settings.exportFeatures,
|
||||
attacks: this.settings.exportAttacks,
|
||||
description: this.settings.exportDescription,
|
||||
}
|
||||
return improvedInitiativeJson(this._id, options);
|
||||
},
|
||||
});
|
||||
|
||||
Template.exportDialog.events({
|
||||
"change #exportFeatures": function(event, template){
|
||||
Characters.update(this._id, {$set: {
|
||||
"settings.exportFeatures": event.target.checked,
|
||||
}});
|
||||
},
|
||||
"change #exportAttacks": function(event, template){
|
||||
Characters.update(this._id, {$set: {
|
||||
"settings.exportAttacks": event.target.checked,
|
||||
}});
|
||||
},
|
||||
"change #exportDescription": function(event, template){
|
||||
Characters.update(this._id, {$set: {
|
||||
"settings.exportDescription": event.target.checked,
|
||||
}});
|
||||
},
|
||||
"click #copyExportButton": function(event, template){
|
||||
var copyTextarea = template.find(".iiexport");
|
||||
copyTextarea.select();
|
||||
var msg;
|
||||
try {
|
||||
var successful = document.execCommand("copy");
|
||||
var msg = successful ? "JSON copied to clipboard" : "Unable to copy JSON";
|
||||
} catch (err) {
|
||||
msg = "Unable to copy JSON";
|
||||
} finally {
|
||||
clearSelection();
|
||||
GlobalUI.toast(msg);
|
||||
}
|
||||
},
|
||||
"click .doneButton": function(event, instance){
|
||||
popDialogStack();
|
||||
},
|
||||
});
|
||||
|
||||
var clearSelection = function(){
|
||||
if (window.getSelection) {
|
||||
if (window.getSelection().empty) { // Chrome
|
||||
window.getSelection().empty();
|
||||
} else if (window.getSelection().removeAllRanges) { // Firefox
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
} else if (document.selection) { // IE?
|
||||
document.selection.empty();
|
||||
}
|
||||
};
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
|
||||
<div>{{#markdown}}{{evaluateSpellString charId parent.id description}}{{/markdown}}</div>
|
||||
{{> attacksViewList charId=charId parentId=_id}}
|
||||
</template>
|
||||
|
||||
|
||||
@@ -61,6 +61,18 @@
|
||||
<td>Total</td>
|
||||
<td>{{characterCalculate "skillMod" charId skillName}}</td>
|
||||
</tr>
|
||||
{{#each passiveEffects}}
|
||||
<tr>
|
||||
<td>{{sourceName}}</td>
|
||||
<td>Passive Bonus: {{statValue}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{#if showPassiveTotal}}
|
||||
<tr class="paper-font-body2">
|
||||
<td>Passive Score</td>
|
||||
<td>{{characterCalculate "passiveSkill" charId skillName}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ Template.skillDialogView.helpers({
|
||||
var char = Characters.findOne(this.charId);
|
||||
if (!char) return;
|
||||
var prof = Characters.calculate.proficiency(this.charId, this.skillName);
|
||||
var proficiencyBonus =
|
||||
var proficiencyBonus =
|
||||
Characters.calculate.attributeValue(this.charId, "proficiencyBonus");
|
||||
return prof * proficiencyBonus;
|
||||
},
|
||||
@@ -189,6 +189,23 @@ Template.skillDialogView.helpers({
|
||||
enabled: true,
|
||||
});
|
||||
},
|
||||
passiveEffects: function(){
|
||||
return Effects.find({
|
||||
charId: this.charId,
|
||||
stat: this.skillName,
|
||||
operation: "passiveAdd",
|
||||
enabled: true,
|
||||
});
|
||||
},
|
||||
showPassiveTotal: function(){
|
||||
if (this.skillName === "perception") return true;
|
||||
return Effects.find({
|
||||
charId: this.charId,
|
||||
stat: this.skillName,
|
||||
operation: "passiveAdd",
|
||||
enabled: true,
|
||||
}).count();
|
||||
},
|
||||
ability: function(){
|
||||
var opts = {fields: {}};
|
||||
opts.fields[this.skillName] = 1;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{{#if conditionalCount}}
|
||||
*
|
||||
{{/if}}
|
||||
{{#if showPassive}}
|
||||
{{#if isPassiveShown}}
|
||||
({{characterCalculate "passiveSkill" ../_id skill}})
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
@@ -38,4 +38,16 @@ Template.skillRow.helpers({
|
||||
operation: "conditional",
|
||||
}).count();
|
||||
},
|
||||
isPassiveShown: function(){
|
||||
if (this.showPassive === "forced") return true;
|
||||
if (this.showPassive === "ifNeeded"){
|
||||
var charId = Template.parentData()._id;
|
||||
return Effects.find({
|
||||
charId,
|
||||
stat: this.skill,
|
||||
operation: "passiveAdd",
|
||||
enabled: true,
|
||||
}).count();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -51,24 +51,24 @@
|
||||
Skills
|
||||
</div>
|
||||
<div flex class="bottom list">
|
||||
{{> skillRow name="Acrobatics" skill="acrobatics"}}
|
||||
{{> skillRow name="Animal Handling" skill="animalHandling"}}
|
||||
{{> skillRow name="Arcana" skill="arcana"}}
|
||||
{{> skillRow name="Athletics" skill="athletics"}}
|
||||
{{> skillRow name="Deception" skill="deception"}}
|
||||
{{> skillRow name="History" skill="history"}}
|
||||
{{> skillRow name="Insight" skill="insight"}}
|
||||
{{> skillRow name="Intimidation" skill="intimidation"}}
|
||||
{{> skillRow name="Investigation" skill="investigation"}}
|
||||
{{> skillRow name="Medicine" skill="medicine"}}
|
||||
{{> skillRow name="Nature" skill="nature"}}
|
||||
{{> skillRow name="Perception" skill="perception" showPassive="true"}}
|
||||
{{> skillRow name="Performance" skill="performance"}}
|
||||
{{> skillRow name="Persuasion" skill="persuasion"}}
|
||||
{{> skillRow name="Religion" skill="religion"}}
|
||||
{{> skillRow name="Sleight of Hand" skill="sleightOfHand"}}
|
||||
{{> skillRow name="Stealth" skill="stealth"}}
|
||||
{{> skillRow name="Survival" skill="survival"}}
|
||||
{{> skillRow name="Acrobatics" skill="acrobatics" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Animal Handling" skill="animalHandling" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Arcana" skill="arcana" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Athletics" skill="athletics" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Deception" skill="deception" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="History" skill="history" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Insight" skill="insight" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Intimidation" skill="intimidation" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Investigation" skill="investigation" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Medicine" skill="medicine" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Nature" skill="nature" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Perception" skill="perception" showPassive="forced"}}
|
||||
{{> skillRow name="Performance" skill="performance" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Persuasion" skill="persuasion" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Religion" skill="religion" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Sleight of Hand" skill="sleightOfHand" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Stealth" skill="stealth" showPassive="ifNeeded"}}
|
||||
{{> skillRow name="Survival" skill="survival" showPassive="ifNeeded"}}
|
||||
</div>
|
||||
</paper-material>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,16 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.character-card .image {
|
||||
.partyHeader {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.partyHeader iron-icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.partyHeader:hover iron-icon{
|
||||
visibility: initial;
|
||||
}
|
||||
|
||||
.character-card .initials {
|
||||
|
||||
@@ -10,31 +10,27 @@
|
||||
{{#if currentUser}}
|
||||
{{#if characters.count}}
|
||||
<div class="character-list layout horizontal wrap">
|
||||
{{# each characters}}
|
||||
<a class="character-card flex layout vertical end-justified" href="/character/{{_id}}">
|
||||
<iron-image class="fit {{colorClass}}"
|
||||
sizing="cover" preload fade src={{picture}}>
|
||||
</iron-image>
|
||||
{{#unless picture}}
|
||||
<div class="fit initials layout vertical center center-justified">
|
||||
{{initials name}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
<paper-item>
|
||||
<paper-item-body two-lines>
|
||||
<div class="name white87">
|
||||
{{name}}
|
||||
</div>
|
||||
<div secondary style="color: #8a8a8a; color: rgba(255,255,255,0.87);">
|
||||
{{alignment}} {{gender}} {{race}}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
</paper-item>
|
||||
<paper-ripple></paper-ripple>
|
||||
</a>
|
||||
{{# each charactersWithNoParty}}
|
||||
{{> characterCard}}
|
||||
{{/each}}
|
||||
{{> gridPadding class="character-card flex layout vertical" num=12}}
|
||||
</div>
|
||||
{{# each party in parties}}
|
||||
<div class="party" data-id={{party._id}}>
|
||||
{{#with party}}
|
||||
<div class="partyHeader clickable paper-font-title padded">
|
||||
{{name}}
|
||||
<iron-icon icon="create"></iron-icon>
|
||||
</div>
|
||||
{{/with}}
|
||||
<div class="character-list layout horizontal wrap">
|
||||
{{# each charactersInParty party._id}}
|
||||
{{> characterCard}}
|
||||
{{/each}}
|
||||
{{> gridPadding class="character-card flex layout vertical" num=12}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<div layout vertical center center-justified class="padded">
|
||||
<div>You don't seem to have any characters yet</div>
|
||||
@@ -47,9 +43,46 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="fab-buffer"></div>
|
||||
<paper-fab class="floatyButton addCharacter"
|
||||
icon="add"
|
||||
title="Add"></paper-fab>
|
||||
{{#fabMenu}}
|
||||
<div>
|
||||
<paper-fab icon="social:group"
|
||||
class="addParty"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> New Party </paper-tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<paper-fab icon="face"
|
||||
class="addCharacter"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> New Character </paper-tooltip>
|
||||
</div>
|
||||
{{/fabMenu}}
|
||||
</div>
|
||||
</app-header-layout>
|
||||
</template>
|
||||
|
||||
<template name="characterCard">
|
||||
<a class="character-card flex layout vertical end-justified" href="{{characterPath this}}">
|
||||
<iron-image class="fit {{colorClass}}"
|
||||
sizing="cover" preload fade src={{picture}}>
|
||||
</iron-image>
|
||||
{{#unless picture}}
|
||||
<div class="fit initials layout vertical center center-justified">
|
||||
{{initials name}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
<paper-item>
|
||||
<paper-item-body two-lines>
|
||||
<div class="name white87">
|
||||
{{name}}
|
||||
</div>
|
||||
<div secondary style="color: #8a8a8a; color: rgba(255,255,255,0.87);">
|
||||
{{alignment}} {{gender}} {{race}}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
</paper-item>
|
||||
<paper-ripple></paper-ripple>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
@@ -1,27 +1,57 @@
|
||||
Template.characterList.helpers({
|
||||
characters(){
|
||||
characters() {
|
||||
var userId = Meteor.userId();
|
||||
return Characters.find(
|
||||
{
|
||||
$or: [
|
||||
{readers: userId},
|
||||
{writers: userId},
|
||||
{owner: userId},
|
||||
]
|
||||
},
|
||||
{
|
||||
fields: {name: 1, picture: 1, color: 1, race: 1, alignment: 1, gender: 1},
|
||||
sort: {name: 1},
|
||||
}
|
||||
{$or: [{readers: userId}, {writers: userId}, {owner: userId}]},
|
||||
{sort: {name: 1}}
|
||||
);
|
||||
},
|
||||
parties() {
|
||||
return Parties.find({owner: Meteor.userId()});
|
||||
},
|
||||
charactersInParty(partyId) {
|
||||
var userId = Meteor.userId();
|
||||
var party = Parties.findOne(partyId);
|
||||
return Characters.find(
|
||||
{
|
||||
_id: {$in: party.characters},
|
||||
$or: [{readers: userId}, {writers: userId}, {owner: userId}],
|
||||
},
|
||||
{sort: {name: 1}}
|
||||
);
|
||||
},
|
||||
charactersWithNoParty() {
|
||||
var userId = Meteor.userId();
|
||||
var charArrays = Parties.find({owner: userId}).map(p => p.characters);
|
||||
var partyChars = _.uniq(_.flatten(charArrays));
|
||||
return Characters.find(
|
||||
{
|
||||
_id: {$nin: partyChars},
|
||||
$or: [{readers: userId}, {writers: userId}, {owner: userId}],
|
||||
},
|
||||
{sort: {name: 1}}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Template.characterCard.helpers({
|
||||
initials(name){
|
||||
return name.replace(/[^A-Z]/g, "");
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
Template.characterList.events({
|
||||
"tap .addCharacter": function(event, template) {
|
||||
"click .partyHeader": function(event, instance){
|
||||
pushDialogStack({
|
||||
template: "partyDialog",
|
||||
data: {
|
||||
_id: this._id,
|
||||
startEditing: true,
|
||||
},
|
||||
element: event.currentTarget.parentElement,
|
||||
});
|
||||
},
|
||||
"click .addCharacter": function(event, instance) {
|
||||
pushDialogStack({
|
||||
template: "newCharacterDialog",
|
||||
element: event.currentTarget,
|
||||
@@ -29,8 +59,23 @@ Template.characterList.events({
|
||||
if (!character) return;
|
||||
character.owner = Meteor.userId();
|
||||
let _id = Characters.insert(character);
|
||||
Router.go("characterSheet", {_id});
|
||||
let urlName = getSlug(character.name, {maintainCase: true}) || "-"
|
||||
Router.go("characterSheet", {_id, urlName});
|
||||
},
|
||||
})
|
||||
},
|
||||
"click .addParty": function(event, instance) {
|
||||
var partyId = Parties.insert({
|
||||
owner: Meteor.userId(),
|
||||
});
|
||||
pushDialogStack({
|
||||
template: "partyDialog",
|
||||
data: {
|
||||
_id: partyId,
|
||||
startEditing: true,
|
||||
},
|
||||
element: event.currentTarget,
|
||||
returnElement: instance.find(`.party[data-id='${partyId}']`),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,8 +2,21 @@
|
||||
prevent character names from wrapping
|
||||
*/
|
||||
|
||||
.character-name {
|
||||
.side-list .character-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.side-list .partyHead {
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.side-list .partyHead iron-icon {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.side-list .partyHead iron-icon.open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
<template name="characterSideList">
|
||||
{{#if characters.count}}
|
||||
<div class="side-list">
|
||||
{{#each characters}}
|
||||
<a href={{pathFor route="characterSheet" data=this}} tabindex="-1" class="side-list-character characterRepresentative">
|
||||
<paper-item class="short">
|
||||
<div class="character-name">
|
||||
{{name}}
|
||||
</div>
|
||||
</paper-item>
|
||||
</a>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="side-list">
|
||||
{{#each charactersWithNoParty}}
|
||||
<a href={{characterPath this}} tabindex="-1" class="side-list-character characterRepresentative">
|
||||
<paper-item class="short">
|
||||
<div class="character-name">
|
||||
{{name}}
|
||||
</div>
|
||||
</paper-item>
|
||||
</a>
|
||||
{{/each}}
|
||||
{{#each parties}}
|
||||
<div class="paper-font-subhead partyHead">
|
||||
<iron-icon icon="chevron-right" class="{{#if isOpen _id}}open{{/if}}">
|
||||
</iron-icon>
|
||||
{{name}}
|
||||
</div>
|
||||
<iron-collapse opened={{isOpen _id}}>
|
||||
{{#each charactersInParty}}
|
||||
<a href={{characterPath this}} tabindex="-1" class="side-list-character characterRepresentative">
|
||||
<paper-item class="short">
|
||||
<div class="character-name">
|
||||
{{name}}
|
||||
</div>
|
||||
</paper-item>
|
||||
</a>
|
||||
{{/each}}
|
||||
</iron-collapse>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,33 +1,50 @@
|
||||
Template.characterSideList.onCreated(function() {
|
||||
this.subscribe("characterList");
|
||||
this.openedParties = new ReactiveVar(new Set());
|
||||
});
|
||||
|
||||
Template.characterSideList.helpers({
|
||||
characters: function() {
|
||||
parties() {
|
||||
var userId = Meteor.userId();
|
||||
return Parties.find({owner: userId});
|
||||
},
|
||||
charactersInParty() {
|
||||
var userId = Meteor.userId();
|
||||
return Characters.find(
|
||||
{
|
||||
$or: [
|
||||
{readers: userId},
|
||||
{writers: userId},
|
||||
{owner: userId},
|
||||
]
|
||||
_id: {$in: this.characters},
|
||||
$or: [{readers: userId}, {writers: userId}, {owner: userId}],
|
||||
},
|
||||
{
|
||||
fields: {name: 1},
|
||||
sort: {name: 1},
|
||||
}
|
||||
{sort: {name: 1}}
|
||||
);
|
||||
},
|
||||
charactersWithNoParty() {
|
||||
var userId = Meteor.userId();
|
||||
var charArrays = Parties.find({owner: userId}).map(p => p.characters);
|
||||
var partyChars = _.uniq(_.flatten(charArrays));
|
||||
return Characters.find(
|
||||
{
|
||||
_id: {$nin: partyChars},
|
||||
$or: [{readers: userId}, {writers: userId}, {owner: userId}],
|
||||
},
|
||||
{sort: {name: 1}}
|
||||
);
|
||||
},
|
||||
isOpen(id) {
|
||||
var openedParties = Template.instance().openedParties.get();
|
||||
console.log(openedParties);
|
||||
return openedParties.has(id);
|
||||
},
|
||||
});
|
||||
|
||||
Template.characterSideList.events({
|
||||
"tap .singleLineItem": function(event, instance) {
|
||||
//Router.go("characterSheet", {_id: this._id});
|
||||
$("core-drawer-panel").get(0).closeDrawer();
|
||||
},
|
||||
"tap core-item": function() {
|
||||
Router.go("characterList");
|
||||
$("core-drawer-panel").get(0).closeDrawer();
|
||||
"click .partyHead": function(event, instance){
|
||||
var openedParties = instance.openedParties.get();
|
||||
if (openedParties.has(this._id)){
|
||||
openedParties.delete(this._id);
|
||||
} else {
|
||||
openedParties.add(this._id);
|
||||
}
|
||||
instance.openedParties.set(openedParties);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.partyEdit .inPartyCheckbox {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<template name="partyDialog">
|
||||
{{#with party}}
|
||||
{{#baseDialog title=name hideColor=true startEditing=true}}
|
||||
{{> partyDetails}}
|
||||
{{else}}
|
||||
{{> partyEdit}}
|
||||
{{/baseDialog}}
|
||||
{{/with}}
|
||||
</template>
|
||||
|
||||
<template name="partyDetails">
|
||||
<div class="fit layout vertical partyDetails" style="padding: 24px;">
|
||||
<div>
|
||||
{{#each character in getCharacters}}
|
||||
<div>{{character.name}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="partyEdit">
|
||||
<div class="layout vertical partyEdit" style="padding: 24px;">
|
||||
<paper-input class="partyNameInput" value={{name}} label="Party name">
|
||||
</paper-input>
|
||||
{{#each allCharacters}}
|
||||
<paper-checkbox checked={{charInParty _id}}
|
||||
class="inPartyCheckbox">
|
||||
{{name}}
|
||||
</paper-checkbox>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,62 @@
|
||||
Template.partyDialog.helpers({
|
||||
party(){
|
||||
return Parties.findOne(this._id);
|
||||
}
|
||||
});
|
||||
|
||||
Template.partyDetails.helpers({
|
||||
getCharacters (){
|
||||
var userId = Meteor.userId();
|
||||
return Characters.find(
|
||||
{
|
||||
_id: {$in: this.characters},
|
||||
$or: [{readers: userId}, {writers: userId}, {owner: userId}],
|
||||
},
|
||||
{sort: {name: 1}}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Template.partyEdit.helpers({
|
||||
allCharacters() {
|
||||
var userId = Meteor.userId();
|
||||
return Characters.find(
|
||||
{$or: [{readers: userId}, {writers: userId}, {owner: userId}]},
|
||||
{sort: {name: 1}}
|
||||
);
|
||||
},
|
||||
charInParty(charId) {
|
||||
return _.contains(Template.parentData().characters, charId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.partyDialog.events({
|
||||
"click #deleteButton": function(event, instance){
|
||||
Parties.remove(instance.data._id);
|
||||
popDialogStack();
|
||||
},
|
||||
"click #doneEditingButton": function(event, instance){
|
||||
popDialogStack();
|
||||
},
|
||||
});
|
||||
|
||||
Template.partyEdit.events({
|
||||
"change .inPartyCheckbox": function(event, instance){
|
||||
var currentCharacters = this.characters;
|
||||
var checked = event.currentTarget.checked;
|
||||
var charId = this._id;
|
||||
var partyId = instance.data._id;
|
||||
if (checked){
|
||||
Parties.update(partyId, {$addToSet: {characters: charId}});
|
||||
} else {
|
||||
Parties.update(partyId, {$pull: {characters: charId}});
|
||||
}
|
||||
},
|
||||
"input .partyNameInput": function(event, instance){
|
||||
var name = event.currentTarget.value;
|
||||
Parties.update(this._id, {$set: {name}}, {
|
||||
removeEmptyStrings: false,
|
||||
trimStrings: false,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -4,11 +4,13 @@ Template.baseDialog.onCreated(function(){
|
||||
|
||||
Template.baseDialog.helpers({
|
||||
editing: function(){
|
||||
if (!Template.parentData() || !Template.parentData().charId) return true;
|
||||
return Template.instance().editing.get() &&
|
||||
canEditCharacter(Template.parentData().charId);
|
||||
},
|
||||
showEdit: function() {
|
||||
if (this.hideEdit) return false;
|
||||
if (!Template.parentData() || !Template.parentData().charId) return true;
|
||||
return canEditCharacter(Template.parentData().charId);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user