diff --git a/app/imports/client/ui/properties/components/PropertyCard.vue b/app/imports/client/ui/properties/components/PropertyCard.vue
new file mode 100644
index 00000000..b22ec03e
--- /dev/null
+++ b/app/imports/client/ui/properties/components/PropertyCard.vue
@@ -0,0 +1,30 @@
+
+
+
+
+ A property card for the {{ model.type }} isn't defined. You should report this error.
+
+
+
+
+
diff --git a/app/imports/client/ui/tabletop/CharacterSheetDialog.vue b/app/imports/client/ui/tabletop/CharacterSheetDialog.vue
index f3b010b0..76618c43 100644
--- a/app/imports/client/ui/tabletop/CharacterSheetDialog.vue
+++ b/app/imports/client/ui/tabletop/CharacterSheetDialog.vue
@@ -1,45 +1,9 @@
-
- $store.commit(
- 'setTabForCharacterSheet',
- {id: creatureId, tab: e}
- )"
- >
-
- Stats
-
-
- Actions
-
-
- Spells
-
-
- Inventory
-
-
- Features
-
-
- Journal
-
-
- Build
-
-
- Tree
-
-
+
+
+ {{ creature && creature.name }}
+
+ $store.commit(
+ 'setTabForCharacterSheet',
+ {id: creatureId, tab: e}
+ )"
+ >
+
+ Stats
+ mdi-chart-box
+
+
+ Actions
+ mdi-lightning-bolt
+
+
+ Spells
+ mdi-fire
+
+
+ Inventory
+ mdi-cube
+
+
+ Features
+ mdi-text
+
+
+ Journal
+ mdi-book-open-variant
+
+
+ Build
+ mdi-wrench
+
+
@@ -67,15 +72,26 @@ export default {
required: true,
},
},
- // @ts-ignore
meteor: {
creature() {
if (!this.creatureId) return;
return Creatures.findOne(this.creatureId);
},
},
- data(){return {
- tab: 0,
- }},
}
+
+
diff --git a/app/imports/client/ui/tabletop/selectedCreatureBar/CreatureBarIcon.vue b/app/imports/client/ui/tabletop/selectedCreatureBar/CreatureBarIcon.vue
index 37688f27..6c5ff4bc 100644
--- a/app/imports/client/ui/tabletop/selectedCreatureBar/CreatureBarIcon.vue
+++ b/app/imports/client/ui/tabletop/selectedCreatureBar/CreatureBarIcon.vue
@@ -1,14 +1,19 @@
{{ icon }}
@@ -35,7 +40,8 @@ export default {
icon: {
type: String,
default: undefined,
- }
+ },
+ selected: Boolean,
},
meteor: {
prop() {
diff --git a/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue b/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue
index c6030e1f..a19a479c 100644
--- a/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue
+++ b/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue
@@ -1,8 +1,8 @@
+
+
+
+ {{ activeIcon.icon }}
+
+ {{ activeIcon.tabName }}
+
+
{selectedIcon = icon; menuX = e.clientX; menuY = e.clientY; log(e)}"
+ :selected="selectedIcon === icon"
+ :data-id="icon.propId || icon.standardId"
+ @click="e => selectIcon(e, icon)"
+ @mouseenter="e => hoverIcon(e, icon)"
+ @mouseleave="unHoverIcon(icon)"
/>
@@ -101,19 +129,94 @@ export default {
data() {
return {
rows: 2,
+ hoveredIcon: undefined,
selectedIcon: undefined,
+ menuOpen: false,
menuX: 200,
- menuY: 200,
+ menuY: window.innerHeight - 200,
};
},
+ computed: {
+ activeIcon() {
+ return this.selectedIcon || this.hoveredIcon;
+ }
+ },
+ watch: {
+ menuOpen(val) {
+ if (!val && this.selectIcon) {
+ this.selectedIcon = undefined;
+ }
+ },
+ },
+ methods: {
+ log(e) {
+ console.log(e);
+ },
+ hoverIcon(e, icon) {
+ if (this.selectedIcon) return;
+ // this.menuX = e.clientX - (e.clientX % 44);
+ const { left, right } = e.target.getBoundingClientRect();
+ const x = ( left + right ) / 2
+ this.menuX = x;
+ this.hoveredIcon = icon;
+ this.menuOpen = true;
+ },
+ unHoverIcon(icon) {
+ if (this.hoveredIcon === icon) {
+ this.hoveredIcon = undefined;
+ if (!this.selectedIcon) {
+ this.menuOpen = false;
+ }
+ }
+ },
+ selectIcon(e, icon) {
+ if (icon.tab) {
+ this.openCharacterSheet(icon.tab, icon.standardId);
+ return;
+ }
+ if (this.selectedIcon === icon) {
+ this.selectedIcon = undefined;
+ this.menuOpen = false;
+ return;
+ }
+ const { left, right } = e.target.getBoundingClientRect();
+ const x = ( left + right ) / 2
+ this.menuX = x;
+ this.selectedIcon = icon;
+ this.menuOpen = true;
+ },
+ clickOutsideMenu () {
+ this.menuOpen = false;
+ },
+ menuClickOutsideInclude() {
+ return [
+ document.querySelector('.selected-creature-bar'),
+ document.querySelector('.tabletop-prop-menu')
+ ];
+ },
+ openCharacterSheet(tab, elementId) {
+ this.$store.commit(
+ 'setTabForCharacterSheet',
+ { id: this.creatureId, tab }
+ );
+ this.$store.commit('pushDialogStack', {
+ component: 'character-sheet-dialog',
+ elementId,
+ data: {
+ creatureId: this.creatureId,
+ },
+ });
+ },
+ },
meteor: {
creature() {
if (!this.creatureId) return;
return Creatures.findOne(this.creatureId)
},
selectedProp() {
- if (!this.selectedIcon?.propId) return;
- return CreatureProperties.findOne(this.selectedIcon.propId);
+ const propId = this.activeIcon?.propId;
+ if (!propId) return;
+ return CreatureProperties.findOne(propId);
},
iconGroups() {
if (!this.creature) return;
@@ -121,16 +224,16 @@ export default {
// Get the standard icons
const standardIconsById = {
- 'cast-spell': { groupName: 'Standard Actions', icon: 'mdi-fire' },
- 'make-check': { groupName: 'Standard Actions', icon: 'mdi-radiobox-marked' },
- 'roll-dice': { groupName: 'Standard Actions', icon: 'mdi-dice-d20' },
- 'tab-stats': { groupName: 'Tabs', icon: 'mdi-chart-box' },
- 'tab-actions': { groupName: 'Tabs', icon: 'mdi-lightning-bolt' },
- 'tab-spells': { groupName: 'Tabs', icon: 'mdi-fire' },
- 'tab-inventory': { groupName: 'Tabs', icon: 'mdi-cube' },
- 'tab-features': { groupName: 'Tabs', icon: 'mdi-text' },
- 'tab-journal': { groupName: 'Tabs', icon: 'mdi-book-open-variant' },
- 'tab-build': { groupName: 'Tabs', icon: 'mdi-wrench' },
+ 'cast-spell': {standardId: 'cast-spell', groupName: 'Standard Actions', icon: 'mdi-fire' },
+ 'make-check': {standardId: 'make-check', groupName: 'Standard Actions', icon: 'mdi-radiobox-marked' },
+ 'roll-dice': {standardId: 'roll-dice', groupName: 'Standard Actions', icon: 'mdi-dice-d20' },
+ 'tab-stats': {standardId: 'tab-stats', groupName: 'Tabs', icon: 'mdi-chart-box', tab: 'stats', tabName: 'Stats' },
+ 'tab-actions': {standardId: 'tab-actions', groupName: 'Tabs', icon: 'mdi-lightning-bolt', tab: 'actions', tabName: 'Actions' },
+ 'tab-spells': this.creature?.settings?.hideSpellsTab ? undefined : {standardId: 'tab-spells', groupName: 'Tabs', icon: 'mdi-fire', tab: 'spells', tabName: 'Spells' },
+ 'tab-inventory': {standardId: 'tab-inventory', groupName: 'Tabs', icon: 'mdi-cube', tab: 'inventory', tabName: 'Inventory' },
+ 'tab-features': {standardId: 'tab-features', groupName: 'Tabs', icon: 'mdi-text', tab: 'features', tabName: 'Features' },
+ 'tab-journal': {standardId: 'tab-journal', groupName: 'Tabs', icon: 'mdi-book-open-variant', tab: 'journal', tabName: 'Journal' },
+ 'tab-build': {standardId: 'tab-build', groupName: 'Tabs', icon: 'mdi-wrench', tab: 'build', tabName: 'Build' },
};
// Get the folders that could hide a property
@@ -226,7 +329,8 @@ export default {
// Add default groups for standard icons
for (let key in standardIconsById) {
const standardIcon = standardIconsById[key];
- if (standardIcon._placedInGroup) return;
+ if (!standardIcon) continue;
+ if (standardIcon._placedInGroup) continue;
const groupName = standardIcon.groupName || 'no';
if (!groupsByName[groupName]) {
@@ -234,7 +338,7 @@ export default {
defaultGroups.push(groupsByName[groupName]);
}
- groupsByName[groupName].iconList.push({ standardId: key, icon: standardIcon.icon });
+ groupsByName[groupName].iconList.push(standardIcon);
}
iconGroups.push(...defaultGroups);
@@ -247,16 +351,24 @@ export default {
return iconGroups;
}
},
- methods: {
- selectIcon(e) {
- this.$emit('select-icon', e);
- },
- log(e) {
- console.log(e);
- },
- }
}
-
\ No newline at end of file
diff --git a/app/imports/client/ui/vuetify.js b/app/imports/client/ui/vuetify.js
index f4d31024..adad87d1 100644
--- a/app/imports/client/ui/vuetify.js
+++ b/app/imports/client/ui/vuetify.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
//import Vuetify from 'vuetify/lib';
import Vuetify from 'vuetify/lib/framework';
-import { Scroll, Ripple } from 'vuetify/lib/directives';
+import { Scroll, Ripple, ClickOutside } from 'vuetify/lib/directives';
import SVG_ICONS from '/imports/constants/SVG_ICONS.js';
import SvgIconByName from '/imports/client/ui/icons/SvgIconByName.vue';
import themes from '/imports/client/ui/themes.js';
@@ -11,6 +11,7 @@ Vue.use(Vuetify, {
directives: {
Scroll,
Ripple,
+ ClickOutside,
},
});
diff --git a/app/imports/client/ui/vuexStore.js b/app/imports/client/ui/vuexStore.js
index fec340cd..c4bcc297 100644
--- a/app/imports/client/ui/vuexStore.js
+++ b/app/imports/client/ui/vuexStore.js
@@ -56,6 +56,7 @@ const store = new Vuex.Store({
setTabForCharacterSheet(state, { tab, id }) {
// Convert tab names to tab numbers
if (typeof tab === 'string') {
+ const tabInput = tab;
const creature = Creatures.findOne(id);
if (creature?.settings?.hideSpellsTab) {
tab = tabsWithoutSpells.indexOf(tab);
@@ -63,9 +64,9 @@ const store = new Vuex.Store({
tab = tabs.indexOf(tab);
}
if (!(tab > -1)) {
- throw 'Could not find requested tab';
+ console.warn(`could not find a tab called ${tabInput}`);
+ tab = 0;
}
- console.log('resolved: ', tab);
}
Vue.set(state.characterSheetTabs, id, tab);
},
diff --git a/app/imports/server/publications/tabletops.js b/app/imports/server/publications/tabletops.js
index 58f10a48..a29b506f 100644
--- a/app/imports/server/publications/tabletops.js
+++ b/app/imports/server/publications/tabletops.js
@@ -50,6 +50,7 @@ Meteor.publish('tabletop', function (tabletopId) {
avatarPicture: 1,
tabletop: 1,
initiativeRoll: 1,
+ settings: 1,
},
});
const creatureIds = creatureSummaries.map(c => c._id);