Merge branch 'message_renderer' into 'master'

Message renderer

See merge request crimata-ai/crimata-electron!6
This commit is contained in:
Enrique Hernandez 2020-11-06 18:44:55 +00:00
commit 42dcfd8ff0
14 changed files with 1274 additions and 1091 deletions

View file

@ -23,8 +23,8 @@
<script lang="ts">
import { defineComponent, onMounted, ref, reactive } from "vue";
import useMessageItemComp from "@/composables/computed/useMessageItemComp";
import useMessageItemAnims from "@/composables/anims/useMessageItemAnims";
import useMessageItemComp from "@/composables/messageItem/useMessageItemComp";
import useMessageItemAnims from "@/composables/messageItem/useMessageItemAnims";
import useTextWrap from "@/composables/textWrap/useTextWrap";
export default defineComponent({
name: "MessageItem",

View file

@ -0,0 +1,54 @@
<template>
<g id="messageRenderer">
<g
v-for="(item, index) in renderArr"
:key="index"
:id="index"
class="messageList"
transform="translate(0, 0)"
>
<transition
appear
v-on:before-appear="beforeEnter"
v-on:appear="enter"
v-on:after-enter="afterEnter"
>
<MessageItem :item="item" :key="index" />
</transition>
</g>
</g>
</template>
<script lang="ts">
import useMessageRenderer from "@/composables/messageRenderer/useMessageRenderer";
import { defineComponent, reactive, ref, inject } from "vue";
import MessageItem from "./messageItem.vue";
import { Message } from "@/types/message/index";
import { Mitt } from "@/types/mitt/index";
export default defineComponent({
name: "MessageRenderer",
components: { MessageItem },
setup() {
const renderArr: Message[] = reactive([]);
// imports mitt Emitter safely
let emitter: Mitt;
const emitterInject: Mitt | undefined = inject("mitt");
if (emitterInject) {
emitter = emitterInject;
emitter.on("renderMessage", (payload: Message | undefined) => {
if (payload) renderArr.push(payload);
});
}
const { beforeEnter, enter, afterEnter } = useMessageRenderer();
return {
renderArr,
beforeEnter,
enter,
afterEnter,
};
},
});
</script>

View file

@ -4,66 +4,45 @@
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
@click="renderMessage"
@click="emittMessage"
>
<AI />
<g id="cardsDiv">
<g
v-for="(item, index) in renderArr"
:key="index"
:id="index"
class="test"
transform="translate(0, 0)"
>
<transition
appear
v-on:before-appear="beforeEnter"
v-on:appear="enter"
v-on:after-enter="afterEnter"
>
<MessageItem :item="item" :key="index" />
</transition>
</g>
</g>
<MessageRenderer />
<InputItem />
<TitleBar />
</svg>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from "vue";
import MessageItem from "./UIDetails/messageItem.vue";
import AI from "../AI/index.vue";
import MessageRenderer from "./UIDetails/messageRenderer.vue";
import InputItem from "./UIDetails/inputItem.vue";
import TitleBar from "./UIDetails/titleBar.vue";
import Messages from "./UIDetails/messages";
import useUIindexAnims from "@/composables/anims/useUIindexAnims";
import { Mitt } from "@/types/mitt/index";
import { defineComponent, ref, inject } from "vue";
export default defineComponent({
name: "UIindex",
components: { AI, MessageItem, InputItem, TitleBar },
name: "UI",
components: { AI, MessageRenderer, InputItem, TitleBar },
setup() {
const renderArr: Message[] = reactive([]);
const messages = Messages;
const count = ref(0);
const { beforeEnter, enter, afterEnter } = useUIindexAnims();
// imports mitt Emitter safely
let emitter: Mitt;
const emitterInject: Mitt | undefined = inject("mitt");
if (emitterInject) emitter = emitterInject;
const renderMessage = () => {
function emittMessage() {
if (count.value < messages.length) {
renderArr.push(messages[count.value]);
emitter.emit("renderMessage", messages[count.value]);
count.value++;
return;
}
};
}
return {
renderMessage,
renderArr,
beforeEnter,
enter,
afterEnter,
emittMessage,
};
},
});

View file

@ -1,145 +0,0 @@
import { inject, onMounted } from "vue";
import AnimeFunc from "@/types/animejs/index";
import { MessageAnimeFunc } from '@/types/message/index';
export default function useUIindexAnims() {
// imports animejs safely
let anime: AnimeFunc;
const animeInject: AnimeFunc | undefined = inject("animejs");
if (animeInject) anime = animeInject;
const msgOffset = {
x: 0,
y: 0
};
let winW: number;
let modifier: string;
let msg: HTMLElement | null;
let prevCardH = 0;
let messenger: HTMLElement | null;
let renderList: NodeList;
let scrollAnim = false;
let scroll = 0;
const messagePadding = 10;
onMounted(() => {
messenger = document.getElementById("messenger");
if (messenger) winW = messenger.clientWidth;
});
function beforeEnter(el: SVGGElement): void {
modifier = el.className.baseVal.substring(0, 2);
}
function scrollOffset(l: NodeList, height: number): void {
for (const card of l) {
const c = card.parentNode as HTMLElement;
anime({
targets: c,
transform: `translate(0 ${height})`,
easing: "easeInOutQuad",
duration: 500,
});
}
}
function setRenderList(): void {
const messages = document.querySelectorAll(".test");
if (messages.length) renderList = messages;
}
const scrollAnimate: MessageAnimeFunc = (el, transform) => {
setRenderList();
anime({
targets: el, transform: transform.initialTranslate,
easing: "easeInOutQuad",
duration: 500,
begin: function() {
scrollOffset(renderList, scroll);
},
complete: function() {
anime({
targets: el,
transform: transform.finalTranslate,
offset: 1000
});
}
});
}
const stackAnimate: MessageAnimeFunc = (el, transform) => {
anime({
targets: el,
transform: transform.initialTranslate,
easing: "easeInOutQuad",
duration: 1000,
complete: function() {
anime({
targets: el,
transform: transform.finalTranslate,
easing: "easeInOutQuad",
duration: 500,
offset: 1000
});
}
});
}
function addMessage(el: SVGGElement): void {
const transform = {
initialTranslate: `translate(${msgOffset.x} ${msgOffset.y})`,
finalTranslate: `translate(${msgOffset.x} ${msgOffset.y + 5})`
}
scrollAnim ? scrollAnimate(el, transform) : stackAnimate(el, transform);
}
function setXoffset(messageWidth: number): void {
const paddingLeft = 40;
switch (modifier) {
case "sf":
msgOffset.x = winW - messageWidth;
break;
case "ai":
msgOffset.x = (winW - messageWidth) / 2;
break;
default:
msgOffset.x = paddingLeft;
}
}
function setYoffset(messageHeight: number): void {
if (prevCardH === 0) {
msgOffset.y = 40;
} else {
msgOffset.y += prevCardH + messagePadding;
}
prevCardH = messageHeight;
scrollAnim = msgOffset.y > 450 ? true : false;
}
function calculateMessageOffsets(msg: HTMLElement): void {
const msgRect = msg.getBoundingClientRect();
setXoffset(msgRect.width)
setYoffset(msgRect.height)
}
function enter(el: SVGGElement): void {
msg = document.getElementById(el.id);
}
function afterEnter(el: SVGGElement) {
if (msg) calculateMessageOffsets(msg);
if (scrollAnim) {
scroll -= el.getBBox().height + messagePadding;
}
addMessage(el);
}
return {
beforeEnter,
enter,
msgOffset,
addMessage,
afterEnter
};
}

View file

@ -42,7 +42,6 @@ export default function useKeyDownHandler() {
if (e.shiftKey) {
iCol = 1;
}
console.log(e.shiftKey);
const ch = keyboardCharMap[e.keyCode][iCol];
// Optionally do things with non-printables,
// like CR, ESC, Backspace, LF, Tab, Arrows, F1-F24, etc.

View file

@ -22,9 +22,9 @@ export default function useMessageItemComp(m: string) {
});
const startingOffset = computed(() => {
const cardsDiv = document.getElementById('cardsDiv')
if (cardsDiv) {
const rect = cardsDiv.getBoundingClientRect();
const messageRenderer = document.getElementById('messageRenderer')
if (messageRenderer) {
const rect = messageRenderer.getBoundingClientRect();
const sfX = 120;
const aiX = 200;
const dX = 40;

View file

@ -0,0 +1,50 @@
import useRendererAnims from "./useRendererAnims";
import useOffsetCalculator from './useOffsetCalculator';
export default function useMessageRenderer() {
const { calculateMessageOffsets } = useOffsetCalculator();
const { shiftAnimate, stackAnimate } = useRendererAnims();
let messageList: NodeList;
let modifier: string;
let message: HTMLElement | null;
let messageOffset = { x: 0, y: 0 };
let shift = false;
let shiftValue = 0;
function animateMessage(el: SVGGElement): void {
const transform = {
initialTranslate: `translate(${messageOffset.x} ${messageOffset.y})`,
finalTranslate: `translate(${messageOffset.x} ${messageOffset.y + 5})`
}
shift ? shiftAnimate(el, transform, shiftValue, messageList) : stackAnimate(el, transform);
}
function beforeEnter(el: SVGGElement): void {
modifier = el.className.baseVal.substring(0, 2);
const queryResult = document.querySelectorAll(".messageList");
if (queryResult.length) messageList = queryResult;
}
function enter(el: SVGGElement): void {
message = document.getElementById(el.id);
}
function afterEnter(el: SVGGElement) {
const messagePadding = 10;
if (message) {
messageOffset = calculateMessageOffsets(message, modifier);
shift = messageOffset.y > 450 ? true : false;
}
if (shift) {
shiftValue -= el.getBBox().height + messagePadding;
}
animateMessage(el);
}
return {
beforeEnter,
enter,
afterEnter
};
}

View file

@ -0,0 +1,52 @@
import { ref, onMounted } from "vue";
export default function useOffsetCalculator() {
const xOffset = ref(0);
const yOffset = ref(0);
let messenger: HTMLElement | null;
let winW: number;
let previousMessageHeight = 0;
const messagePadding = 10;
onMounted(() => {
messenger = document.getElementById("messenger");
if (messenger) winW = messenger.clientWidth;
})
function setXoffset(messageWidth: number, modifier: string): void {
const paddingLeft = 40;
switch (modifier) {
case "sf":
xOffset.value = winW - messageWidth;
break;
case "ai":
xOffset.value = (winW - messageWidth) / 2;
break;
default:
xOffset.value = paddingLeft;
}
}
function setYoffset(messageHeight: number): void {
if (previousMessageHeight === 0) {
yOffset.value = 40;
} else {
yOffset.value += previousMessageHeight + messagePadding;
}
previousMessageHeight = messageHeight;
}
const calculateMessageOffsets = (msg: HTMLElement, modifier: string) => {
const msgRect = msg.getBoundingClientRect();
setXoffset(msgRect.width, modifier)
setYoffset(msgRect.height)
const offsets = {
x: xOffset.value as number,
y: yOffset.value as number
};
return offsets;
}
return {
calculateMessageOffsets,
}
}

View file

@ -0,0 +1,67 @@
import { inject } from "vue";
import AnimeFunc from "@/types/animejs/index";
import { MessageAnimeFunc } from '@/types/message/index';
export default function useRendererAnims() {
// imports animejs safely
let anime: AnimeFunc;
const animeInject: AnimeFunc | undefined = inject("animejs");
if (animeInject) anime = animeInject;
function shiftList({ targetList, sv }: { targetList: NodeList | undefined; sv: number | undefined }) {
if (targetList) {
for (const element of targetList) {
const el = element.parentNode as HTMLElement;
anime({
targets: el,
transform: `translate(0 ${sv})`,
easing: "easeInOutQuad",
duration: 500,
});
}
}
}
const shiftAnimate: MessageAnimeFunc = (el, transform, shiftValue, targetList) => {
anime({
targets: el, transform: transform.initialTranslate,
easing: "easeInOutQuad",
duration: 500,
begin: function() {
shiftList({
targetList,
sv: shiftValue
});
},
complete: function() {
anime({
targets: el,
transform: transform.finalTranslate,
offset: 1000
});
}
});
}
const stackAnimate: MessageAnimeFunc = (el, transform) => {
anime({
targets: el,
transform: transform.initialTranslate,
easing: "easeInOutQuad",
duration: 1000,
complete: function() {
anime({
targets: el,
transform: transform.finalTranslate,
easing: "easeInOutQuad",
duration: 500,
offset: 1000
});
}
});
}
return {
shiftAnimate,
stackAnimate
};
}

View file

@ -13,6 +13,6 @@ interface MessageTransform {
}
export interface MessageAnimeFunc {
(message: SVGElement, transform: MessageTransform): void;
(message: SVGElement, transform: MessageTransform, scrollHeight?: number | undefined, targetList?: NodeList | undefined): void;
}

3
src/types/mitt/index.ts Normal file
View file

@ -0,0 +1,3 @@
import { Emitter } from "mitt";
export type Mitt = Emitter;

View file

@ -1,17 +1,12 @@
<template>
<UIindex />
<UI />
</template>
<script lang="ts">
import { defineComponent, inject } from "vue";
import UIindex from "@/components/UI/index.vue";
import { defineComponent } from "vue";
import UI from "@/components/UI/index.vue";
export default defineComponent({
name: "HomeIndex",
components: { UIindex },
setup() {
const emitter: any = inject("mitt");
emitter.on("newSelfMessage", (payload: any) => console.log("foo", payload));
},
components: { UI },
});
</script>

1925
yarn.lock

File diff suppressed because it is too large Load diff