タグ入力欄だけVue化
This commit is contained in:
parent
7a95e0979e
commit
0670cb8736
@ -32,7 +32,9 @@
|
|||||||
"ts-loader": "^6.0.1",
|
"ts-loader": "^6.0.1",
|
||||||
"typescript": "^3.4.5",
|
"typescript": "^3.4.5",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-template-compiler": "^2.6.6"
|
"vue-class-component": "^7.1.0",
|
||||||
|
"vue-property-decorator": "^8.1.1",
|
||||||
|
"vue-template-compiler": "^2.6.10"
|
||||||
},
|
},
|
||||||
"stylelint": {
|
"stylelint": {
|
||||||
"extends": "stylelint-config-recess-order"
|
"extends": "stylelint-config-recess-order"
|
||||||
|
@ -1,59 +1,9 @@
|
|||||||
function updateTags() {
|
import Vue from 'vue';
|
||||||
$('input[name=tags]').val(
|
import TagInput from "./components/TagInput.vue";
|
||||||
$('#tags')
|
|
||||||
.find('li')
|
|
||||||
.map(function () {
|
|
||||||
return $(this).data('value');
|
|
||||||
})
|
|
||||||
.get()
|
|
||||||
.join(' ')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertTag(value: string) {
|
new Vue({
|
||||||
$('<li class="list-inline-item badge badge-primary" style="cursor: pointer;"><span class="oi oi-tag"></span> <span></span> | x</li>')
|
el: '#app',
|
||||||
.data('value', value)
|
components: {
|
||||||
.children(':last-child')
|
TagInput
|
||||||
.text(value)
|
|
||||||
.end()
|
|
||||||
.appendTo('#tags');
|
|
||||||
}
|
|
||||||
|
|
||||||
const initTags = $('input[name=tags]').val() as string;
|
|
||||||
if (initTags.trim() !== '') {
|
|
||||||
initTags.split(' ').forEach(function (value) {
|
|
||||||
insertTag(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#tagInput').on('keydown', function (ev: JQuery.KeyDownEvent) {
|
|
||||||
const $this = $(this);
|
|
||||||
let value = $this.val() as string;
|
|
||||||
if (value.trim() !== '') {
|
|
||||||
switch (ev.key) {
|
|
||||||
case 'Tab':
|
|
||||||
case 'Enter':
|
|
||||||
case ' ':
|
|
||||||
if ((ev.originalEvent as any).isComposing !== true) {
|
|
||||||
insertTag(value.trim());
|
|
||||||
$this.val('');
|
|
||||||
updateTags();
|
|
||||||
}
|
|
||||||
ev.preventDefault();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ev.key === 'Enter') {
|
|
||||||
// 誤爆防止
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#tags')
|
|
||||||
.on('click', 'li', function (ev) {
|
|
||||||
$(this).remove();
|
|
||||||
updateTags();
|
|
||||||
})
|
|
||||||
.parent()
|
|
||||||
.on('click', function (ev) {
|
|
||||||
$('#tagInput').focus();
|
|
||||||
});
|
|
77
resources/assets/js/components/TagInput.vue
Normal file
77
resources/assets/js/components/TagInput.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<div class="form-control h-auto" @click="$refs.input.focus()">
|
||||||
|
<ul class="list-inline d-inline">
|
||||||
|
<li v-for="(tag, i) in tags"
|
||||||
|
class="list-inline-item badge badge-primary"
|
||||||
|
style="cursor: pointer;"
|
||||||
|
@click="removeTag(i)"><span class="oi oi-tag"></span> {{ tag }} | x</li>
|
||||||
|
</ul>
|
||||||
|
<input :id="id"
|
||||||
|
ref="input"
|
||||||
|
type="text"
|
||||||
|
style="border: 0; outline: 0;"
|
||||||
|
v-model="buffer"
|
||||||
|
@keydown="onKeyDown">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {Vue, Component, Prop} from "vue-property-decorator";
|
||||||
|
|
||||||
|
function getElementByName(elementName: string): HTMLElement | null {
|
||||||
|
const elements = document.getElementsByName(elementName);
|
||||||
|
if (elements.length) {
|
||||||
|
return elements[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class TagInput extends Vue {
|
||||||
|
@Prop(String) readonly id!: string;
|
||||||
|
@Prop(String) readonly input!: string;
|
||||||
|
@Prop(Boolean) readonly isInvalid!: boolean;
|
||||||
|
|
||||||
|
tags: string[] = [];
|
||||||
|
buffer: string = "";
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
const tags = getElementByName(this.input);
|
||||||
|
if (tags instanceof HTMLInputElement && tags.value.trim() !== "") {
|
||||||
|
this.tags = tags.value.split(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(event: KeyboardEvent) {
|
||||||
|
if (this.buffer.trim() !== "") {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'Tab':
|
||||||
|
case 'Enter':
|
||||||
|
case ' ':
|
||||||
|
if ((event as any).isComposing !== true) {
|
||||||
|
this.tags.push(this.buffer);
|
||||||
|
this.buffer = "";
|
||||||
|
this.sync();
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (event.key === "Enter") {
|
||||||
|
// 誤爆防止
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTag(index: number) {
|
||||||
|
this.tags.splice(index, 1);
|
||||||
|
this.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
sync() {
|
||||||
|
const tags = getElementByName(this.input);
|
||||||
|
if (tags instanceof HTMLInputElement) {
|
||||||
|
tags.value = this.tags.join(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
4
resources/assets/js/vue-shims.d.ts
vendored
Normal file
4
resources/assets/js/vue-shims.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
declare module "*.vue" {
|
||||||
|
import Vue from "vue";
|
||||||
|
export default Vue;
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
@section('title', 'チェックイン')
|
@section('title', 'チェックイン')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container">
|
<div id="app" class="container">
|
||||||
<h2>今致してる?</h2>
|
<h2>今致してる?</h2>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row justify-content-center mt-5">
|
<div class="row justify-content-center mt-5">
|
||||||
@ -40,10 +40,7 @@
|
|||||||
<div class="form-group col-sm-12">
|
<div class="form-group col-sm-12">
|
||||||
<input name="tags" type="hidden" value="{{ old('tags') ?? $defaults['tags'] }}">
|
<input name="tags" type="hidden" value="{{ old('tags') ?? $defaults['tags'] }}">
|
||||||
<label for="tagInput"><span class="oi oi-tags"></span> タグ</label>
|
<label for="tagInput"><span class="oi oi-tags"></span> タグ</label>
|
||||||
<div class="form-control h-auto {{ $errors->has('tags') ? ' is-invalid' : '' }}">
|
<tag-input id="tagInput" input="tags" :is-invalid="{{ $errors->has('tags') ? 'true' : 'false' }}"></tag-input>
|
||||||
<ul id="tags" class="list-inline d-inline"></ul>
|
|
||||||
<input id="tagInput" type="text" style="outline: 0; border: 0;">
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">
|
<small class="form-text text-muted">
|
||||||
Tab, Enter, 半角スペースのいずれかで入力確定します。
|
Tab, Enter, 半角スペースのいずれかで入力確定します。
|
||||||
</small>
|
</small>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
@section('title', 'チェックインの修正')
|
@section('title', 'チェックインの修正')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container">
|
<div id="app" class="container">
|
||||||
<h2>チェックインの修正</h2>
|
<h2>チェックインの修正</h2>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row justify-content-center mt-5">
|
<div class="row justify-content-center mt-5">
|
||||||
@ -41,10 +41,7 @@
|
|||||||
<div class="form-group col-sm-12">
|
<div class="form-group col-sm-12">
|
||||||
<input name="tags" type="hidden" value="{{ old('tags') ?? $ejaculation->textTags() }}">
|
<input name="tags" type="hidden" value="{{ old('tags') ?? $ejaculation->textTags() }}">
|
||||||
<label for="tagInput"><span class="oi oi-tags"></span> タグ</label>
|
<label for="tagInput"><span class="oi oi-tags"></span> タグ</label>
|
||||||
<div class="form-control h-auto {{ $errors->has('tags') ? ' is-invalid' : '' }}">
|
<tag-input id="tagInput" input="tags" :is-invalid="{{ $errors->has('tags') ? 'true' : 'false' }}"></tag-input>
|
||||||
<ul id="tags" class="list-inline d-inline"></ul>
|
|
||||||
<input id="tagInput" type="text" style="outline: 0; border: 0;">
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">
|
<small class="form-text text-muted">
|
||||||
Tab, Enter, 半角スペースのいずれかで入力確定します。
|
Tab, Enter, 半角スペースのいずれかで入力確定します。
|
||||||
</small>
|
</small>
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"module": "es2015",
|
"module": "es2015",
|
||||||
"strict": true
|
"moduleResolution": "node",
|
||||||
|
"strict": true,
|
||||||
|
"experimentalDecorators": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"resources/assets/js/**/*"
|
"resources/assets/js/**/*"
|
||||||
|
20
yarn.lock
20
yarn.lock
@ -7830,6 +7830,11 @@ vm-browserify@0.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
indexof "0.0.1"
|
indexof "0.0.1"
|
||||||
|
|
||||||
|
vue-class-component@^7.0.1, vue-class-component@^7.1.0:
|
||||||
|
version "7.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.1.0.tgz#b33efcb10e17236d684f70b1e96f1946ec793e87"
|
||||||
|
integrity sha512-G9152NzUkz0i0xTfhk0Afc8vzdXxDR1pfN4dTwE72cskkgJtdXfrKBkMfGvDuxUh35U500g5Ve4xL8PEGdWeHg==
|
||||||
|
|
||||||
vue-hot-reload-api@^2.3.0:
|
vue-hot-reload-api@^2.3.0:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.2.tgz#1fcc1495effe08a790909b46bf7b5c4cfeb6f21b"
|
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.2.tgz#1fcc1495effe08a790909b46bf7b5c4cfeb6f21b"
|
||||||
@ -7846,6 +7851,13 @@ vue-loader@^15.4.2:
|
|||||||
vue-hot-reload-api "^2.3.0"
|
vue-hot-reload-api "^2.3.0"
|
||||||
vue-style-loader "^4.1.0"
|
vue-style-loader "^4.1.0"
|
||||||
|
|
||||||
|
vue-property-decorator@^8.1.1:
|
||||||
|
version "8.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-8.1.1.tgz#80dadbe5ffa0e7eb6a0ba0a07036365471a7d5ee"
|
||||||
|
integrity sha512-K+PUT17ZEMWyhrKZnl4Fc9qMyFpMcjVbZJBwx4BpA8BXfaspaTeFdoHuk1aywC/+4G86sxIr/5n4IQUQLecSWw==
|
||||||
|
dependencies:
|
||||||
|
vue-class-component "^7.0.1"
|
||||||
|
|
||||||
vue-style-loader@^4.1.0:
|
vue-style-loader@^4.1.0:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8"
|
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8"
|
||||||
@ -7854,10 +7866,10 @@ vue-style-loader@^4.1.0:
|
|||||||
hash-sum "^1.0.2"
|
hash-sum "^1.0.2"
|
||||||
loader-utils "^1.0.2"
|
loader-utils "^1.0.2"
|
||||||
|
|
||||||
vue-template-compiler@^2.6.6:
|
vue-template-compiler@^2.6.10:
|
||||||
version "2.6.6"
|
version "2.6.10"
|
||||||
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.6.tgz#a807acbf3d51971d3721d75ecb1b927b517c1a02"
|
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz#323b4f3495f04faa3503337a82f5d6507799c9cc"
|
||||||
integrity sha512-OakxDGyrmMQViCjkakQFbDZlG0NibiOzpLauOfyCUVRQc9yPmTqpiz9nF0VeA+dFkXegetw0E5x65BFhhLXO0A==
|
integrity sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==
|
||||||
dependencies:
|
dependencies:
|
||||||
de-indent "^1.0.2"
|
de-indent "^1.0.2"
|
||||||
he "^1.1.0"
|
he "^1.1.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user