Tried simplifying and spliting some of Movie component.
Simplified sidebar element to use props. Replaced icons with feather icons. Description gets it's own component & tries it best at figuring out if description should be truncated or not. Now it adds a element at bottom of body with the same description and compares the height to default truncated text. If the dummy element is taller we show the truncate button.
This commit is contained in:
40
index.html
40
index.html
File diff suppressed because one or more lines are too long
@@ -23,41 +23,54 @@
|
||||
<div class="movie__wrap movie__wrap--main">
|
||||
<!-- SIDEBAR ACTIONS -->
|
||||
<div class="movie__actions" v-if="movie">
|
||||
<sidebar-list-element
|
||||
:iconRef="'#iconNot_exsits'"
|
||||
:active="matched"
|
||||
:iconRefActive="'#iconExists'"
|
||||
:textActive="'Already in plex 🎉'"
|
||||
>
|
||||
Not yet in plex
|
||||
<sidebar-list-element :active="matched" :disabled="true">
|
||||
<IconThumbsUp v-if="matched" />
|
||||
<IconThumbsDown v-else class="stroke" />
|
||||
{{ !matched ? "Not yet in plex" : "Already in plex 🎉" }}
|
||||
</sidebar-list-element>
|
||||
<sidebar-list-element
|
||||
@click="sendRequest"
|
||||
:iconRef="'#iconSent'"
|
||||
:active="requested"
|
||||
:textActive="'Requested to be downloaded'"
|
||||
>
|
||||
Request to be downloaded?
|
||||
|
||||
<sidebar-list-element @click="sendRequest" :active="requested">
|
||||
<IconMail />
|
||||
{{
|
||||
!requested
|
||||
? "Request to be downloaded?"
|
||||
: "Requested to be downloaded"
|
||||
}}
|
||||
</sidebar-list-element>
|
||||
|
||||
<sidebar-list-element
|
||||
v-if="isPlexAuthenticated && matched"
|
||||
v-if="plexId && matched"
|
||||
@click="openInPlex"
|
||||
:iconString="'⏯ '"
|
||||
>
|
||||
Watch in plex now!
|
||||
</sidebar-list-element>
|
||||
|
||||
<sidebar-list-element
|
||||
v-if="
|
||||
movie &&
|
||||
movie.credits &&
|
||||
movie.credits.cast &&
|
||||
movie.credits.cast.length
|
||||
"
|
||||
:active="showCast"
|
||||
@click="() => (showCast = !showCast)"
|
||||
>
|
||||
<IconProfile class="icon stroke" />
|
||||
{{ showCast ? "Hide cast" : "Show cast" }}
|
||||
</sidebar-list-element>
|
||||
|
||||
<sidebar-list-element
|
||||
v-if="admin"
|
||||
@click="showTorrents = !showTorrents"
|
||||
:iconRef="'#icon_torrents'"
|
||||
:active="showTorrents"
|
||||
:supplementaryText="numberOfTorrentResults"
|
||||
>
|
||||
<IconMagnet class="rotate" />
|
||||
Search for torrents
|
||||
<span class="meta">{{ numberOfTorrentResults || 123 }}</span>
|
||||
</sidebar-list-element>
|
||||
<sidebar-list-element @click="openTmdb" :iconRef="'#icon_info'">
|
||||
<sidebar-list-element @click="openTmdb">
|
||||
<IconInfo />
|
||||
See more info
|
||||
</sidebar-list-element>
|
||||
</div>
|
||||
@@ -78,55 +91,43 @@
|
||||
<!-- MOVIE INFO -->
|
||||
<div class="movie__info">
|
||||
<!-- Loading placeholder -->
|
||||
<div
|
||||
class="movie__description noselect"
|
||||
@click="truncatedDescription = !truncatedDescription"
|
||||
v-if="!loading"
|
||||
>
|
||||
<span :class="truncatedDescription ? 'truncated' : null">{{
|
||||
movie.overview
|
||||
}}</span>
|
||||
<button
|
||||
v-if="movie.overview && movie.overview.length > 220"
|
||||
class="truncate-toggle"
|
||||
>
|
||||
<i>⬆</i>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="movie__description">
|
||||
<div v-if="loading">
|
||||
<loading-placeholder :count="5" />
|
||||
</div>
|
||||
|
||||
<MovieDescription v-else :description="movie.overview" />
|
||||
|
||||
<div class="movie__details" v-if="movie">
|
||||
<div v-if="movie.year">
|
||||
<h2 class="title">Release Date</h2>
|
||||
<span class="info">{{ movie.year }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="movie.rating">
|
||||
<h2 class="title">Rating</h2>
|
||||
<span class="info">{{ movie.rating }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="movie.type == 'show'">
|
||||
<h2 class="title">Seasons</h2>
|
||||
<span class="info">{{ movie.seasons }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="movie.genres">
|
||||
<h2 class="title">Genres</h2>
|
||||
<span class="info">{{ movie.genres.join(", ") }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="movie.type == 'show'">
|
||||
<h2 class="title">Production status</h2>
|
||||
<span class="info">{{ movie.production_status }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="movie.type == 'show'">
|
||||
<h2 class="title">Runtime</h2>
|
||||
<span class="info">{{ movie.runtime[0] }} minutes</span>
|
||||
</div>
|
||||
<MovieDetail
|
||||
v-if="movie.year"
|
||||
title="Release date"
|
||||
:detail="movie.year"
|
||||
/>
|
||||
<MovieDetail
|
||||
v-if="movie.rating"
|
||||
title="Rating"
|
||||
:detail="movie.rating"
|
||||
/>
|
||||
<MovieDetail
|
||||
v-if="movie.type == 'show'"
|
||||
title="Seasons"
|
||||
:detail="movie.seasons"
|
||||
/>
|
||||
<MovieDetail
|
||||
v-if="movie.genres"
|
||||
title="Genres"
|
||||
:detail="movie.genres.join(', ')"
|
||||
/>
|
||||
<MovieDetail
|
||||
v-if="movie.type == 'show'"
|
||||
title="Production status"
|
||||
:detail="movie.production_status"
|
||||
/>
|
||||
<MovieDetail
|
||||
v-if="movie.type == 'show'"
|
||||
title="Runtime"
|
||||
:detail="movie.runtime[0]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -135,14 +136,16 @@
|
||||
<div
|
||||
class="movie__admin"
|
||||
v-if="
|
||||
showCast &&
|
||||
movie &&
|
||||
movie.credits &&
|
||||
movie.credits.cast &&
|
||||
movie.credits.cast.length
|
||||
"
|
||||
>
|
||||
<h2 class="title">Cast</h2>
|
||||
<Cast :cast="movie.credits.cast" />
|
||||
<MovieDetail title="cast">
|
||||
<Cast :cast="movie.credits.cast" />
|
||||
</MovieDetail>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -161,9 +164,17 @@
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import img from "@/directives/v-image";
|
||||
import IconProfile from "../icons/IconProfile";
|
||||
import IconThumbsUp from "../icons/IconThumbsUp";
|
||||
import IconThumbsDown from "../icons/IconThumbsDown";
|
||||
import IconInfo from "../icons/IconInfo";
|
||||
import IconMail from "../icons/IconMail";
|
||||
import IconMagnet from "../icons/IconMagnet";
|
||||
import TorrentList from "./TorrentList";
|
||||
import Cast from "./Cast";
|
||||
import MovieDetail from "./ui/MovieDetail";
|
||||
import SidebarListElement from "./ui/sidebarListElem";
|
||||
import MovieDescription from "./ui/MovieDescription";
|
||||
import store from "@/store";
|
||||
import LoadingPlaceholder from "./ui/LoadingPlaceholder";
|
||||
|
||||
@@ -188,7 +199,20 @@ export default {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
components: { TorrentList, Cast, LoadingPlaceholder, SidebarListElement },
|
||||
components: {
|
||||
MovieDescription,
|
||||
MovieDetail,
|
||||
IconProfile,
|
||||
IconThumbsUp,
|
||||
IconThumbsDown,
|
||||
IconMail,
|
||||
IconInfo,
|
||||
IconMagnet,
|
||||
TorrentList,
|
||||
Cast,
|
||||
LoadingPlaceholder,
|
||||
SidebarListElement
|
||||
},
|
||||
directives: { img: img }, // TODO decide to remove or use
|
||||
data() {
|
||||
return {
|
||||
@@ -201,9 +225,9 @@ export default {
|
||||
matched: false,
|
||||
requested: false,
|
||||
showTorrents: false,
|
||||
showCast: false,
|
||||
compact: false,
|
||||
loading: true,
|
||||
truncatedDescription: true
|
||||
loading: true
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -226,13 +250,10 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters("user", ["loggedIn", "admin"]),
|
||||
...mapGetters("user", ["loggedIn", "admin", "plexId"]),
|
||||
numberOfTorrentResults: () => {
|
||||
let numTorrents = store.getters["torrentModule/resultCount"];
|
||||
return numTorrents !== null ? numTorrents + " results" : null;
|
||||
},
|
||||
isPlexAuthenticated: () => {
|
||||
return store.getters["userModule/isPlexAuthenticated"];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -282,19 +303,19 @@ export default {
|
||||
this.prevDocumentTitle = store.getters["documentTitle/title"];
|
||||
|
||||
if (this.type === "movie") {
|
||||
getMovie(this.id, true)
|
||||
getMovie(this.id, false, true)
|
||||
.then(this.parseResponse)
|
||||
.catch(error => {
|
||||
this.$router.push({ name: "404" });
|
||||
});
|
||||
} else if (this.type == "person") {
|
||||
getPerson(this.id, true)
|
||||
getPerson(this.id, false)
|
||||
.then(this.parseResponse)
|
||||
.catch(error => {
|
||||
this.$router.push({ name: "404" });
|
||||
});
|
||||
} else {
|
||||
getShow(this.id, true)
|
||||
getShow(this.id, false)
|
||||
.then(this.parseResponse)
|
||||
.catch(error => {
|
||||
this.$router.push({ name: "404" });
|
||||
@@ -366,51 +387,6 @@ header {
|
||||
}
|
||||
}
|
||||
|
||||
.truncate-toggle {
|
||||
border: none;
|
||||
background: none;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: $text-color;
|
||||
|
||||
> i {
|
||||
font-style: unset;
|
||||
font-size: 0.7rem;
|
||||
transition: 0.3s ease all;
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
border-bottom: 1px solid $text-color-50;
|
||||
}
|
||||
&::before {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
&::after {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2.title {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.2rem;
|
||||
color: $green;
|
||||
}
|
||||
|
||||
span.info {
|
||||
font-weight: 300;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.8px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.movie {
|
||||
&__wrap {
|
||||
display: flex;
|
||||
@@ -493,40 +469,9 @@ span.info {
|
||||
&__info {
|
||||
margin-left: 0;
|
||||
}
|
||||
&__description {
|
||||
font-weight: 300;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 20px;
|
||||
|
||||
& .truncated {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
& + .truncate-toggle > i {
|
||||
transform: rotateY(0deg) rotateZ(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
@include tablet-min {
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
&__details {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> div {
|
||||
margin-bottom: 20px;
|
||||
margin-right: 20px;
|
||||
@include tablet-min {
|
||||
margin-bottom: 30px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__admin {
|
||||
width: 100%;
|
||||
|
||||
123
src/components/ui/MovieDescription.vue
Normal file
123
src/components/ui/MovieDescription.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div
|
||||
id="description"
|
||||
class="movie-description noselect"
|
||||
@click="overflow ? (truncated = !truncated) : null"
|
||||
>
|
||||
<span ref="description" :class="{ truncated }">{{ description }}</span>
|
||||
|
||||
<button v-if="description && overflow" class="truncate-toggle">
|
||||
<IconArrowDown :class="{ rotate: !truncated }" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconArrowDown from "../../icons/IconArrowDown";
|
||||
export default {
|
||||
components: { IconArrowDown },
|
||||
props: {
|
||||
description: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
truncated: true,
|
||||
overflow: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.checkDescriptionOverflowing();
|
||||
},
|
||||
methods: {
|
||||
checkDescriptionOverflowing() {
|
||||
const descriptionEl = document.getElementById("description");
|
||||
if (!descriptionEl) return;
|
||||
|
||||
const { height, width } = descriptionEl.getBoundingClientRect();
|
||||
const { fontSize, lineHeight } = getComputedStyle(descriptionEl);
|
||||
|
||||
const elementWithoutOverflow = document.createElement("div");
|
||||
elementWithoutOverflow.setAttribute(
|
||||
"style",
|
||||
`max-width: ${width}px; display: block; font-size: ${fontSize}; line-height: ${lineHeight};`
|
||||
);
|
||||
|
||||
elementWithoutOverflow.classList.add("dummy-non-overflow");
|
||||
elementWithoutOverflow.innerText = this.description;
|
||||
|
||||
document.body.appendChild(elementWithoutOverflow);
|
||||
const elemWithoutOverflowHeight =
|
||||
elementWithoutOverflow.getBoundingClientRect()["height"];
|
||||
|
||||
this.overflow = elemWithoutOverflowHeight > height;
|
||||
this.removeElements(document.querySelectorAll(".dummy-non-overflow"));
|
||||
},
|
||||
removeElements: elems => elems.forEach(el => el.remove())
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./src/scss/media-queries";
|
||||
|
||||
.movie-description {
|
||||
font-weight: 300;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 20px;
|
||||
transition: all 1s ease;
|
||||
|
||||
@include tablet-min {
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
span.truncated {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.truncate-toggle {
|
||||
border: none;
|
||||
background: none;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: var(--text-color);
|
||||
margin-top: 1rem;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
transition: 0.4s ease all;
|
||||
|
||||
@include mobile {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
&.rotate {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
border-bottom: 1px solid var(--text-color-50);
|
||||
}
|
||||
&::before {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
&::after {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
src/components/ui/MovieDetail.vue
Normal file
52
src/components/ui/MovieDetail.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="movie-detail">
|
||||
<h2 class="title">{{ title }}</h2>
|
||||
<span v-if="detail" class="info">{{ detail }}</span>
|
||||
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
detail: {
|
||||
required: false,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./src/scss/media-queries";
|
||||
|
||||
.movie-detail {
|
||||
margin-bottom: 20px;
|
||||
margin-right: 20px;
|
||||
|
||||
@include tablet-min {
|
||||
margin-bottom: 30px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
h2.title {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.2rem;
|
||||
color: var(--color-green);
|
||||
}
|
||||
|
||||
span.info {
|
||||
font-weight: 300;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.8px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,76 +1,25 @@
|
||||
<template>
|
||||
<div>
|
||||
<a @click="$emit('click')">
|
||||
<li>
|
||||
<figure v-if="iconRef" :class="activeClassIfActive">
|
||||
<svg class="icon"><use :xlink:href="iconRefNameIfActive"/></svg>
|
||||
</figure>
|
||||
|
||||
<span class="text" :class="activeClassIfActive">{{ contentTextToDisplay }}</span>
|
||||
|
||||
<span v-if="supplementaryText" class="supplementary-text">
|
||||
{{ supplementaryText }}
|
||||
</span>
|
||||
</li>
|
||||
</a>
|
||||
</div>
|
||||
<li @click="event => $emit('click', event)" :class="{ active, disabled }">
|
||||
<slot></slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// TODO if a image is hovered and we can't set the hover color we want to
|
||||
// go into it and change the fill
|
||||
export default {
|
||||
props: {
|
||||
iconRef: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
iconRefActive: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: false
|
||||
},
|
||||
textActive: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
supplementaryText: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iconRefNameIfActive() {
|
||||
const { iconRefActive, iconRef, active } = this
|
||||
|
||||
if ((iconRefActive && iconRef) && active) {
|
||||
return iconRefActive
|
||||
}
|
||||
return iconRef
|
||||
},
|
||||
contentTextToDisplay() {
|
||||
const { textActive, active, $slots } = this
|
||||
|
||||
if (textActive && active)
|
||||
return textActive
|
||||
|
||||
if ($slots.default && $slots.default.length > 0)
|
||||
return $slots.default[0].text
|
||||
|
||||
return ''
|
||||
},
|
||||
activeClassIfActive() {
|
||||
return this.active ? 'active' : ''
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./src/scss/variables";
|
||||
@import "./src/scss/media-queries";
|
||||
|
||||
li {
|
||||
@@ -78,61 +27,58 @@ li {
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
color: $text-color-50;
|
||||
transition: color 0.5s ease;
|
||||
font-size: 11px;
|
||||
color: var(--text-color-50);
|
||||
font-size: 12px;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid $text-color-5;
|
||||
border-bottom: 1px solid var(--text-color-5);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $text-color;
|
||||
cursor: pointer;
|
||||
svg {
|
||||
margin-right: 1rem;
|
||||
transition: all 0.5s ease;
|
||||
stroke: var(--text-color-70);
|
||||
|
||||
.icon {
|
||||
fill: $text-color;
|
||||
cursor: pointer;
|
||||
transform: scale(1.1, 1.1);
|
||||
&[data-fill] {
|
||||
stroke: none;
|
||||
fill: vaR(--text-color-70);
|
||||
}
|
||||
}
|
||||
.active {
|
||||
color: $text-color;
|
||||
|
||||
.icon {
|
||||
fill: $green;
|
||||
&:hover,
|
||||
&.active {
|
||||
color: var(--text-color);
|
||||
|
||||
svg {
|
||||
stroke: var(--text-color);
|
||||
transform: scale(1.1, 1.1);
|
||||
|
||||
&[data-fill] {
|
||||
stroke: none;
|
||||
fill: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active svg {
|
||||
stroke: var(--color-green);
|
||||
|
||||
&[data-fill] {
|
||||
stroke: none;
|
||||
fill: var(--color-green);
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pending {
|
||||
color: #f8bd2d;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-left: 26px;
|
||||
}
|
||||
|
||||
.supplementary-text {
|
||||
flex-grow: 1;
|
||||
.meta {
|
||||
margin-left: auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
figure {
|
||||
position: absolute;
|
||||
|
||||
> svg {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0 7px 0 0;
|
||||
fill: $text-color-50;
|
||||
transition: fill 0.5s ease, transform 0.5s ease;
|
||||
|
||||
& .waiting {
|
||||
transform: scale(0.8, 0.8);
|
||||
}
|
||||
& .pending {
|
||||
fill: #f8bd2d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
16
src/icons/IconArrowDown.vue
Normal file
16
src/icons/IconArrowDown.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<polyline points="19 12 12 19 5 12"></polyline>
|
||||
</svg>
|
||||
</template>
|
||||
19
src/icons/IconInfo.vue
Normal file
19
src/icons/IconInfo.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="2" y1="12" x2="22" y2="12"></line>
|
||||
<path
|
||||
d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
37
src/icons/IconMagnet.vue
Normal file
37
src/icons/IconMagnet.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 512 512"
|
||||
fill="currentColor"
|
||||
data-fill=""
|
||||
stroke="none"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
style="transform: rotate(-45deg); margin-top: -7px"
|
||||
>
|
||||
<g transform="translate(0,512) scale(0.1,-0.1)">
|
||||
<path
|
||||
d="M1420 4173 c-956 -958 -1013 -1019 -1133 -1213 -401 -651 -377 -1492
|
||||
61 -2125 287 -415 711 -693 1222 -803 116 -24 143 -26 375 -27 236 0 257 2
|
||||
383 28 315 66 598 199 847 397 106 84 1920 1895 1936 1931 6 16 9 39 5 51 -3
|
||||
13 -149 167 -325 343 -301 299 -322 319 -356 319 -34 0 -80 -44 -963 -925
|
||||
-959 -958 -985 -981 -1127 -1044 -337 -149 -721 -96 -993 138 -367 315 -433
|
||||
848 -154 1247 27 40 401 422 963 985 866 867 919 923 919 955 0 33 -23 58
|
||||
-323 358 -315 315 -323 322 -362 322 -40 0 -46 -6 -975 -937z m1235 37 l-220
|
||||
-220 -240 240 -240 240 220 220 220 220 240 -240 240 -240 -220 -220z m-575
|
||||
-105 l234 -235 -605 -607 c-632 -635 -663 -669 -747 -838 -123 -251 -139 -591
|
||||
-37 -860 55 -148 142 -281 260 -397 378 -377 971 -423 1414 -111 38 27 320
|
||||
300 671 649 l605 604 240 -240 240 -241 -610 -608 c-623 -621 -710 -699 -885
|
||||
-801 -199 -116 -422 -195 -655 -231 -103 -16 -397 -16 -500 0 -403 63 -748
|
||||
241 -1025 527 -328 339 -500 764 -500 1234 0 434 143 821 429 1160 73 86 1213
|
||||
1230 1226 1230 5 0 116 -106 245 -235z m2615 -1935 l-220 -220 -240 240 -240
|
||||
240 220 220 220 220 240 -240 240 -240 -220 -220z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
18
src/icons/IconMail.vue
Normal file
18
src/icons/IconMail.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
|
||||
></path>
|
||||
<polyline points="22,6 12,13 2,6"></polyline>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -9,7 +9,6 @@
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
style="transition: stroke-width 0.5s ease"
|
||||
>
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
|
||||
17
src/icons/IconThumbsDown.vue
Normal file
17
src/icons/IconThumbsDown.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
17
src/icons/IconThumbsUp.vue
Normal file
17
src/icons/IconThumbsUp.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path
|
||||
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
Reference in New Issue
Block a user