--- /dev/null
+<template>
+ <defs>
+ <radialGradient
+ id="radial-gradient"
+ cx="0.5"
+ cy="1"
+ r="0.54"
+ gradientTransform="translate(-1.489 1.5) rotate(-90) scale(1 1.989)"
+ gradientUnits="objectBoundingBox"
+ >
+ <stop offset="0" stop-color="#fff" />
+ <stop offset="1" stop-color="#e6e6e6" />
+ </radialGradient>
+ <filter id="Rectangle_410" x="0" y="0" width="380" height="530" filterUnits="userSpaceOnUse">
+ <feOffset dy="3" input="SourceAlpha" />
+ <feGaussianBlur stdDeviation="5" result="blur" />
+ <feFlood flood-opacity="0.129" />
+ <feComposite operator="in" in2="blur" />
+ <feComposite in="SourceGraphic" />
+ </filter>
+ </defs>
+ <g transform="matrix(1, 0, 0, 1, 0, 0)" filter="url(#Rectangle_410)">
+ <rect
+ id="Rectangle_410-2"
+ data-name="Rectangle 410"
+ width="350"
+ height="500"
+ rx="20"
+ fill="url(#radial-gradient)"
+ />
+ </g>
+</template>
+
+<script>
+import { defineComponent } from "vue";
+export default defineComponent({
+ name: "AI",
+});
+</script>
<template>
- <foreignObject x="10" y="10" width="100%" height="100%">
+ <foreignObject class="inputContainer" :x="x" y="450" width="55%" height="50%">
<div xmlns="http://www.w3.org/1999/xhtml">
- <div contenteditable
- v-if="input.length > 0"
- >
- {{ input }}
+ <div class="inputBubble" contenteditable v-show="input.length > 0">
+ <p>{{ input }}</p>
</div>
</div>
</foreignObject>
<script lang="ts">
import useKeyDownHandler from "@/composables/keyDownHandler/useKeyDownHandler";
-import { defineComponent, onUnmounted } from "vue";
+import AnimeFunc from "@/types/animejs/index";
+import {
+ defineComponent,
+ onUnmounted,
+ computed,
+ ref,
+ onMounted,
+ watch,
+ inject,
+} from "vue";
export default defineComponent({
name: "InputItem",
setup() {
- const {keyDownHandler, input} = useKeyDownHandler();
+ const emitter: any = inject("mitt");
+ emitter.on("newSelfMessage", (payload: any) => console.log("foo", payload));
+ const { keyDownHandler, input } = useKeyDownHandler();
+ const inputHeight = ref(42);
+ let inputBubble: HTMLElement;
+ const inputWidth = ref(0);
+
+ // imports animejs safely
+ let anime: AnimeFunc;
+ const animeInject: AnimeFunc | undefined = inject("animejs");
+ if (animeInject) anime = animeInject;
// add window event listener
window.addEventListener("keydown", keyDownHandler);
window.removeEventListener("keydown", keyDownHandler);
});
+ watch(input, async (input, prevInput) => {
+ const inputDivs = await document.getElementsByClassName("inputBubble");
+ const height = inputDivs[0].getBoundingClientRect().height;
+ inputWidth.value = inputDivs[0].getBoundingClientRect().width;
+ const foreignObjectDiv = document.getElementsByClassName(
+ "inputContainer"
+ );
+ if (height !== inputHeight.value) {
+ inputHeight.value = height;
+ }
+ const foreignEl = foreignObjectDiv[0] as HTMLElement;
+ if (inputHeight.value === 0) {
+ anime({
+ targets: foreignEl,
+ translateY: -10,
+ });
+ } else if (inputHeight.value > 36) {
+ anime({
+ targets: foreignEl,
+ translateY: -inputHeight.value + 30,
+ easing: "easeOutQuad",
+ duration: 150,
+ });
+ }
+ });
+
+ const x = computed(() => {
+ return `calc(50% - ${inputWidth.value / 2})`;
+ });
+
return {
- input
+ input,
+ x,
};
- }
-})
+ },
+});
</script>
<style lang="scss" scoped>
div[contenteditable] {
width: auto;
- max-width: 200px;
+ max-width: 180px;
+ height: auto;
+ min-height: 36px;
display: table;
- padding: 7px 15px 7px 15px;
+ justify-items: center;
border: 0;
- border-radius: 25px;
+ border-radius: 20px;
color: #fff;
background-color: #61b4f4;
font-size: 14;
text-align: left;
+ p {
+ max-width: 150px;
+ overflow-wrap: break-word;
+ padding: 10px 15px 10px 15px;
+ margin: 0;
+ }
}
</style>
<template>
- <g
- :id="item.id"
- :class="item.modifier"
- :transform = "startingOffset"
- >
- <rect
- ref="messageRect"
- x="30"
- y="10"
- width="196"
- height="63"
- rx="20"
- :fill="bubbleFill"
- />
- <text
- font-size="14"
- font-family="SF Pro"
- ref="messageText"
- :fill="textFill"
- >
- <tspan
- dy="16"
- x="0"
- v-for="(item, index) in textArr"
- :key="index"
- >
- {{ item }}
- </tspan>
+ <g :id="item.id" :class="item.modifier" :transform="startingOffset">
+ <rect ref="messageRect" x="30" y="10" width="196" height="63" rx="20" :fill="bubbleFill" />
+ <text font-size="14" font-family="SF Pro" ref="messageText" :fill="textFill">
+ <tspan dy="16" x="0" v-for="(item, index) in textArr" :key="index">{{ item }}</tspan>
</text>
<g :transform="signatureTranslate">
- <image
- xlink:href="profile.png"
- width="29"
- height="29"
- fill="#b5b5b5"
- clip-path="url(#clip)"
- />
+ <image xlink:href="profile.png" width="29" height="29" fill="#b5b5b5" clip-path="url(#clip)" />
<text
transform="translate(36, 17)"
fill="#888"
>
<tspan v-if="item.outgoing">You to</tspan>
<tspan v-else>From</tspan>
- <tspan xml:space="preserve" fill="#000"> {{ item.signature }}</tspan>
+ <tspan xml:space="preserve" fill="#000">{{ item.signature }}</tspan>
</text>
</g>
</g>
</template>
<script lang="ts">
-import { defineComponent, 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";
+import useTextWrap from "@/composables/textWrap/useTextWrap";
export default defineComponent({
name: "MessageItem",
props: {
item: {
type: Object,
- required: true
+ required: true,
},
},
setup(props) {
const messageRect = ref(`message${msgID.value}`);
const messageText = ref(`text${msgID.value}`);
- const { textFill, bubbleFill, startingOffset } = useMessageItemComp(props.item.modifier);
+ const { textFill, bubbleFill, startingOffset } = useMessageItemComp(
+ props.item.modifier
+ );
const { textWrap } = useTextWrap();
const { drawMessage, signatureTranslate } = useMessageItemAnims({
mr: messageRect,
tr: messageText,
- })
+ });
// wraps message text before render
textArr = textWrap({
s: props.item.content,
- w: 32
+ w: 32,
});
onMounted(() => {
drawMessage();
- })
+ });
return {
textFill,
textArr,
signatureTranslate,
messageText,
- messageRect
- }
+ messageRect,
+ };
},
});
</script>
--- /dev/null
+<template>
+ <defs>
+ <linearGradient
+ id="linear-gradient"
+ x1="0.5"
+ y1="-1.438"
+ x2="0.5"
+ y2="0.633"
+ gradientUnits="objectBoundingBox"
+ >
+ <stop offset="0" stop-color="#fff33b" stop-opacity="0" />
+ <stop offset="0.152" stop-color="#ffde46" stop-opacity="0.141" />
+ <stop offset="0.467" stop-color="#ffa764" stop-opacity="0.435" />
+ <stop offset="0.581" stop-color="#ff916f" stop-opacity="0.541" />
+ <stop offset="1" stop-color="#ff5782" />
+ </linearGradient>
+ <linearGradient
+ id="linear-gradient-2"
+ x1="0.498"
+ y1="0.728"
+ x2="0.498"
+ y2="-0.433"
+ gradientUnits="objectBoundingBox"
+ >
+ <stop offset="0" stop-color="#f7cf83" />
+ <stop offset="1" stop-color="#fff" />
+ </linearGradient>
+ <rect id="rect" width="29" height="29" rx="10" />
+ <clipPath id="clip">
+ <use xlink:href="#rect" />
+ </clipPath>
+ </defs>
+
+ <g id="titlebar" transform="translate(0, 0)">
+ <g data-name="Group 73" transform="translate(20 20)">
+ <ellipse
+ id="Oval"
+ cx="8.134"
+ cy="8.134"
+ rx="8.134"
+ ry="8.134"
+ transform="translate(0 0)"
+ fill="url(#linear-gradient)"
+ />
+ <ellipse
+ id="Oval-2"
+ data-name="Oval"
+ cx="8.134"
+ cy="8.134"
+ rx="8.134"
+ ry="8.134"
+ transform="translate(27 0)"
+ fill="url(#linear-gradient-2)"
+ />
+ </g>
+ </g>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+export default defineComponent({
+ name: "TitleBar",
+});
+</script>
xmlns:xlink="http://www.w3.org/1999/xlink"
@click="renderMessage"
>
- <defs>
- <linearGradient
- id="linear-gradient"
- x1="0.5"
- y1="-1.438"
- x2="0.5"
- y2="0.633"
- gradientUnits="objectBoundingBox"
- >
- <stop offset="0" stop-color="#fff33b" stop-opacity="0" />
- <stop offset="0.152" stop-color="#ffde46" stop-opacity="0.141" />
- <stop offset="0.467" stop-color="#ffa764" stop-opacity="0.435" />
- <stop offset="0.581" stop-color="#ff916f" stop-opacity="0.541" />
- <stop offset="1" stop-color="#ff5782" />
- </linearGradient>
- <linearGradient
- id="linear-gradient-2"
- x1="0.498"
- y1="0.728"
- x2="0.498"
- y2="-0.433"
- gradientUnits="objectBoundingBox"
- >
- <stop offset="0" stop-color="#f7cf83" />
- <stop offset="1" stop-color="#fff" />
- </linearGradient>
- <rect id="rect" width="29" height="29" rx="10" />
- <clipPath id="clip">
- <use xlink:href="#rect" />
- </clipPath>
- </defs>
+ <AI />
+
<g id="cardsDiv">
<g
v-for="(item, index) in renderArr"
v-on:appear="enter"
v-on:after-enter="afterEnter"
>
- <MessageItem
- :item="item"
- :key="index"
- />
+ <MessageItem :item="item" :key="index" />
</transition>
</g>
</g>
- <g id="titlebar" transform="translate(0, 0)">
- <g data-name="Group 73" transform="translate(20 20)">
- <ellipse
- id="Oval"
- cx="8.134"
- cy="8.134"
- rx="8.134"
- ry="8.134"
- transform="translate(0 0)"
- fill="url(#linear-gradient)"
- />
- <ellipse
- id="Oval-2"
- data-name="Oval"
- cx="8.134"
- cy="8.134"
- rx="8.134"
- ry="8.134"
- transform="translate(27 0)"
- fill="url(#linear-gradient-2)"
- />
- </g>
- </g>
+
+ <InputItem />
+ <TitleBar />
</svg>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from "vue";
-import MessageItem from './UIDetails/messageItem.vue';
+import MessageItem from "./UIDetails/messageItem.vue";
+import AI from "../AI/index.vue";
import InputItem from "./UIDetails/inputItem.vue";
+import TitleBar from "./UIDetails/titleBar.vue";
import Messages from "./UIDetails/messages";
import useUIindexAnims from "@/composables/anims/useUIindexAnims";
export default defineComponent({
- name: 'UIindex',
- components: { MessageItem },
+ name: "UIindex",
+ components: { AI, MessageItem, InputItem, TitleBar },
setup() {
- const renderArr: Message[] = reactive([])
+ const renderArr: Message[] = reactive([]);
const messages = Messages;
const count = ref(0);
const { beforeEnter, enter, afterEnter } = useUIindexAnims();
const renderMessage = () => {
- if (count.value < messages.length) {
- renderArr.push(messages[count.value])
- count.value++;
- return;
- }
- }
+ if (count.value < messages.length) {
+ renderArr.push(messages[count.value]);
+ count.value++;
+ return;
+ }
+ };
return {
- renderMessage,
- renderArr,
- beforeEnter,
- enter,
- afterEnter
- }
+ renderMessage,
+ renderArr,
+ beforeEnter,
+ enter,
+ afterEnter,
+ };
},
});
</script>
#messenger {
width: 350px;
height: 500px;
- background-color: #E6E6E6;
+ background-color: #e6e6e6;
box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.3);
border-radius: 20px;
}
+++ /dev/null
-<template>
- <div>
- Hello from the mic
- </div>
-</template>
-
-<script>
- import { defineComponent } from 'vue';
- export default defineComponent({
- name: 'Microphone',
- })
-
-</script>
}
function enter(el: SVGGElement): void {
- console.log(window.getComputedStyle(el).transform)
msg = document.getElementById(el.id);
}
- const afterEnter = (el: SVGGElement) => {
+ function afterEnter(el: SVGGElement) {
if (msg) calculateMessageOffsets(msg);
if (scrollAnim) {
scroll -= el.getBBox().height + messagePadding;
}
addMessage(el);
-
}
return {
const cardsDiv = document.getElementById('cardsDiv')
if (cardsDiv) {
const rect = cardsDiv.getBoundingClientRect();
- const sfX = 115;
+ const sfX = 120;
const aiX = 200;
- const dX = 20;
+ const dX = 40;
let Y = 600;
if (rect.height > 500) {
Y = rect.height + 75;
};
// fire event with message payload
emitter.emit("newSelfMessage", message);
+ input.value = "";
}
function keyDownHandler(e: KeyDownEvent) {
interface AnimeFuncParams {
- targets: HTMLElement | SVGGElement | SVGElement;
+ targets: Element | HTMLElement | SVGGElement | SVGElement;
translateY?: number;
transform?: string;
easing?: string;
<template>
- <UIindex />
+ <UIindex />
</template>
<script lang="ts">
-import { defineComponent, inject } from 'vue';
+import { defineComponent, inject } from "vue";
import UIindex from "@/components/UI/index.vue";
-// import Microphone from "@/components/microphone/index.vue";
export default defineComponent({
name: "HomeIndex",
components: { UIindex },
setup() {
const emitter: any = inject("mitt");
- emitter.on('newSelfMessage', (payload: any) => console.log('foo', payload) )
- }
+ emitter.on("newSelfMessage", (payload: any) => console.log("foo", payload));
+ },
});
</script>