color picker
This commit is contained in:
parent
afb8510ea5
commit
cf1f23a19e
|
@ -6,13 +6,22 @@
|
|||
<i class="fa fa-code"></i>
|
||||
</a>
|
||||
<div class="bbcode-toolbar btn-toolbar" role="toolbar" :disabled="disabled" :style="showToolbar ? {display: 'flex'} : undefined" @mousedown.stop.prevent
|
||||
v-if="hasToolbar" style="flex:1 51%">
|
||||
<div class="btn-group" style="flex-wrap:wrap">
|
||||
v-if="hasToolbar" style="flex:1 51%; position: relative">
|
||||
|
||||
<div class="popover popover-top color-selector" v-show="colorPopupVisible" v-on-clickaway="dismissColorSelector">
|
||||
<div class="popover-body">
|
||||
<div class="btn-group" role="group" aria-label="Color">
|
||||
<button v-for="btnCol in buttonColors" type="button" class="btn text-color" :class="btnCol" :title="btnCol" @click.prevent.stop="colorApply(btnCol)"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group toolbar-buttons" style="flex-wrap:wrap">
|
||||
<div v-if="!!characterName" class="character-btn">
|
||||
<icon :character="characterName"></icon>
|
||||
</div>
|
||||
|
||||
<div class="btn btn-light btn-sm" v-for="button in buttons" :title="button.title" @click.prevent.stop="apply(button)">
|
||||
<div class="btn btn-light btn-sm" v-for="button in buttons" :class="button.outerClass" :title="button.title" @click.prevent.stop="apply(button)">
|
||||
<i :class="(button.class ? button.class : 'fa ') + button.icon"></i>
|
||||
</div>
|
||||
<div @click="previewBBCode" class="btn btn-light btn-sm" :class="preview ? 'active' : ''"
|
||||
|
@ -41,7 +50,9 @@
|
|||
|
||||
<script lang="ts">
|
||||
import {Component, Hook, Prop, Watch} from '@f-list/vue-ts';
|
||||
import _ from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { mixin as clickaway } from 'vue-clickaway';
|
||||
import {getKey} from '../chat/common';
|
||||
import {Keys} from '../keys';
|
||||
import {BBCodeElement, CoreBBCodeParser, urlRegex} from './core';
|
||||
|
@ -52,7 +63,8 @@
|
|||
@Component({
|
||||
components: {
|
||||
'icon': IconView
|
||||
}
|
||||
},
|
||||
mixins: [ clickaway ]
|
||||
})
|
||||
export default class Editor extends Vue {
|
||||
@Prop
|
||||
|
@ -82,6 +94,9 @@
|
|||
@Prop({default: null})
|
||||
readonly characterName: string | null = null;
|
||||
|
||||
buttonColors = ['red', 'orange', 'yellow', 'green', 'cyan', 'purple', 'blue', 'pink', 'black', 'brown', 'white', 'gray'];
|
||||
colorPopupVisible = false;
|
||||
|
||||
preview = false;
|
||||
previewWarnings: ReadonlyArray<string> = [];
|
||||
previewResult = '';
|
||||
|
@ -156,12 +171,27 @@
|
|||
|
||||
get buttons(): EditorButton[] {
|
||||
const buttons = this.defaultButtons.slice();
|
||||
|
||||
if(this.extras !== undefined)
|
||||
for(let i = 0, l = this.extras.length; i < l; i++)
|
||||
buttons.push(this.extras[i]);
|
||||
|
||||
const colorButtonIndex = _.findIndex(buttons, (b) => b.icon === 'fa-eye-dropper')!;
|
||||
|
||||
if (this.colorPopupVisible) {
|
||||
const colorButton = _.cloneDeep(buttons[colorButtonIndex]);
|
||||
colorButton.outerClass = 'toggled';
|
||||
|
||||
buttons[colorButtonIndex] = colorButton;
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
getColorButton(): EditorButton {
|
||||
return _.find(this.buttons, (b) => b.icon === 'fa-eye-dropper')!;
|
||||
}
|
||||
|
||||
@Watch('value')
|
||||
watchValue(newValue: string): void {
|
||||
this.$nextTick(() => this.resize());
|
||||
|
@ -213,7 +243,32 @@
|
|||
this.$emit('input', this.text);
|
||||
}
|
||||
|
||||
dismissColorSelector(): void {
|
||||
this.colorPopupVisible = false;
|
||||
}
|
||||
|
||||
colorApply(btnColor: string): void {
|
||||
const button = this.getColorButton();
|
||||
|
||||
this.applyButtonEffect(button, btnColor);
|
||||
|
||||
this.colorPopupVisible = false;
|
||||
}
|
||||
|
||||
apply(button: EditorButton): void {
|
||||
if (button.tag === 'color') {
|
||||
this.colorPopupVisible = !this.colorPopupVisible;
|
||||
return;
|
||||
} else if (button.tag === 'eicon') {
|
||||
|
||||
} else if (button.tag === 'emoji') {
|
||||
|
||||
}
|
||||
|
||||
this.applyButtonEffect(button);
|
||||
}
|
||||
|
||||
applyButtonEffect(button: EditorButton, withArgument?: string): void {
|
||||
// Allow emitted variations for custom buttons.
|
||||
this.$once('insert', (startText: string, endText: string) => this.applyText(startText, endText));
|
||||
// noinspection TypeScriptValidateTypes
|
||||
|
@ -221,8 +276,8 @@
|
|||
// tslint:ignore-next-line:no-any
|
||||
return button.handler.call(this as any, this);
|
||||
}
|
||||
if(button.startText === undefined)
|
||||
button.startText = `[${button.tag}]`;
|
||||
if(button.startText === undefined || withArgument)
|
||||
button.startText = `[${button.tag}${withArgument ? '=' + withArgument : ''}]`;
|
||||
if(button.endText === undefined)
|
||||
button.endText = `[/${button.tag}]`;
|
||||
|
||||
|
@ -266,7 +321,7 @@
|
|||
if(button.key === key) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.apply(button);
|
||||
this.applyButtonEffect(button);
|
||||
break;
|
||||
}
|
||||
} else if(e.shiftKey) this.isShiftPressed = true;
|
||||
|
@ -339,4 +394,94 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bbcode-toolbar {
|
||||
.toolbar-buttons {
|
||||
.btn.toggled {
|
||||
background-color: var(--secondary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.color-selector {
|
||||
max-width: 145px;
|
||||
top: -57px;
|
||||
left: 94px;
|
||||
line-height: 1;
|
||||
z-index: 1000;
|
||||
background-color: var(--input-bg);
|
||||
|
||||
.btn-group {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.btn {
|
||||
&.text-color {
|
||||
border-radius: 0 !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin-right: -1px !important;
|
||||
margin-bottom: -1px !important;
|
||||
border: 1px solid var(--secondary);
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
|
||||
&::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--gray-dark) !important;
|
||||
}
|
||||
|
||||
&.red {
|
||||
background-color: var(--textRedColor);
|
||||
}
|
||||
|
||||
&.orange {
|
||||
background-color: var(--textOrangeColor);
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
background-color: var(--textYellowColor);
|
||||
}
|
||||
|
||||
&.green {
|
||||
background-color: var(--textGreenColor);
|
||||
}
|
||||
|
||||
&.cyan {
|
||||
background-color: var(--textCyanColor);
|
||||
}
|
||||
|
||||
&.purple {
|
||||
background-color: var(--textPurpleColor);
|
||||
}
|
||||
|
||||
&.blue {
|
||||
background-color: var(--textBlueColor);
|
||||
}
|
||||
|
||||
&.pink {
|
||||
background-color: var(--textPinkColor);
|
||||
}
|
||||
|
||||
&.black {
|
||||
background-color: var(--textBlackColor);
|
||||
}
|
||||
|
||||
&.brown {
|
||||
background-color: var(--textBrownColor);
|
||||
}
|
||||
|
||||
&.white {
|
||||
background-color: var(--textWhiteColor);
|
||||
}
|
||||
|
||||
&.gray {
|
||||
background-color: var(--textGrayColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,6 +7,7 @@ export interface EditorButton {
|
|||
icon: string;
|
||||
key?: Keys;
|
||||
class?: string;
|
||||
outerClass?: string;
|
||||
startText?: string;
|
||||
endText?: string;
|
||||
handler?(vm: Vue): void;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<modal :action="l('chat.setStatus')" @submit="setStatus" @close="reset" dialogClass="w-100 modal-lg">
|
||||
<modal :action="l('chat.setStatus')" @submit="setStatus" @close="reset" dialogClass="w-100 modal-lg statusEditor">
|
||||
<div class="form-group" id="statusSelector">
|
||||
<label class="control-label">{{l('chat.setStatus.status')}}</label>
|
||||
<dropdown linkClass="custom-select">
|
||||
|
@ -105,3 +105,9 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.statusEditor .bbcode-toolbar .color-selector {
|
||||
left: 58px !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -91,6 +91,10 @@
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.ad-list .bbcode-toolbar .color-selector {
|
||||
left: 58px !important;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
min-width: 70%;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
import * as electron from 'electron';
|
||||
const app = electron.app;
|
||||
import * as remote from '@electron/remote';
|
||||
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { EIconRecord, EIconUpdater } from './updater';
|
||||
|
||||
export class EIconStore {
|
||||
protected records: EIconRecord[] = [];
|
||||
|
||||
protected lookup: Record<string, EIconRecord> = {};
|
||||
|
||||
protected asOfTimestamp = 0;
|
||||
|
||||
protected updater = new EIconUpdater();
|
||||
|
||||
async save(): Promise<void> {
|
||||
const fn = this.getStoreFilename();
|
||||
log.info('eicons.save', { records: this.records.length, asOfTimestamp: this.asOfTimestamp, fn });
|
||||
|
||||
fs.writeFileSync(fn, JSON.stringify({
|
||||
asOfTimestamp: this.asOfTimestamp,
|
||||
records: this.records
|
||||
}));
|
||||
|
||||
remote.ipcMain.emit('eicons.reload', { asOfTimestamp: this.asOfTimestamp });
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
const fn = this.getStoreFilename();
|
||||
log.info('eicons.load', { fn });
|
||||
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(fn, 'utf-8'));
|
||||
|
||||
this.records = data?.records || [];
|
||||
this.asOfTimestamp = data?.asOfTimestamp || 0;
|
||||
this.lookup = _.fromPairs(_.map(this.records, (r) => [r.eicon, r]));
|
||||
|
||||
this.resortList();
|
||||
|
||||
log.info('eicons.loaded', { records: this.records.length, asOfTimestamp: this.asOfTimestamp });
|
||||
} catch (err) {
|
||||
try {
|
||||
await this.downloadAll();
|
||||
} catch (err2) {
|
||||
log.error('eicons.load.failure', { err: err2 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected getStoreFilename(): string {
|
||||
const baseDir = app.getPath('userData');
|
||||
const settingsDir = path.join(baseDir, 'data');
|
||||
|
||||
return path.join(settingsDir, 'eicons.json');
|
||||
}
|
||||
|
||||
async downloadAll(): Promise<void> {
|
||||
log.info('eicons.downloadAll');
|
||||
|
||||
const eicons = await this.updater.fetchAll();
|
||||
|
||||
this.records = eicons.records;
|
||||
this.lookup = _.fromPairs(_.map(this.records, (r) => [r.eicon, r]));
|
||||
|
||||
_.each(eicons.records, (changeRecord) => this.addIcon(changeRecord));
|
||||
|
||||
this.resortList();
|
||||
|
||||
this.asOfTimestamp = eicons.asOfTimestamp;
|
||||
|
||||
await this.save();
|
||||
}
|
||||
|
||||
async update(): Promise<void> {
|
||||
log.info('eicons.update');
|
||||
|
||||
const changes = await this.updater.fetchUpdates(this.asOfTimestamp);
|
||||
|
||||
const removals = _.filter(changes.recordUpdates, (changeRecord) => changeRecord.action === '-');
|
||||
const additions = _.filter(changes.recordUpdates, (changeRecord) => changeRecord.action === '+');
|
||||
|
||||
_.each(removals, (changeRecord) => this.removeIcon(changeRecord));
|
||||
_.each(additions, (changeRecord) => this.addIcon(changeRecord));
|
||||
|
||||
this.resortList();
|
||||
|
||||
this.asOfTimestamp = changes.asOfTimestamp;
|
||||
|
||||
if (changes.recordUpdates.length > 0) {
|
||||
await this.save();
|
||||
}
|
||||
}
|
||||
|
||||
protected resortList(): void {
|
||||
_.sortBy(this.records, 'eicon');
|
||||
}
|
||||
|
||||
protected addIcon(record: EIconRecord): void {
|
||||
if (record.eicon in this.lookup) {
|
||||
this.lookup[record.eicon].timestamp = record.timestamp;
|
||||
return;
|
||||
}
|
||||
|
||||
const r = {
|
||||
eicon: record.eicon,
|
||||
timestamp: record.timestamp
|
||||
};
|
||||
|
||||
this.records.push(r);
|
||||
this.lookup[record.eicon] = r;
|
||||
}
|
||||
|
||||
protected removeIcon(record: EIconRecord): void {
|
||||
if (!(record.eicon in this.lookup)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete this.lookup[record.eicon];
|
||||
|
||||
const idx = this.records.findIndex((r) => (r.eicon === record.eicon));
|
||||
|
||||
if (idx >= 0) {
|
||||
this.records.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
search(searchString: string): EIconRecord[] {
|
||||
const lcSearch = searchString.toLowerCase();
|
||||
const found = _.filter(this.records, (r) => r.eicon.indexOf(lcSearch) >= 0);
|
||||
|
||||
return found.sort((a, b) => {
|
||||
if ((a.eicon.substr(0, lcSearch.length) === lcSearch) && (b.eicon.substr(0, lcSearch.length) !== lcSearch)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((b.eicon.substr(0, lcSearch.length) === lcSearch) && (a.eicon.substr(0, lcSearch.length) !== lcSearch)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return a.eicon.localeCompare(b.eicon);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import Axios from 'axios';
|
||||
import _ from 'lodash';
|
||||
|
||||
export interface EIconRecord {
|
||||
eicon: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export interface EIconRecordUpdate extends EIconRecord {
|
||||
action: '+' | '-';
|
||||
}
|
||||
|
||||
export class EIconUpdater {
|
||||
static readonly FULL_DATA_URL = 'https://xariah.net/eicons/Home/EiconsDataBase/base.doc';
|
||||
|
||||
static readonly DATA_UPDATE_URL = 'https://xariah.net/eicons/Home/EiconsDataDeltaSince';
|
||||
|
||||
async fetchAll(): Promise<{ records: EIconRecord[], asOfTimestamp: number }> {
|
||||
const result = await Axios.get(EIconUpdater.FULL_DATA_URL);
|
||||
const lines = _.split(result.data, '\n');
|
||||
|
||||
const records = _.map(_.filter(lines, (line) => (line.trim().substr(0, 1) !== '#')), (line) => {
|
||||
const [eicon, timestamp] = _.split(line, '\t', 2);
|
||||
return { eicon: eicon.toLowerCase(), timestamp: parseInt(timestamp, 10) };
|
||||
});
|
||||
|
||||
const asOfLine = _.first(_.filter(lines, (line: string) => line.substring(0, 9) === '# As Of: '));
|
||||
const asOfTimestamp = asOfLine ? parseInt(asOfLine.substring(9), 10) : 0;
|
||||
|
||||
return { records, asOfTimestamp };
|
||||
}
|
||||
|
||||
async fetchUpdates(fromTimestampInSecs: number): Promise<{ recordUpdates: EIconRecordUpdate[], asOfTimestamp: number }> {
|
||||
const result = await Axios.get(`${EIconUpdater.DATA_UPDATE_URL}/${fromTimestampInSecs}`);
|
||||
const lines = _.split(result.data, '\n');
|
||||
|
||||
const recordUpdates = _.map(_.filter(lines, (line) => (line.trim().substr(0, 1) !== '#')), (line) => {
|
||||
const [action, eicon, timestamp] = _.split(line, '\t', 3);
|
||||
return { action: action as '+' | '-', eicon: eicon.toLowerCase(), timestamp: parseInt(timestamp, 10) };
|
||||
});
|
||||
|
||||
const asOfLine = _.first(_.filter(lines, (line: string) => line.substring(0, 9) === '# As Of: '));
|
||||
const asOfTimestamp = asOfLine ? parseInt(asOfLine.substring(9), 10) : 0;
|
||||
|
||||
return { recordUpdates, asOfTimestamp };
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
"@types/lodash": "4.14.162",
|
||||
"@types/node": "16.18.32",
|
||||
"@types/node-fetch": "2.6.4",
|
||||
"@types/vue-clickaway": "2.2.0",
|
||||
"@types/qs": "^6.9.5",
|
||||
"@types/request-promise": "^4.1.46",
|
||||
"@types/sortablejs": "^1.10.6",
|
||||
|
@ -48,6 +49,7 @@
|
|||
"tslint": "^6.1.3",
|
||||
"typescript": "^3.9.7",
|
||||
"vue": "2.6.12",
|
||||
"vue-clickaway": "2.2.2",
|
||||
"vue-input-tag": "^2.0.7",
|
||||
"vue-loader": "15.9.8",
|
||||
"vue-template-compiler": "2.6.12",
|
||||
|
|
|
@ -60,4 +60,17 @@ $risingVariables: (
|
|||
|
||||
input-bg: #{$input-bg},
|
||||
input-color: #{$input-color},
|
||||
|
||||
textRedColor: #{$red-color},
|
||||
textBlueColor: #{$blue-color},
|
||||
textYellowColor: #{$yellow-color},
|
||||
textGreenColor: #{$green-color},
|
||||
textCyanColor: #{$cyan-color},
|
||||
textPurpleColor: #{$purple-color},
|
||||
textWhiteColor: #{$white-color},
|
||||
textBlackColor: #{$black-color},
|
||||
textBrownColor: #{$brown-color},
|
||||
textPinkColor: #{$pink-color},
|
||||
textGrayColor: #{$gray-color},
|
||||
textOrangeColor: #{$orange-color},
|
||||
);
|
||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -518,6 +518,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
|
||||
integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
|
||||
|
||||
"@types/vue-clickaway@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/vue-clickaway/-/vue-clickaway-2.2.0.tgz#a560c5280dac0dd0906b5b78249be0c859acffc6"
|
||||
integrity sha512-bPRisDZhtscJ+2PmGIAyEBB7+ep6j/FFrPGr5kpWzAFylRxCcZrw70tlXUwslgZtSYZEU1e6QJkSOPHFw21XXQ==
|
||||
dependencies:
|
||||
vue "^2.0.0"
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599"
|
||||
|
@ -4698,7 +4705,7 @@ log-symbols@^4.1.0:
|
|||
chalk "^4.1.0"
|
||||
is-unicode-supported "^0.1.0"
|
||||
|
||||
loose-envify@^1.0.0:
|
||||
loose-envify@^1.0.0, loose-envify@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
|
@ -7680,6 +7687,13 @@ vue-class-component@6.3.2:
|
|||
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.3.2.tgz#e6037e84d1df2af3bde4f455e50ca1b9eec02be6"
|
||||
integrity sha512-cH208IoM+jgZyEf/g7mnFyofwPDJTM/QvBNhYMjqGB8fCsRyTf68rH2ISw/G20tJv+5mIThQ3upKwoL4jLTr1A==
|
||||
|
||||
vue-clickaway@2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-clickaway/-/vue-clickaway-2.2.2.tgz#cecf6839575e8b2afc5d3edb3efb616d293dbb44"
|
||||
integrity sha512-25SpjXKetL06GLYoLoC8pqAV6Cur9cQ//2g35GRFBV4FgoljbZZjTINR8g2NuVXXDMLSUXaKx5dutgO4PaDE7A==
|
||||
dependencies:
|
||||
loose-envify "^1.2.0"
|
||||
|
||||
vue-hot-reload-api@^2.3.0:
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
|
||||
|
@ -7724,7 +7738,7 @@ vue-template-es2015-compiler@^1.9.0:
|
|||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||
|
||||
vue@2.6.12, vue@^2.5.17, vue@^2.5.20:
|
||||
vue@2.6.12, vue@^2.0.0, vue@^2.5.17, vue@^2.5.20:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
|
||||
integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
|
||||
|
|
Loading…
Reference in New Issue