refactor messageItem component
This commit is contained in:
parent
0a3bf4ca42
commit
9272dfb179
8 changed files with 212 additions and 221 deletions
|
|
@ -45,5 +45,4 @@ div[contenteditable] {
|
||||||
font-size: 14;
|
font-size: 14;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@
|
||||||
:transform = "startingOffset"
|
:transform = "startingOffset"
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
ref="bubbleTag"
|
ref="messageRect"
|
||||||
id="#bubble"
|
|
||||||
x="30"
|
x="30"
|
||||||
y="10"
|
y="10"
|
||||||
width="196"
|
width="196"
|
||||||
|
|
@ -17,8 +16,7 @@
|
||||||
<text
|
<text
|
||||||
font-size="14"
|
font-size="14"
|
||||||
font-family="SF Pro"
|
font-family="SF Pro"
|
||||||
id="#message"
|
ref="messageText"
|
||||||
ref="contentTag"
|
|
||||||
:fill="textFill"
|
:fill="textFill"
|
||||||
>
|
>
|
||||||
<tspan
|
<tspan
|
||||||
|
|
@ -30,7 +28,7 @@
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</tspan>
|
</tspan>
|
||||||
</text>
|
</text>
|
||||||
<g :transform="signature">
|
<g :transform="signatureTranslate">
|
||||||
<image
|
<image
|
||||||
xlink:href="profile.png"
|
xlink:href="profile.png"
|
||||||
width="29"
|
width="29"
|
||||||
|
|
@ -54,7 +52,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, onMounted, ref, reactive } from 'vue';
|
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({
|
export default defineComponent({
|
||||||
name: "MessageItem",
|
name: "MessageItem",
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -64,100 +65,38 @@ import { defineComponent, computed, onMounted, ref, reactive } from 'vue';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
|
||||||
let textArr: string[] = reactive([]);
|
let textArr: string[] = reactive([]);
|
||||||
const signature = ref("");
|
|
||||||
const msgID = ref(props.item.id);
|
const msgID = ref(props.item.id);
|
||||||
const bubbleTag = ref(`bubble${msgID.value}`);
|
const messageRect = ref(`message${msgID.value}`);
|
||||||
const contentTag = ref(`content${msgID.value}`);
|
const messageText = ref(`text${msgID.value}`);
|
||||||
let bubble: any;
|
|
||||||
let content: any;
|
|
||||||
console.log(props)
|
|
||||||
|
|
||||||
const textFill = computed(() => {
|
const { textFill, bubbleFill, startingOffset } = useMessageItemComp(props.item.modifier);
|
||||||
switch (props.item.modifier) {
|
const { textWrap } = useTextWrap();
|
||||||
case "ai" || "sf":
|
const { drawMessage, signatureTranslate } = useMessageItemAnims({
|
||||||
return "#fff";
|
mr: messageRect,
|
||||||
default:
|
tr: messageText
|
||||||
return "#000";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const bubbleFill = computed(() => {
|
|
||||||
switch (props.item.modifier) {
|
|
||||||
case "sf":
|
|
||||||
return "#61b4f4";
|
|
||||||
case "ai":
|
|
||||||
return "#585858";
|
|
||||||
default:
|
|
||||||
return "#fff";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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 {
|
// wraps message text before render
|
||||||
if (bubble.getAttribute("height") !== null) {
|
textArr = textWrap({
|
||||||
const height = Number(bubble.getAttribute("height"));
|
s: props.item.content,
|
||||||
const p = -10;
|
w: 32
|
||||||
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();
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
drawMessage();
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
textFill,
|
textFill,
|
||||||
bubbleFill,
|
bubbleFill,
|
||||||
startingOffset,
|
startingOffset,
|
||||||
signaturePosition,
|
|
||||||
textArr,
|
textArr,
|
||||||
signature,
|
signatureTranslate,
|
||||||
contentTag,
|
messageText,
|
||||||
msgID,
|
messageRect
|
||||||
bubbleTag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, inject, onMounted, reactive } from "vue";
|
import { defineComponent, inject, onMounted, reactive } from "vue";
|
||||||
import MessageItem from './UIDetails/messageItem.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";
|
import Messages from "./UIDetails/messages";
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'UIindex',
|
name: 'UIindex',
|
||||||
|
|
|
||||||
43
src/composables/anims/useMessageItemAnims.ts
Normal file
43
src/composables/anims/useMessageItemAnims.ts
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
40
src/composables/computed/useMessageItemComp.ts
Normal file
40
src/composables/computed/useMessageItemComp.ts
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
17
src/composables/textWrap/useTextWrap.ts
Normal file
17
src/composables/textWrap/useTextWrap.ts
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue