diff --git a/app/imports/api/library/LibraryNodes.js b/app/imports/api/library/LibraryNodes.js
index 37426c1c..197131db 100644
--- a/app/imports/api/library/LibraryNodes.js
+++ b/app/imports/api/library/LibraryNodes.js
@@ -5,6 +5,7 @@ import librarySchemas from '/imports/api/library/librarySchemas.js';
import Libraries from '/imports/api/library/Libraries.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getModifierFields from '/imports/api/getModifierFields.js';
+import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
let LibraryNodes = new Mongo.Collection('libraryNodes');
@@ -62,6 +63,18 @@ function assertNodeEditPermission(node, userId){
return assertEditPermission(lib, userId);
}
+const insertNode = new ValidatedMethod({
+ name: 'LibraryNodes.methods.insert',
+ mixins: [
+ simpleSchemaMixin,
+ ],
+ schema: LibraryNodeSchema,
+ run(libraryNode) {
+ assertNodeEditPermission(libraryNode, this.userId);
+ return LibraryNodes.insert(libraryNode);
+ },
+});
+
const updateNode = new ValidatedMethod({
name: 'LibraryNodes.methods.update',
validate({_id, update}){
@@ -101,4 +114,4 @@ function libraryNodesToTree(ancestorId){
}
export default LibraryNodes;
-export { LibraryNodeSchema, updateNode };
+export { LibraryNodeSchema, insertNode, updateNode };
diff --git a/app/imports/server/publications/library.js b/app/imports/server/publications/library.js
index 0e9219d9..18d6394f 100644
--- a/app/imports/server/publications/library.js
+++ b/app/imports/server/publications/library.js
@@ -46,6 +46,7 @@ Meteor.publish('library', function(libraryId){
const user = Meteor.user();
const userId = user && user._id;
if (!userId) return [];
+ const subs = user && user.subscribedLibraries || [];
let libraryCursor = Libraries.find({
_id: libraryId,
$or: [
diff --git a/app/imports/ui/components/forms/schemaFormMixin.js b/app/imports/ui/components/forms/schemaFormMixin.js
new file mode 100644
index 00000000..d8dabe3f
--- /dev/null
+++ b/app/imports/ui/components/forms/schemaFormMixin.js
@@ -0,0 +1,30 @@
+/**
+ * Forms that take in a schema and a model of the current data, manages smart
+ * inputs, and sends update events when valid data model changes must occur
+ */
+export default function schemaFormMixin(schema){
+ return {
+ data(){ return {
+ valid: true,
+ };},
+ created(){
+ this.validationContext = schema.newContext();
+ },
+ computed: {
+ errors(){
+ this.valid = true;
+ if (!this.model){
+ throw new Error("this.model must be set");
+ }
+ let cleanModel = this.validationContext.clean(this.model);
+ this.validationContext.validate(cleanModel);
+ let errors = {};
+ this.validationContext.validationErrors().forEach(error => {
+ if (this.valid) this.valid = false;
+ errors[error.name] = Attributes.simpleSchema().messageForError(error);
+ });
+ return errors;
+ },
+ },
+ };
+}
diff --git a/app/imports/ui/components/global/SmartInputMixin.js b/app/imports/ui/components/global/SmartInputMixin.js
index d0574f0d..5e643881 100644
--- a/app/imports/ui/components/global/SmartInputMixin.js
+++ b/app/imports/ui/components/global/SmartInputMixin.js
@@ -79,11 +79,11 @@ export default {
this.$emit('change', val, this.acknowledgeChange);
},
hasChangeListener(){
- return this.$listeners && this.$listeners.change
+ return this.$listeners && this.$listeners.change;
},
forceSafeValueUpdate(){
// hack to force the value to update on the child component
- this.safeValue = null
+ this.safeValue = null;
this.$nextTick(() => this.safeValue = this.value);
},
},
diff --git a/app/imports/ui/components/properties/PropertySelector.vue b/app/imports/ui/components/properties/PropertySelector.vue
new file mode 100644
index 00000000..4087d06a
--- /dev/null
+++ b/app/imports/ui/components/properties/PropertySelector.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ {{ property.icon }}
+
+
+ {{ property.name }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/imports/ui/creature/properties/attributes/AttributeCreationDialog.vue b/app/imports/ui/creature/properties/attributes/AttributeCreationDialog.vue
index 1dfcc7ff..07ba3aad 100644
--- a/app/imports/ui/creature/properties/attributes/AttributeCreationDialog.vue
+++ b/app/imports/ui/creature/properties/attributes/AttributeCreationDialog.vue
@@ -44,6 +44,9 @@
},
valid: true,
}},
+ created(){
+ this.validationContext = Attributes.simpleSchema().newContext();
+ },
methods: {
change(update, ack){
for (key in update){
@@ -58,9 +61,6 @@
if (ack) ack();
},
},
- created(){
- this.validationContext = Attributes.simpleSchema().newContext();
- },
computed: {
errors(){
this.valid = true;
diff --git a/app/imports/ui/dialogStack/DialogBase.vue b/app/imports/ui/dialogStack/DialogBase.vue
index 8a42574c..ff219374 100644
--- a/app/imports/ui/dialogStack/DialogBase.vue
+++ b/app/imports/ui/dialogStack/DialogBase.vue
@@ -1,10 +1,10 @@
-
+
arrow_back
-
+
@@ -20,6 +20,7 @@
example > bread > crumb
+
@@ -43,6 +44,7 @@
props: {
color: String,
breadcrumbs: Object,
+ overrideBackButton: Function,
},
data(){ return {
offsetTop: 0,
@@ -52,6 +54,13 @@
onScroll(e){
this.offsetTop = e.target.scrollTop
},
+ back(){
+ if (this.overrideBackButton){
+ this.overrideBackButton();
+ } else {
+ this.close();
+ }
+ },
close(){
store.dispatch("popDialogStack");
},
diff --git a/app/imports/ui/dialogStack/DialogComponentIndex.js b/app/imports/ui/dialogStack/DialogComponentIndex.js
index 4f022a5d..32624bce 100644
--- a/app/imports/ui/dialogStack/DialogComponentIndex.js
+++ b/app/imports/ui/dialogStack/DialogComponentIndex.js
@@ -4,6 +4,7 @@ import AttributeCreationDialog from '/imports/ui/creature/properties/attributes/
import FeatureCreationDialog from '/imports/ui/creature/properties/features/FeatureCreationDialog.vue';
import FeatureDialogContainer from '/imports/ui/creature/properties/features/FeatureDialogContainer.vue';
import LibraryCreationDialog from '/imports/ui/library/LibraryCreationDialog.vue';
+import LibraryNodeCreationDialog from '/imports/ui/library/LibraryNodeCreationDialog.vue';
import SkillDialogContainer from '/imports/ui/creature/properties/skills/SkillDialogContainer.vue';
export default {
@@ -13,5 +14,6 @@ export default {
FeatureCreationDialog,
FeatureDialogContainer,
LibraryCreationDialog,
+ LibraryNodeCreationDialog,
SkillDialogContainer,
};
diff --git a/app/imports/ui/library/LibraryNodeCreationDialog.vue b/app/imports/ui/library/LibraryNodeCreationDialog.vue
new file mode 100644
index 00000000..b7d807a0
--- /dev/null
+++ b/app/imports/ui/library/LibraryNodeCreationDialog.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+ {{ type }}
+
+
+
+
+
+
+
+
+
diff --git a/app/imports/ui/pages/Library.vue b/app/imports/ui/pages/Library.vue
index 431284a1..3fb9c3f9 100644
--- a/app/imports/ui/pages/Library.vue
+++ b/app/imports/ui/pages/Library.vue
@@ -6,17 +6,38 @@
:library-id="$route.params.id"
/>
+
+ add
+