Merge branch 'version-2' into version-2-tabletop
This commit is contained in:
@@ -4,7 +4,10 @@
|
||||
Delete {{ typeName }}
|
||||
</v-toolbar-title>
|
||||
<div>
|
||||
<v-alert type="warning" outlined>
|
||||
<v-alert
|
||||
type="warning"
|
||||
outlined
|
||||
>
|
||||
This can't be undone
|
||||
</v-alert>
|
||||
<p v-if="name">
|
||||
@@ -13,6 +16,8 @@
|
||||
<v-text-field
|
||||
v-if="name"
|
||||
v-model="inputName"
|
||||
label="Confirmation"
|
||||
outlined
|
||||
/>
|
||||
<div class="layout justify-center">
|
||||
<v-btn
|
||||
@@ -39,26 +44,35 @@
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DialogBase,
|
||||
},
|
||||
props: {
|
||||
typeName: String,
|
||||
name: String,
|
||||
},
|
||||
data(){return {
|
||||
inputName: undefined,
|
||||
}},
|
||||
computed: {
|
||||
nameMatch(){
|
||||
if (!this.name) return true;
|
||||
let uppername = this.name.toUpperCase();
|
||||
let upperInputName = this.inputName && this.inputName.toUpperCase();
|
||||
return uppername === upperInputName;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
DialogBase,
|
||||
},
|
||||
props: {
|
||||
typeName: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputName: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
nameMatch() {
|
||||
if (!this.name) return true;
|
||||
let uppername = this.name.toUpperCase();
|
||||
let upperInputName = this.inputName && this.inputName.toUpperCase();
|
||||
return uppername === upperInputName;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -50,63 +50,69 @@
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import getThemeColor from '/imports/ui/utility/getThemeColor.js';
|
||||
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
|
||||
import getThemeColor from '/imports/ui/utility/getThemeColor.js';
|
||||
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
overrideBackButton: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
darkBody: Boolean,
|
||||
},
|
||||
data(){ return {
|
||||
offsetTop: 0,
|
||||
}},
|
||||
computed: {
|
||||
isDark(){
|
||||
return isDarkColor(this.computedColor);
|
||||
},
|
||||
computedColor(){
|
||||
return this.color || getThemeColor('secondary');
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
overrideBackButton: {
|
||||
type: Function,
|
||||
default: undefined,
|
||||
},
|
||||
darkBody: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
offsetTop: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isDark() {
|
||||
return isDarkColor(this.computedColor);
|
||||
},
|
||||
computedColor() {
|
||||
return this.color || getThemeColor('secondary');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onScroll(e) {
|
||||
this.offsetTop = e.target.scrollTop
|
||||
},
|
||||
back() {
|
||||
if (this.overrideBackButton) {
|
||||
this.overrideBackButton();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onScroll(e){
|
||||
this.offsetTop = e.target.scrollTop
|
||||
},
|
||||
back(){
|
||||
if (this.overrideBackButton){
|
||||
this.overrideBackButton();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
close(){
|
||||
this.$store.dispatch('popDialogStack');
|
||||
},
|
||||
},
|
||||
}
|
||||
close() {
|
||||
this.$store.dispatch('popDialogStack');
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.base-dialog-toolbar {
|
||||
z-index: 2;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
#base-dialog-body, .unwrapped-content {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
#base-dialog-body.dark-body {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.theme--dark #base-dialog-body.dark-body {
|
||||
background-color: #303030;
|
||||
}
|
||||
.base-dialog-toolbar {
|
||||
z-index: 2;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
#base-dialog-body,
|
||||
.unwrapped-content {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#base-dialog-body.dark-body {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.theme--dark #base-dialog-body.dark-body {
|
||||
background-color: #303030;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,23 @@
|
||||
const AddCreaturePropertyDialog = () => import('/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue');
|
||||
// Load commonly used dialogs immediately
|
||||
import AddCreaturePropertyDialog from '/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue';
|
||||
import CharacterCreationDialog from '/imports/ui/creature/character/CharacterCreationDialog.vue';
|
||||
import CastSpellWithSlotDialog from '/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue';
|
||||
import CreatureFormDialog from '/imports/ui/creature/CreatureFormDialog.vue';
|
||||
import CreaturePropertyCreationDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyCreationDialog.vue';
|
||||
import CreaturePropertyDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyDialog.vue';
|
||||
import CreaturePropertyFromLibraryDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyFromLibraryDialog.vue';
|
||||
import CreatureRootDialog from '/imports/ui/creature/character/CreatureRootDialog.vue';
|
||||
import DeleteConfirmationDialog from '/imports/ui/dialogStack/DeleteConfirmationDialog.vue';
|
||||
import ExperienceInsertDialog from '/imports/ui/creature/experiences/ExperienceInsertDialog.vue';
|
||||
import ExperienceListDialog from '/imports/ui/creature/experiences/ExperienceListDialog.vue';
|
||||
import HelpDialog from '/imports/ui/dialogStack/HelpDialog.vue';
|
||||
import LevelUpDialog from '/imports/ui/creature/slots/LevelUpDialog.vue';
|
||||
import SelectLibraryNodeDialog from '/imports/ui/library/SelectLibraryNodeDialog.vue';
|
||||
import SlotFillDialog from '/imports/ui/creature/slots/SlotFillDialog.vue';
|
||||
import TierTooLowDialog from '/imports/ui/user/TierTooLowDialog.vue';
|
||||
import TransferOwnershipDialog from '/imports/ui/sharing/TransferOwnershipDialog.vue';
|
||||
|
||||
// Lazily load less common dialogs
|
||||
const ArchiveDialog = () => import('/imports/ui/creature/archive/ArchiveDialog.vue');
|
||||
const CastSpellWithSlotDialog = () => import('/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue');
|
||||
const CharacterSheetDialog = () => import('/imports/ui/tabletop/CharacterSheetDialog.vue');
|
||||
@@ -8,21 +27,16 @@ const CreaturePropertyDialog = () => import('/imports/ui/creature/creatureProper
|
||||
const CreaturePropertyFromLibraryDialog = () => import('/imports/ui/creature/creatureProperties/CreaturePropertyFromLibraryDialog.vue');
|
||||
const DeleteConfirmationDialog = () => import('/imports/ui/dialogStack/DeleteConfirmationDialog.vue');
|
||||
const DeleteUserAccountDialog = () => import('/imports/ui/user/DeleteUserAccountDialog.vue');
|
||||
const ExperienceInsertDialog = () => import( '/imports/ui/creature/experiences/ExperienceInsertDialog.vue');
|
||||
const ExperienceListDialog = () => import( '/imports/ui/creature/experiences/ExperienceListDialog.vue');
|
||||
const InviteDialog = () => import('/imports/ui/user/InviteDialog.vue');
|
||||
const LibraryCollectionCreationDialog = () => import('/imports/ui/library/LibraryCollectionCreationDialog.vue');
|
||||
const LibraryCollectionEditDialog = () => import('/imports/ui/library/LibraryCollectionEditDialog.vue');
|
||||
const LibraryCreationDialog = () => import('/imports/ui/library/LibraryCreationDialog.vue');
|
||||
const LibraryEditDialog = () => import('/imports/ui/library/LibraryEditDialog.vue');
|
||||
const LibraryNodeCreationDialog = () => import('/imports/ui/library/LibraryNodeCreationDialog.vue');
|
||||
const LibraryNodeDialog = () => import('/imports/ui/library/LibraryNodeDialog.vue');
|
||||
const MoveLibraryNodeDialog = () => import('/imports/ui/library/MoveLibraryNodeDialog.vue');
|
||||
const SelectCreaturesDialog = () => import('/imports/ui/tabletop/SelectCreaturesDialog.vue');
|
||||
const SelectLibraryNodeDialog = () => import('/imports/ui/library/SelectLibraryNodeDialog.vue');
|
||||
const ShareDialog = () => import('/imports/ui/sharing/ShareDialog.vue');
|
||||
const SlotDetailsDialog = () => import('/imports/ui/creature/slots/SlotDetailsDialog.vue');
|
||||
const SlotFillDialog = () => import('/imports/ui/creature/slots/SlotFillDialog.vue');
|
||||
const TierTooLowDialog = () => import('/imports/ui/user/TierTooLowDialog.vue');
|
||||
const TransferOwnershipDialog = () => import('/imports/ui/sharing/TransferOwnershipDialog.vue');
|
||||
const UsernameDialog = () => import('/imports/ui/user/UsernameDialog.vue');
|
||||
|
||||
export default {
|
||||
@@ -30,15 +44,21 @@ export default {
|
||||
ArchiveDialog,
|
||||
CastSpellWithSlotDialog,
|
||||
CharacterSheetDialog,
|
||||
CharacterCreationDialog,
|
||||
CreatureFormDialog,
|
||||
CreaturePropertyCreationDialog,
|
||||
CreaturePropertyDialog,
|
||||
CreaturePropertyFromLibraryDialog,
|
||||
CreatureRootDialog,
|
||||
DeleteConfirmationDialog,
|
||||
DeleteUserAccountDialog,
|
||||
ExperienceInsertDialog,
|
||||
ExperienceListDialog,
|
||||
HelpDialog,
|
||||
InviteDialog,
|
||||
LevelUpDialog,
|
||||
LibraryCollectionCreationDialog,
|
||||
LibraryCollectionEditDialog,
|
||||
LibraryCreationDialog,
|
||||
LibraryEditDialog,
|
||||
LibraryNodeCreationDialog,
|
||||
@@ -47,7 +67,6 @@ export default {
|
||||
SelectCreaturesDialog,
|
||||
SelectLibraryNodeDialog,
|
||||
ShareDialog,
|
||||
SlotDetailsDialog,
|
||||
SlotFillDialog,
|
||||
TierTooLowDialog,
|
||||
TransferOwnershipDialog,
|
||||
|
||||
108
app/imports/ui/dialogStack/HelpDialog.vue
Normal file
108
app/imports/ui/dialogStack/HelpDialog.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template lang="html">
|
||||
<dialog-base>
|
||||
<v-icon
|
||||
slot="toolbar"
|
||||
class="mr-2"
|
||||
>
|
||||
mdi-help
|
||||
</v-icon>
|
||||
<v-toolbar-title slot="toolbar">
|
||||
Help: {{ title }}
|
||||
</v-toolbar-title>
|
||||
<div>
|
||||
<v-progress-circular
|
||||
v-if="!doc && !$subReady.docs"
|
||||
indeterminate
|
||||
color="primary"
|
||||
size="32"
|
||||
/>
|
||||
<div v-else-if="!doc">
|
||||
Help document not found for {{ title }}
|
||||
</div>
|
||||
<markdown-text
|
||||
v-else
|
||||
:markdown="doc"
|
||||
@click="linkClick"
|
||||
/>
|
||||
</div>
|
||||
<v-spacer slot="actions" />
|
||||
<v-btn
|
||||
slot="actions"
|
||||
text
|
||||
@click="$store.dispatch('popDialogStack')"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||
import { propsByDocsPath } from '/imports/constants/PROPERTIES.js';
|
||||
import MarkdownText from '/imports/ui/components/MarkdownText.vue';
|
||||
import Docs from '/imports/api/docs/Docs.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DialogBase,
|
||||
MarkdownText,
|
||||
},
|
||||
props: {
|
||||
path: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
prop() {
|
||||
return propsByDocsPath.get(this.path);
|
||||
},
|
||||
title() {
|
||||
if (this.prop) {
|
||||
return this.prop.name;
|
||||
} else {
|
||||
const titleCase = this.path.replace(
|
||||
/(\w*)(\W+)/g,
|
||||
function (txt, word) {
|
||||
return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase() + ' ';
|
||||
}
|
||||
);
|
||||
return titleCase || 'Character Sheet';
|
||||
}
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
$subscribe: {
|
||||
'docs'() {
|
||||
return [this.path];
|
||||
},
|
||||
},
|
||||
doc() {
|
||||
const doc = Docs.findOne(this.path);
|
||||
return doc && doc.text;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
linkClick(e) {
|
||||
const target = e.target || e.srcElement;
|
||||
const href = target && target.href;
|
||||
if (!href) return;
|
||||
const path = href.split('/docs/')[1];
|
||||
if (!path) return;
|
||||
e.preventDefault();
|
||||
target.dataset.id = path;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'help-dialog',
|
||||
elementId: path,
|
||||
data: {
|
||||
path,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
</style>
|
||||
@@ -2,42 +2,42 @@ import { parse, stringify } from 'css-box-shadow';
|
||||
|
||||
// Only supports border radius defined like "20px" or "100%"
|
||||
const transformedRadius = (radiusString, deltaWidth, deltaHeight) => {
|
||||
if (/^\d+\.?\d*px$/.test(radiusString)){
|
||||
//The radius is defined in pixel units, so get the radius as a number
|
||||
const rad = +radiusString.match(/\d+\.?\d*/)[0];
|
||||
// Set the x and y radius of the "to" element, compensating for scale
|
||||
return `${rad / deltaWidth}px / ${rad / deltaHeight}px`;
|
||||
} else if (/^\d+\.?\d*%$/.test(radiusString)) {
|
||||
//The radius is defined as a percentage, so just use it as is
|
||||
return radiusString;
|
||||
}
|
||||
if (/^\d+\.?\d*px$/.test(radiusString)) {
|
||||
//The radius is defined in pixel units, so get the radius as a number
|
||||
const rad = +radiusString.match(/\d+\.?\d*/)[0];
|
||||
// Set the x and y radius of the "to" element, compensating for scale
|
||||
return `${rad / deltaWidth}px / ${rad / deltaHeight}px`;
|
||||
} else if (/^\d+\.?\d*%$/.test(radiusString)) {
|
||||
//The radius is defined as a percentage, so just use it as is
|
||||
return radiusString;
|
||||
}
|
||||
};
|
||||
|
||||
const transformedBoxShadow = (shadowString, deltaWidth, deltaHeight) => {
|
||||
if (shadowString === 'none') return shadowString;
|
||||
if (shadowString[0] === 'r'){
|
||||
let strings = shadowString.match(/rgba\([^)]+\)[^,]+/g);
|
||||
strings = strings.map(string => {
|
||||
// Move color to end
|
||||
let m = string.match(/(rgba\([^)]+\))([^,]+)/);
|
||||
return `${m[2].trim()} ${m[1]}`;
|
||||
});
|
||||
shadowString = strings.join(', ');
|
||||
}
|
||||
let scaleAverage = (deltaWidth + deltaHeight) / 2;
|
||||
let shadows = parse(shadowString);
|
||||
shadows.forEach(shadow => {
|
||||
shadow.offsetX /= deltaWidth;
|
||||
shadow.offsetY /= deltaHeight;
|
||||
shadow.blurRadius /= scaleAverage;
|
||||
shadow.spreadRadius /= scaleAverage;
|
||||
})
|
||||
return stringify(shadows);
|
||||
if (shadowString === 'none') return shadowString;
|
||||
if (shadowString[0] === 'r') {
|
||||
let strings = shadowString.match(/rgba\([^)]+\)[^,]+/g);
|
||||
strings = strings.map(string => {
|
||||
// Move color to end
|
||||
let m = string.match(/(rgba\([^)]+\))([^,]+)/);
|
||||
return `${m[2].trim()} ${m[1]}`;
|
||||
});
|
||||
shadowString = strings.join(', ');
|
||||
}
|
||||
let scaleAverage = (deltaWidth + deltaHeight) / 2;
|
||||
let shadows = parse(shadowString);
|
||||
shadows.forEach(shadow => {
|
||||
shadow.offsetX /= deltaWidth;
|
||||
shadow.offsetY /= deltaHeight;
|
||||
shadow.blurRadius /= scaleAverage;
|
||||
shadow.spreadRadius /= scaleAverage;
|
||||
})
|
||||
return stringify(shadows);
|
||||
}
|
||||
|
||||
export default function mockElement({source, target, offset = {x: 0, y: 0}}){
|
||||
if (!source || !target) throw `Can't mock without ${source ? 'target' : 'source'}` ;
|
||||
let sourceRect = source.getBoundingClientRect();
|
||||
export default function mockElement({ source, target, offset = { x: 0, y: 0 } }) {
|
||||
if (!source || !target) throw `Can't mock without ${source ? 'target' : 'source'}`;
|
||||
let sourceRect = source.getBoundingClientRect();
|
||||
let targetRect = target.getBoundingClientRect();
|
||||
|
||||
// Get how must the target change to become the source
|
||||
@@ -47,20 +47,20 @@ export default function mockElement({source, target, offset = {x: 0, y: 0}}){
|
||||
const deltaTop = sourceRect.top - targetRect.top + offset.y;
|
||||
// Mock the source
|
||||
target.style.transform = `translate(${deltaLeft}px, ${deltaTop}px) ` +
|
||||
`scale(${deltaWidth}, ${deltaHeight})`;
|
||||
`scale(${deltaWidth}, ${deltaHeight})`;
|
||||
// Mock the background color unless it's completely transparent
|
||||
let backgroundColor = getComputedStyle(source).backgroundColor
|
||||
if (backgroundColor !== 'rgba(0, 0, 0, 0)'){
|
||||
if (backgroundColor !== 'rgba(0, 0, 0, 0)') {
|
||||
target.style.backgroundColor = backgroundColor;
|
||||
}
|
||||
// Edge might not combine all border radii into a single value,
|
||||
// So we just sample the top left one if we need to
|
||||
let oldRadius = getComputedStyle(source).borderRadius ||
|
||||
getComputedStyle(source).borderTopLeftRadius;
|
||||
let borderRadius = transformedRadius(oldRadius, deltaWidth, deltaHeight);
|
||||
target.style.borderRadius = borderRadius;
|
||||
let boxShadow = transformedBoxShadow(
|
||||
getComputedStyle(source).boxShadow, deltaWidth, deltaHeight
|
||||
);
|
||||
target.style.setProperty('box-shadow', boxShadow, 'important');
|
||||
// Edge might not combine all border radii into a single value,
|
||||
// So we just sample the top left one if we need to
|
||||
let oldRadius = getComputedStyle(source).borderRadius ||
|
||||
getComputedStyle(source).borderTopLeftRadius;
|
||||
let borderRadius = transformedRadius(oldRadius, deltaWidth, deltaHeight);
|
||||
target.style.borderRadius = borderRadius;
|
||||
let boxShadow = transformedBoxShadow(
|
||||
getComputedStyle(source).boxShadow, deltaWidth, deltaHeight
|
||||
);
|
||||
target.style.setProperty('box-shadow', boxShadow, 'important');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user