mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-24 00:36:14 -04:00
feat: added memo base
This commit is contained in:
parent
1034296359
commit
0a4abde23d
16 changed files with 537 additions and 151 deletions
|
@ -13,132 +13,121 @@
|
|||
<v-progress-circular
|
||||
indeterminate
|
||||
color="primary"
|
||||
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>Drag & drop a file here</p>
|
||||
<p class="or">
|
||||
or
|
||||
</p>
|
||||
<v-btn depressed @click="manualUploadClicked">
|
||||
select a file
|
||||
</v-btn>
|
||||
<p class="or">or</p>
|
||||
<v-btn depressed @click="manualUploadClicked">select a file</v-btn>
|
||||
<input ref="uploadInput" type="file" hidden @change="(e) => handleFiles(e.target.files[0])">
|
||||
|
||||
<div v-if="allowUrl">
|
||||
<p class="or">
|
||||
or
|
||||
</p>
|
||||
<p class="or">or</p>
|
||||
<v-text-field
|
||||
ref="urlInput"
|
||||
@click:append="urlFilled(url)"
|
||||
@keypress.enter="urlFilled(url)"
|
||||
v-model="url"
|
||||
append-icon="fa-arrow-right"
|
||||
dense
|
||||
label="Paste the file url"
|
||||
outlined
|
||||
:error-messages="urlErrors"
|
||||
@click:append="urlFilled(url)"
|
||||
@keypress.enter="urlFilled(url)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'nuxt-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class FileUploader extends Vue {
|
||||
@Prop({default: true}) readonly allowUrl!: boolean
|
||||
dragging = false
|
||||
urlErrors = null
|
||||
dragEnterCounter = 0
|
||||
url = ''
|
||||
loading = false
|
||||
|
||||
imageDropped(e) {
|
||||
this.dragging = false
|
||||
if (e.dataTransfer.items.length > 0) {
|
||||
const item = e.dataTransfer.items[0]
|
||||
switch (item.kind) {
|
||||
case 'string':
|
||||
item.getAsString(url => this.urlFilled(url))
|
||||
break
|
||||
case 'file':
|
||||
this.handleFiles(item.getAsFile())
|
||||
break
|
||||
<script>
|
||||
import * as axios from "axios";
|
||||
export default {
|
||||
name: "FileUploader",
|
||||
props: {
|
||||
allowUrl: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragging: false,
|
||||
urlErrors: undefined,
|
||||
dragEnterCounter: 0,
|
||||
url: '',
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
imageDropped(e) {
|
||||
this.dragging = false;
|
||||
if (e.dataTransfer.items.length > 0) {
|
||||
const item = e.dataTransfer.items[0];
|
||||
switch (item.kind) {
|
||||
case 'string':
|
||||
item.getAsString(url => this.urlFilled(url));
|
||||
break;
|
||||
case 'file':
|
||||
this.handleFiles(item.getAsFile());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dragEnter() {
|
||||
this.dragEnterCounter++
|
||||
this.dragging = true
|
||||
}
|
||||
|
||||
dragLeave() {
|
||||
if (--this.dragEnterCounter <= 0) {
|
||||
this.dragging = false
|
||||
}
|
||||
}
|
||||
|
||||
async urlFilled(url) {
|
||||
if (url && url.length > 0) {
|
||||
this.loading = true
|
||||
try {
|
||||
const {data, headers} = await axios.get(url)
|
||||
const name = url.split('/').pop()
|
||||
const file = new File([data], name, {type: headers['content-type']})
|
||||
this.handleFiles(file)
|
||||
} catch (ignored) {
|
||||
this.urlErrors = 'Incorrect url'
|
||||
},
|
||||
dragEnter() {
|
||||
this.dragEnterCounter++;
|
||||
this.dragging = true;
|
||||
},
|
||||
dragLeave() {
|
||||
if (--this.dragEnterCounter <= 0) {
|
||||
this.dragging = false;
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
async urlFilled(url) {
|
||||
if (url && url.length > 0) {
|
||||
this.loading = true;
|
||||
try {
|
||||
const {data, headers} = await axios.get(url);
|
||||
const name = url.split('/').pop();
|
||||
const file = new File([data], name, {type: headers['content-type']})
|
||||
this.handleFiles(file);
|
||||
} catch (ignored) {
|
||||
this.urlErrors = 'Incorrect url'
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
dragEnd() {
|
||||
this.dragging = false;
|
||||
},
|
||||
dragExit() {
|
||||
this.dragging = false;
|
||||
},
|
||||
handleFiles(file) {
|
||||
if (!file) return;
|
||||
this.$emit('input', file)
|
||||
},
|
||||
manualUploadClicked() {
|
||||
this.$refs.uploadInput.click()
|
||||
}
|
||||
}
|
||||
|
||||
dragEnd() {
|
||||
this.dragging = false
|
||||
}
|
||||
|
||||
dragExit() {
|
||||
this.dragging = false
|
||||
}
|
||||
|
||||
handleFiles(file) {
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
this.$emit('input', file)
|
||||
}
|
||||
|
||||
manualUploadClicked() {
|
||||
this.$refs.uploadInput.click()
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.drop-area {
|
||||
border: 2px dashed rgba(255,255,255,0.3);
|
||||
border: 2px dashed #363636;
|
||||
border-radius: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.03);
|
||||
|
||||
& > *, .v-btn {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.or {
|
||||
opacity: 0.7;
|
||||
margin: 5px 0 !important;
|
||||
}
|
||||
|
||||
&.drag-over {
|
||||
border-color: var(--v-primary-base);
|
||||
}
|
||||
|
||||
.v-input__icon {
|
||||
button {
|
||||
margin-top: 0 !important;
|
||||
|
@ -146,3 +135,152 @@ export default class FileUploader extends Vue {
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--<template>-->
|
||||
<!-- <div-->
|
||||
<!-- class="drop-area pa-4 text-center"-->
|
||||
<!-- :class="{'drag-over':dragging, 'pb-0':!loading}"-->
|
||||
<!-- @dragover.prevent-->
|
||||
<!-- @drop.prevent="imageDropped"-->
|
||||
<!-- @dragenter="dragEnter()"-->
|
||||
<!-- @dragend="dragEnd()"-->
|
||||
<!-- @dragleave="dragLeave()"-->
|
||||
<!-- @dragexit="dragExit()"-->
|
||||
<!-- >-->
|
||||
<!-- <div v-if="loading">-->
|
||||
<!-- <v-progress-circular-->
|
||||
<!-- indeterminate-->
|
||||
<!-- color="primary"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-else>-->
|
||||
<!-- <p>Drag & drop a file here</p>-->
|
||||
<!-- <p class="or">-->
|
||||
<!-- or-->
|
||||
<!-- </p>-->
|
||||
<!-- <v-btn depressed @click="manualUploadClicked">-->
|
||||
<!-- select a file-->
|
||||
<!-- </v-btn>-->
|
||||
<!-- <input ref="uploadInput" type="file" hidden @change="(e) => handleFiles(e.target.files[0])">-->
|
||||
|
||||
<!-- <div v-if="allowUrl">-->
|
||||
<!-- <p class="or">-->
|
||||
<!-- or-->
|
||||
<!-- </p>-->
|
||||
<!-- <v-text-field-->
|
||||
<!-- ref="urlInput"-->
|
||||
<!-- v-model="url"-->
|
||||
<!-- append-icon="fa-arrow-right"-->
|
||||
<!-- dense-->
|
||||
<!-- label="Paste the file url"-->
|
||||
<!-- outlined-->
|
||||
<!-- :error-messages="urlErrors"-->
|
||||
<!-- @click:append="urlFilled(url)"-->
|
||||
<!-- @keypress.enter="urlFilled(url)"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!--</template>-->
|
||||
|
||||
<!--<script lang="ts">-->
|
||||
<!--import {Component, Prop, Vue} from 'nuxt-property-decorator'-->
|
||||
|
||||
<!--@Component-->
|
||||
<!--export default class FileUploader extends Vue {-->
|
||||
<!-- @Prop({default: true}) readonly allowUrl!: boolean-->
|
||||
<!-- dragging = false-->
|
||||
<!-- urlErrors: string|null = null-->
|
||||
<!-- dragEnterCounter = 0-->
|
||||
<!-- url = ''-->
|
||||
<!-- loading = false-->
|
||||
|
||||
<!-- imageDropped(e: DragEvent) {-->
|
||||
<!-- this.dragging = false-->
|
||||
<!-- if (e.dataTransfer.items.length > 0) {-->
|
||||
<!-- const item = e.dataTransfer.items[0]-->
|
||||
<!-- switch (item.kind) {-->
|
||||
<!-- case 'string':-->
|
||||
<!-- item.getAsString((url: string) => this.urlFilled(url))-->
|
||||
<!-- break-->
|
||||
<!-- case 'file':-->
|
||||
<!-- this.handleFiles(item.getAsFile())-->
|
||||
<!-- break-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- dragEnter() {-->
|
||||
<!-- this.dragEnterCounter++-->
|
||||
<!-- this.dragging = true-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- dragLeave() {-->
|
||||
<!-- if (--this.dragEnterCounter <= 0) {-->
|
||||
<!-- this.dragging = false-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- async urlFilled(url: string) {-->
|
||||
<!-- if (url && url.length > 0) {-->
|
||||
<!-- this.loading = true-->
|
||||
<!-- try {-->
|
||||
<!-- const {data, headers} = await this.$axios.$get(url)-->
|
||||
<!-- const name = url.split('/').pop()-->
|
||||
<!-- const file = new File([data], name, {type: headers['content-type']})-->
|
||||
<!-- this.handleFiles(file)-->
|
||||
<!-- } catch (ignored) {-->
|
||||
<!-- this.urlErrors = 'Incorrect url'-->
|
||||
<!-- }-->
|
||||
<!-- this.loading = false-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- dragEnd() {-->
|
||||
<!-- this.dragging = false-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- dragExit() {-->
|
||||
<!-- this.dragging = false-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- handleFiles(file: File) {-->
|
||||
<!-- if (!file) {-->
|
||||
<!-- return-->
|
||||
<!-- }-->
|
||||
<!-- this.$emit('input', file)-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- manualUploadClicked() {-->
|
||||
<!-- this.$refs.uploadInput.click()-->
|
||||
<!-- }-->
|
||||
<!--}-->
|
||||
|
||||
<!--</script>-->
|
||||
|
||||
<!--<style lang="less">-->
|
||||
<!--.drop-area {-->
|
||||
<!-- border: 2px dashed rgba(255, 255, 255, 0.3);-->
|
||||
<!-- border-radius: 10px;-->
|
||||
<!-- background-color: rgba(255, 255, 255, 0.03);-->
|
||||
|
||||
<!-- & > *, .v-btn {-->
|
||||
<!-- margin: 0 !important;-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- .or {-->
|
||||
<!-- opacity: 0.7;-->
|
||||
<!-- margin: 5px 0 !important;-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- &.drag-over {-->
|
||||
<!-- border-color: var(--v-primary-base);-->
|
||||
<!-- }-->
|
||||
|
||||
<!-- .v-input__icon {-->
|
||||
<!-- button {-->
|
||||
<!-- margin-top: 0 !important;-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!--}-->
|
||||
<!--</style>-->
|
||||
|
|
103
components/Memo.vue
Normal file
103
components/Memo.vue
Normal file
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<div class="memo">
|
||||
<ToolHeader :config="memoConfig" />
|
||||
|
||||
Warning: le style/aspect est toujours en wip, so focus on content <br><br>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6" lg="4" v-for="item in items" :key="item.section" class="memo-section">
|
||||
<v-card class="mb-5">
|
||||
<v-card-title>{{ item.section }}</v-card-title>
|
||||
<v-card-text>
|
||||
<div v-for="tip in item.items" :key="tip.text" class="memo-item">
|
||||
<div class="memo-item-title">
|
||||
{{ tip.text }}
|
||||
</div>
|
||||
<div v-if="tip.subtitle" class="memo-item-subtitle">
|
||||
{{ tip.subtitle }}
|
||||
</div>
|
||||
<div v-if="tip.code" class="memo-item-code" @click="copy(tip.code)">
|
||||
<pre>{{ tip.code }}</pre>
|
||||
<v-icon>mdi-content-copy</v-icon>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {Component} from 'nuxt-property-decorator'
|
||||
import Tool from './Tool.vue'
|
||||
import type {ToolConfig} from '~/types/ToolConfig'
|
||||
import {CopyableMixin} from '~/mixins/copyable.mixin'
|
||||
import ToolHeader from '~/components/ToolHeader.vue'
|
||||
|
||||
type MemoItems = { section: string, items: { text: string, code?: string, subtitle?: string }[] }[];
|
||||
|
||||
@Component({
|
||||
mixins: [CopyableMixin],
|
||||
components: {ToolHeader}
|
||||
})
|
||||
export default class Memo extends Tool {
|
||||
private memoConfig: ToolConfig = this.config();
|
||||
private items: MemoItems = this.$t('memo') as unknown as MemoItems
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.memo {
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
font-size: 50px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.memo-item {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.memo-item-title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
color: var(--v-primary-base);
|
||||
}
|
||||
|
||||
.memo-item-subtitle {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.memo-item-code {
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
padding: 8px 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
pre {
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.v-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.v-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,8 +1,7 @@
|
|||
<script lang="ts">
|
||||
import {Component, Vue} from 'nuxt-property-decorator'
|
||||
import ToolWrapper from '~/components/ToolWrapper.vue'
|
||||
import {ToolConfig} from '~/types/ToolConfig'
|
||||
|
||||
import type {ToolConfig} from '~/types/ToolConfig'
|
||||
@Component({components: {ToolWrapper}})
|
||||
export default class Tool extends Vue {
|
||||
config(): ToolConfig {
|
||||
|
|
44
components/ToolHeader.vue
Normal file
44
components/ToolHeader.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div class="tool-wrapper-info">
|
||||
<h1>{{ config.title }}</h1>
|
||||
<div class="spacer" />
|
||||
<div class="description">
|
||||
{{ config.description }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'nuxt-property-decorator'
|
||||
import type {ToolConfig} from '@/types/ToolConfig'
|
||||
|
||||
@Component
|
||||
export default class ToolWrapper extends Vue {
|
||||
@Prop() readonly config!: ToolConfig;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.tool-wrapper-info {
|
||||
padding: 50px 0 30px;
|
||||
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
font-size: 50px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 200px;
|
||||
height: 2px;
|
||||
background: var(--v-primary-base);
|
||||
background: linear-gradient(90deg, rgba(71, 177, 113, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(37, 99, 108, 1) 200%);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #829097;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,24 +2,18 @@
|
|||
<div class="tool-wrapper">
|
||||
<v-row no-gutters justify="center" align="center">
|
||||
<v-col cols="12" xl="6" lg="8" md="10">
|
||||
<div class="tool-wrapper-info">
|
||||
<h1>{{ config.title }}</h1>
|
||||
<div class="spacer"/>
|
||||
<div class="description">
|
||||
{{ config.description }}
|
||||
</div>
|
||||
</div>
|
||||
<ToolHeader :config="config" />
|
||||
|
||||
<template v-if="!noCard">
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-10">
|
||||
<slot/>
|
||||
<slot />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot/>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
@ -27,9 +21,9 @@
|
|||
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'nuxt-property-decorator'
|
||||
import {ToolConfig} from '~/types/ToolConfig'
|
||||
|
||||
@Component
|
||||
import ToolHeader from './ToolHeader.vue'
|
||||
import type {ToolConfig} from '~/types/ToolConfig'
|
||||
@Component({components: {ToolHeader}})
|
||||
export default class ToolWrapper extends Vue {
|
||||
@Prop() readonly config!: ToolConfig;
|
||||
@Prop({default: () => false}) readonly noCard!: boolean;
|
||||
|
@ -41,31 +35,8 @@ export default class ToolWrapper extends Vue {
|
|||
.tool-wrapper {
|
||||
height: 100%;
|
||||
|
||||
.tool-wrapper-info {
|
||||
padding: 50px 0 30px;
|
||||
}
|
||||
|
||||
.category {
|
||||
color: #546167;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
font-size: 50px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 200px;
|
||||
height: 2px;
|
||||
background: var(--v-primary-base);
|
||||
background: linear-gradient(90deg, rgba(71, 177, 113, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(37, 99, 108, 1) 200%);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #829097;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue