From c780c11e3f137b5d3c98d571bae13fb73db96058 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 22 Jan 2021 14:09:23 +0200 Subject: [PATCH] Improved spell slot casting dialog with search, filters, spell details --- .../ui/components/global/SmartInputMixin.js | 8 +- .../ui/components/global/TextField.vue | 5 +- .../spells/CastSpellWithSlotDialog.vue | 126 +++++++++++++++++- .../components/spells/SpellListTile.vue | 14 ++ app/imports/ui/utility/escapeRegex.js | 3 + 5 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 app/imports/ui/utility/escapeRegex.js diff --git a/app/imports/ui/components/global/SmartInputMixin.js b/app/imports/ui/components/global/SmartInputMixin.js index 7b8d63c0..5df4f5b2 100644 --- a/app/imports/ui/components/global/SmartInputMixin.js +++ b/app/imports/ui/components/global/SmartInputMixin.js @@ -26,6 +26,10 @@ export default { value: [String, Number, Date, Array, Object, Boolean], errorMessages: [String, Array], disabled: Boolean, + debounce: { + type: Number, + default: undefined, + }, }, watch: { focused(newFocus){ @@ -113,7 +117,9 @@ export default { return this.context.editPermission === false || this.disabled; }, debounceTime() { - if (Number.isFinite(this.context.debounceTime)){ + if (Number.isFinite(this.debounce)){ + return this.debounce; + } else if (Number.isFinite(this.context.debounceTime)){ return this.context.debounceTime; } else { return 750; diff --git a/app/imports/ui/components/global/TextField.vue b/app/imports/ui/components/global/TextField.vue index 5f8b79c3..337543e8 100644 --- a/app/imports/ui/components/global/TextField.vue +++ b/app/imports/ui/components/global/TextField.vue @@ -6,7 +6,7 @@ :error-messages="errors" :value="safeValue" :disabled="isDisabled" - box + :box="!regular" @input="input" @focus="focused = true" @blur="focused = false" @@ -18,5 +18,8 @@ export default { mixins: [SmartInput], + props: { + regular: Boolean, + }, }; diff --git a/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue b/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue index f280b36b..d21d678a 100644 --- a/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue +++ b/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue @@ -5,7 +5,66 @@ Cast a Spell - + + + + + + + + +
+ + Clear + + + + Done + +
+
+
@@ -119,6 +180,16 @@ export default { searchString: undefined, selectedSlotId: this.slotId, selectedSpellId: this.spellId, + searchValue: undefined, + searchError: undefined, + filterMenuOpen: false, + booleanFilters: { + verbal: {name: 'Verbal', enabled: false, value: false}, + somatic: {name: 'Somatic', enabled: false, value: false}, + material: {name: 'Material', enabled: false, value: false}, + concentration: {name: 'Concentration', enabled: false, value: false}, + ritual: {name: 'Ritual', enabled: false, value: false}, + }, }}, computed: { computedSpells(){ @@ -135,7 +206,15 @@ export default { } else { return slot.spellSlotLevelValue >= spell.level; } - } + }, + filtersApplied(){ + for (let key in this.booleanFilters){ + if (this.booleanFilters[key].enabled){ + return true; + } + } + return false; + }, }, watch: { selectedSpell(spell){ @@ -152,16 +231,53 @@ export default { } }, }, + methods: { + clearBooleanFilters(){ + for (let key in this.booleanFilters){ + this.booleanFilters[key].enabled = false; + } + }, + spellDialog(_id){ + this.$store.commit('pushDialogStack', { + component: 'creature-property-dialog', + elementId: `spell-info-btn-${_id}`, + data: {_id}, + }); + }, + searchChanged(val, ack){ + this.searchValue = val; + setTimeout(ack, 200); + }, + }, meteor: { spells(){ let slotLevel = this.selectedSlot && this.selectedSlot.spellSlotLevelValue || 0; - return CreatureProperties.find({ + let filter = { 'ancestors.id': this.creatureId, removed: {$ne: true}, inactive: {$ne: true}, prepared: true, level: {$lte: slotLevel}, - }, { + }; + // Apply the filters from the filter menu + for (let key in this.booleanFilters){ + if (this.booleanFilters[key].enabled){ + let value = this.booleanFilters[key].value; + if (key === 'material'){ + filter[key] = {$exists: this.booleanFilters[key].value}; + } else { + filter[key] = value ? true: {$ne: true}; + } + } + } + // Apply the search string to the name field + if (this.searchValue){ + filter.name = { + $regex: this.searchValue.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), + $options: 'i' + }; + } + return CreatureProperties.find(filter, { sort: {order: 1} }); }, @@ -186,7 +302,7 @@ export default { }, selectedSpell(){ return CreatureProperties.findOne(this.selectedSpellId); - } + }, }, } diff --git a/app/imports/ui/properties/components/spells/SpellListTile.vue b/app/imports/ui/properties/components/spells/SpellListTile.vue index 977b9f53..61c777d4 100644 --- a/app/imports/ui/properties/components/spells/SpellListTile.vue +++ b/app/imports/ui/properties/components/spells/SpellListTile.vue @@ -33,6 +33,16 @@ > drag_indicator + + info + @@ -46,6 +56,7 @@ export default { props: { preparingSpells: Boolean, hideHandle: Boolean, + showInfoButton: Boolean, }, computed: { hasClickListener(){ @@ -86,4 +97,7 @@ export default { .primary--text .v-icon, .primary--text .v-list__tile__sub-title { color: #b71c1c } +.theme--light.info-icon{ + color: rgba(0,0,0,.54) !important; +} diff --git a/app/imports/ui/utility/escapeRegex.js b/app/imports/ui/utility/escapeRegex.js new file mode 100644 index 00000000..8ed9c0b9 --- /dev/null +++ b/app/imports/ui/utility/escapeRegex.js @@ -0,0 +1,3 @@ +RegExp.escape = function(s) { + return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); +};