refactor messageItem component

This commit is contained in:
riqo 2020-10-25 15:13:06 -05:00
commit 9272dfb179
8 changed files with 212 additions and 221 deletions

View file

@ -45,5 +45,4 @@ div[contenteditable] {
font-size: 14;
text-align: left;
}
</style>

View file

@ -5,8 +5,7 @@
:transform = "startingOffset"
>
<rect
ref="bubbleTag"
id="#bubble"
ref="messageRect"
x="30"
y="10"
width="196"
@ -17,20 +16,19 @@
<text
font-size="14"
font-family="SF Pro"
id="#message"
ref="contentTag"
ref="messageText"
:fill="textFill"
>
<tspan
dy="16"
x="0"
v-for="(item, index) in textArr"
:key="index"
dy="16"
x="0"
v-for="(item, index) in textArr"
:key="index"
>
{{ item }}
</tspan>
</text>
<g :transform="signature">
<g :transform="signatureTranslate">
<image
xlink:href="profile.png"
width="29"
@ -54,8 +52,11 @@
</template>
<script lang="ts">
import { defineComponent, computed, onMounted, ref, reactive } from 'vue';
export default defineComponent({
import { defineComponent, onMounted, ref, reactive } from 'vue';
import useMessageItemComp from "@/composables/computed/useMessageItemComp";
import useMessageItemAnims from "@/composables/anims/useMessageItemAnims";
import useTextWrap from "@/composables/textWrap/useTextWrap";
export default defineComponent({
name: "MessageItem",
props: {
item: {
@ -64,100 +65,38 @@ import { defineComponent, computed, onMounted, ref, reactive } from 'vue';
},
},
setup(props) {
let textArr: string[] = reactive([]);
let textArr: string[] = reactive([]);
const signature = ref("");
const msgID = ref(props.item.id);
const bubbleTag = ref(`bubble${msgID.value}`);
const contentTag = ref(`content${msgID.value}`);
let bubble: any;
let content: any;
console.log(props)
const msgID = ref(props.item.id);
const messageRect = ref(`message${msgID.value}`);
const messageText = ref(`text${msgID.value}`);
const textFill = computed(() => {
switch (props.item.modifier) {
case "ai" || "sf":
return "#fff";
default:
return "#000";
}
});
const { textFill, bubbleFill, startingOffset } = useMessageItemComp(props.item.modifier);
const { textWrap } = useTextWrap();
const { drawMessage, signatureTranslate } = useMessageItemAnims({
mr: messageRect,
tr: messageText
})
const bubbleFill = computed(() => {
switch (props.item.modifier) {
case "sf":
return "#61b4f4";
case "ai":
return "#585858";
default:
return "#fff";
}
});
// wraps message text before render
textArr = textWrap({
s: props.item.content,
w: 32
});
const startingOffset = computed(() => {
switch (props.item.modifier) {
case "sf":
return "translate(10, 600)";
case "ai":
return "translate(135, 600)";
default:
return "translate(20, 600)";
}
})
function signaturePosition(): void {
if (bubble.getAttribute("height") !== null) {
const height = Number(bubble.getAttribute("height"));
const p = -10;
signature.value = `translate(-10 ${height + p})`;
}
}
function createTextBox(s: string, w: number): void {
// return tspan elements for text with correct size
const wrap = (s: string, w: number) =>
s.replace(
new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, "g"),
"$1\n"
);
textArr = wrap(s, w).split("\n");
}
function adjustRect(): void {
// must get dimensions of text box to fit bubble around
const padding = 20;
const bbox = content.getBBox();
bubble.setAttribute("x", bbox.x - padding);
bubble.setAttribute("y", bbox.y - padding);
bubble.setAttribute("width", bbox.width + 2 * padding);
bubble.setAttribute("height", bbox.height + 2 * padding);
}
// make textbox before mount
createTextBox(props.item.content, 32);
onMounted(async () => {
// update bubble and content for current message
bubble = bubbleTag.value;
content = contentTag.value;
await adjustRect();
signaturePosition();
})
return {
textFill,
bubbleFill,
startingOffset,
signaturePosition,
textArr,
signature,
contentTag,
msgID,
bubbleTag
}
onMounted(() => {
drawMessage();
})
return {
textFill,
bubbleFill,
startingOffset,
textArr,
signatureTranslate,
messageText,
messageRect
}
},
});
});
</script>
<style></style>

View file

@ -85,91 +85,91 @@
<script lang="ts">
import { defineComponent, inject, onMounted, reactive } from "vue";
import MessageItem from './UIDetails/messageItem.vue';
import InputItem from "@/components/UI/UIDetails/inputItem.vue";
import InputItem from "./UIDetails/inputItem.vue";
import Messages from "./UIDetails/messages";
export default defineComponent({
name: 'UIindex',
components: { MessageItem, InputItem },
setup() {
const anime: object | any = inject('animejs');
const timeline: typeof anime = anime.timeline({ loop: true });
const renderArr: Message[] = reactive([])
const messages = Messages;
const msgOffset = {
x: 0 ,
y: 0
}
let messenger: any;
let winW: number;
let modifier: string;
let msg: any;
let cardsDiv: any;
let prevCardH = 0;
const anime: object | any = inject('animejs');
const timeline: typeof anime = anime.timeline({ loop: true });
const renderArr: Message[] = reactive([])
const messages = Messages;
const msgOffset = {
x: 0 ,
y: 0
}
let messenger: any;
let winW: number;
let modifier: string;
let msg: any;
let cardsDiv: any;
let prevCardH = 0;
function renderMsg(i: number): void {
setTimeout(() => {
renderArr.push(messages[i]);
}, 2000);
function renderMsg(i: number): void {
setTimeout(() => {
renderArr.push(messages[i]);
}, 2000);
}
function runAnim(): void {
for (let i = 0; i < messages.length; i++) {
renderMsg(i);
}
}
async function beforeEnter(el: any) {
modifier = el.className.baseVal.substring(0, 2);
}
async function enter(el: any, done: any) {
const paddingLeft = 40;
msg = await document.getElementById(el.id);
const rect = msg.getBoundingClientRect();
const msgW = rect.width;
const msgH = rect.height;
cardsDiv = await document.getElementById("cardsDiv");
const cdBox = cardsDiv.getBBox();
if (prevCardH === 0) {
msgOffset.y += cdBox.height;
} else if (prevCardH > msgH) {
// cardsDiv is offset by 17.5
msgOffset.y += prevCardH + msgH / 2 + 17.5;
} else {
msgOffset.y += cdBox.height - 20;
}
prevCardH = msgH;
switch (modifier) {
case "sf":
msgOffset.x = winW - msgW;
break;
case "ai":
msgOffset.x = (winW - msgW) / 2;
break;
default:
msgOffset.x = paddingLeft;
}
function runAnim(): void {
for (let i = 0; i < messages.length; i++) {
renderMsg(i);
}
}
async function beforeEnter(el: any) {
modifier = el.className.baseVal.substring(0, 2);
}
async function enter(el: any, done: any) {
const paddingLeft = 40;
msg = await document.getElementById(el.id);
const rect = msg.getBoundingClientRect();
const msgW = rect.width;
const msgH = rect.height;
cardsDiv = await document.getElementById("cardsDiv");
const cdBox = cardsDiv.getBBox();
if (prevCardH === 0) {
msgOffset.y += cdBox.height;
} else if (prevCardH > msgH) {
// cardsDiv is offset by 17.5
msgOffset.y += prevCardH + msgH / 2 + 17.5;
} else {
msgOffset.y += cdBox.height - 20;
}
prevCardH = msgH;
switch (modifier) {
case "sf":
msgOffset.x = winW - msgW;
break;
case "ai":
msgOffset.x = (winW - msgW) / 2;
break;
default:
msgOffset.x = paddingLeft;
}
const translateUp = `translate(${msgOffset.x} ${msgOffset.y})`;
const translateDown = `translate(${msgOffset.x} ${msgOffset.y + 5})`;
timeline
.add({
targets: el,
transform: translateUp,
easing: "easeInOutQuad",
duration: 1000
})
.add({
targets: el,
transform: translateDown,
easing: "easeInOutQuad",
duration: 500,
offset: 1000
});
done();
}
const translateUp = `translate(${msgOffset.x} ${msgOffset.y})`;
const translateDown = `translate(${msgOffset.x} ${msgOffset.y + 5})`;
timeline
.add({
targets: el,
transform: translateUp,
easing: "easeInOutQuad",
duration: 1000
})
.add({
targets: el,
transform: translateDown,
easing: "easeInOutQuad",
duration: 500,
offset: 1000
});
done();
}
function leave() {

View file

@ -0,0 +1,43 @@
import { ref, onMounted } from "vue";
interface Ref {
value: string;
}
export default function useMessageItemAnims({ mr, tr }: { mr: Ref; tr: Ref }) {
const signatureTranslate = ref("");
let messageRect: string | SVGRectElement | SVGSVGElement | any;
let messageText: string | SVGTextElement | SVGSVGElement | any;
onMounted(() => {
messageRect = mr.value;
messageText = tr.value;
});
function adjustMessageRect(): void {
// must get dimensions of text box to fit bubble around
const padding = 20;
const textBox = messageText.getBBox();
messageRect.setAttribute("x", textBox.x - padding);
messageRect.setAttribute("y", textBox.y - padding);
messageRect.setAttribute("width", textBox.width + 2 * padding);
messageRect.setAttribute("height", textBox.height + 2 * padding);
}
function translateMsgSignature(): void {
if (messageRect.getAttribute("height") !== null) {
const height = Number(messageRect.getAttribute("height"));
const p = -10;
signatureTranslate.value = `translate(-10 ${height + p})`;
}
}
async function drawMessage() {
// ignore lint error: await needed for proper render
await adjustMessageRect();
translateMsgSignature();
}
return {
drawMessage,
signatureTranslate
};
}

View file

@ -0,0 +1,40 @@
import { computed } from "vue";
// takes in message modifier as param
export default function useMessageItemComp(m: string) {
const textFill = computed(() => {
switch (m) {
case "fr":
return "#000";
default:
return "#fff";
}
});
const bubbleFill = computed(() => {
switch (m) {
case "sf":
return "#61b4f4";
case "ai":
return "#585858";
default:
return "#fff";
}
});
const startingOffset = computed(() => {
switch (m) {
case "sf":
return "translate(10, 600)";
case "ai":
return "translate(135, 600)";
default:
return "translate(20, 600)";
}
});
return {
textFill,
bubbleFill,
startingOffset
};
}

View file

@ -0,0 +1,17 @@
export default function useTextWrap() {
// text wraps a string to a given width
function textWrap({ s, w }: { s: string; w: number }): string[] {
// return tspan elements for text with correct size
const wrap = (s: string, w: number) =>
s.replace(
new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, "g"),
"$1\n"
);
const result = wrap(s, w).split("\n");
return result;
}
return {
textWrap
};
}

View file

@ -1,31 +0,0 @@
import { reactive, onMounted } from "vue";
export default function useTextBox() {
const textArr: string[] = reactive([]);
function createTextBox(s: string, w: number): string[] {
// return tspan elements for text with correct size
const wrap = (s: string, w: number) =>
s.replace(
new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, "g"),
"$1\n"
);
const textWrap = wrap(s, w).split("\n");
return textWrap;
}
function adjustRect(rect: any, text: any): void {
// must get dimensions of text box to fit bubble around
const padding = 20;
const bbox = text.getBBox();
rect.setAttribute("x", bbox.x - padding);
rect.setAttribute("y", bbox.y - padding);
rect.setAttribute("width", bbox.width + 2 * padding);
rect.setAttribute("height", bbox.height + 2 * padding);
}
return {
textArr,
adjustRect,
createTextBox
};
}

View file

@ -1,16 +0,0 @@
import { provide, inject } from "vue";
const animeSymbol = Symbol();
export function provideAnime(anime: any) {
provide(animeSymbol, anime);
}
export function useAnime() {
const anime = inject(animeSymbol);
if (!anime) {
console.log("Error! Failed to provide AnimeJS");
// throw error, no store provided
}
return anime;
}