forked from devsam/core
feat: 턴 선택기에서 '드래그' 선택 지원
This commit is contained in:
@@ -50,17 +50,29 @@
|
||||
</div>
|
||||
|
||||
<div class="commandTable">
|
||||
<template
|
||||
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
|
||||
0,
|
||||
Math.min(maxTurn, viewMaxTurn)
|
||||
)"
|
||||
:key="turnIdx"
|
||||
<DragSelect
|
||||
:style="rowGridStyle"
|
||||
attribute="turnIdx"
|
||||
@dragStart="isDragToggle = true"
|
||||
@dragDone="
|
||||
isDragToggle = false;
|
||||
toggleTurn(...$event);
|
||||
"
|
||||
v-slot="{ selected }"
|
||||
>
|
||||
<div class="idx_pad center d-grid" @click="toggleTurn(turnIdx)">
|
||||
<div
|
||||
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
|
||||
0,
|
||||
viewMaxTurn
|
||||
)"
|
||||
:turnIdx="turnIdx"
|
||||
:key="turnIdx"
|
||||
class="idx_pad center d-grid"
|
||||
>
|
||||
<b-button
|
||||
size="sm"
|
||||
:variant="
|
||||
(isDragToggle && selected.has(`${turnIdx}`))?'light':
|
||||
turnList.has(turnIdx)
|
||||
? 'info'
|
||||
: turnList.size == 0 && prevTurnList.has(turnIdx)
|
||||
@@ -70,10 +82,26 @@
|
||||
>{{ turnIdx + 1 }}</b-button
|
||||
>
|
||||
</div>
|
||||
</DragSelect>
|
||||
<DragSelect
|
||||
:style="rowGridStyle"
|
||||
attribute="turnIdx"
|
||||
@dragStart="isDragSingle = true"
|
||||
@dragDone="
|
||||
isDragSingle = false;
|
||||
selectTurn(...$event);
|
||||
"
|
||||
v-slot="{ selected }"
|
||||
>
|
||||
<div
|
||||
@click="selectTurn(turnIdx)"
|
||||
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
|
||||
0,
|
||||
viewMaxTurn
|
||||
)"
|
||||
:key="turnIdx"
|
||||
height="24"
|
||||
class="month_pad center"
|
||||
:turnIdx="turnIdx"
|
||||
:style="{
|
||||
'white-space': 'nowrap',
|
||||
'font-size': `${Math.min(
|
||||
@@ -81,18 +109,40 @@
|
||||
(75 / (`${turnObj.year ?? 1}`.length + 8)) * 1.8
|
||||
)}px`,
|
||||
overflow: 'hidden',
|
||||
color:
|
||||
isDragSingle && selected.has(`${turnIdx}`) ? 'cyan' : undefined,
|
||||
}"
|
||||
>
|
||||
{{ turnObj.year ? `${turnObj.year}年` : "" }}
|
||||
{{ turnObj.month ? `${turnObj.month}月` : "" }}
|
||||
</div>
|
||||
</DragSelect>
|
||||
<div :style="rowGridStyle">
|
||||
<div
|
||||
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
|
||||
0,
|
||||
viewMaxTurn
|
||||
)"
|
||||
:key="turnIdx"
|
||||
class="time_pad center"
|
||||
style="background-color: black; white-space: nowrap; overflow: hidden"
|
||||
:style="{
|
||||
backgroundColor: 'black',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
}"
|
||||
>
|
||||
{{ turnObj.time }}
|
||||
</div>
|
||||
<div class="turn_pad center">
|
||||
</div>
|
||||
<div :style="rowGridStyle">
|
||||
<div
|
||||
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
|
||||
0,
|
||||
viewMaxTurn
|
||||
)"
|
||||
:key="turnIdx"
|
||||
class="turn_pad center"
|
||||
>
|
||||
<span
|
||||
class="turn_text"
|
||||
:style="turnObj.style"
|
||||
@@ -101,7 +151,7 @@
|
||||
v-html="turnObj.brief"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row gx-1">
|
||||
<div class="col d-grid">
|
||||
@@ -200,9 +250,9 @@
|
||||
<script lang="ts">
|
||||
import addMilliseconds from "date-fns/esm/addMilliseconds";
|
||||
import addMinutes from "date-fns/esm/addMinutes";
|
||||
import { range } from "lodash";
|
||||
import { isString, range } from "lodash";
|
||||
import { stringifyUrl } from "query-string";
|
||||
import { defineComponent } from "vue";
|
||||
import { defineComponent, ref, watch } from "vue";
|
||||
import { formatTime } from "@util/formatTime";
|
||||
import { joinYearMonth } from "@util/joinYearMonth";
|
||||
import { mb_strwidth } from "@util/mb_strwidth";
|
||||
@@ -210,6 +260,8 @@ import { parseTime } from "@util/parseTime";
|
||||
import { parseYearMonth } from "@util/parseYearMonth";
|
||||
import { sammoAPI } from "@util/sammoAPI";
|
||||
import { convertSearch초성 } from "./util/convertSearch초성";
|
||||
import DragSelect from "@/components/DragSelect.vue";
|
||||
|
||||
type commandItem = {
|
||||
value: string;
|
||||
title: string;
|
||||
@@ -286,7 +338,9 @@ const searchModeKey = `sammo_searchModeOn`;
|
||||
|
||||
export default defineComponent({
|
||||
name: "PartialReservedCommand",
|
||||
|
||||
components: {
|
||||
DragSelect,
|
||||
},
|
||||
methods: {
|
||||
updateNow() {
|
||||
const serverNow = addMilliseconds(new Date(), this.timeDiff);
|
||||
@@ -295,16 +349,27 @@ export default defineComponent({
|
||||
this.updateNow();
|
||||
}, 1000 - serverNow.getMilliseconds());
|
||||
},
|
||||
toggleTurn(turnIdx: number) {
|
||||
if (this.turnList.has(turnIdx)) {
|
||||
this.turnList.delete(turnIdx);
|
||||
} else {
|
||||
this.turnList.add(turnIdx);
|
||||
toggleTurn(...turnList: number[] | string[]) {
|
||||
for (let turnIdx of turnList) {
|
||||
if (isString(turnIdx)) {
|
||||
turnIdx = parseInt(turnIdx);
|
||||
}
|
||||
if (this.turnList.has(turnIdx)) {
|
||||
this.turnList.delete(turnIdx);
|
||||
} else {
|
||||
this.turnList.add(turnIdx);
|
||||
}
|
||||
}
|
||||
},
|
||||
selectTurn(turnIdx: number) {
|
||||
selectTurn(...turnList: number[] | string[]) {
|
||||
this.turnList.clear();
|
||||
this.turnList.add(turnIdx);
|
||||
for (const turnIdx of turnList) {
|
||||
if (isString(turnIdx)) {
|
||||
this.turnList.add(parseInt(turnIdx));
|
||||
} else {
|
||||
this.turnList.add(turnIdx);
|
||||
}
|
||||
}
|
||||
},
|
||||
selectAll(e: Event | true) {
|
||||
//NOTE: split 구현에 버그가 있어서, 수동으로 구분해야함
|
||||
@@ -519,8 +584,7 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
maxTurn,
|
||||
flippedMaxTurn: 15,
|
||||
viewMaxTurn: 15,
|
||||
|
||||
maxPushTurn,
|
||||
commandList,
|
||||
serverNow: formatTime(serverNowObj, "HH:mm:ss"),
|
||||
@@ -533,6 +597,34 @@ export default defineComponent({
|
||||
searchModeOn,
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
const flippedMaxTurn = 15;
|
||||
const viewMaxTurn = ref(flippedMaxTurn);
|
||||
const rowGridStyle = ref({
|
||||
display: "grid",
|
||||
gridTemplateRows: `repeat(${viewMaxTurn.value}, 30px)`,
|
||||
});
|
||||
|
||||
watch(viewMaxTurn, (val) => {
|
||||
rowGridStyle.value.gridTemplateRows = `repeat(${val}, 29.4px)`;
|
||||
});
|
||||
|
||||
function debugSel(val: unknown) {
|
||||
console.log(val);
|
||||
}
|
||||
|
||||
const isDragSingle = ref(false);
|
||||
const isDragToggle = ref(false);
|
||||
|
||||
return {
|
||||
isDragSingle,
|
||||
isDragToggle,
|
||||
flippedMaxTurn,
|
||||
viewMaxTurn,
|
||||
rowGridStyle,
|
||||
debugSel,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
void this.reloadCommandList();
|
||||
},
|
||||
|
||||
@@ -14,7 +14,14 @@
|
||||
|
||||
<script lang="ts">
|
||||
/// https://github.com/andi23rosca/drag-select-vue/blob/master/src/DragSelect.vue
|
||||
import { defineComponent, ref, watch, onMounted, onBeforeUnmount, PropType } from "vue";
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
watch,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
PropType,
|
||||
} from "vue";
|
||||
import VueTypes from "vue-types";
|
||||
|
||||
function getDimensions(p1: coord, p2: coord): rect {
|
||||
@@ -47,12 +54,12 @@ export default defineComponent({
|
||||
required: false,
|
||||
},
|
||||
modelValue: {
|
||||
type: Object as PropType<Set<string>>,
|
||||
required: false,
|
||||
default: ()=>new Set(),
|
||||
}
|
||||
type: Object as PropType<Set<string>>,
|
||||
required: false,
|
||||
default: () => ref(new Set()),
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue", "dragDone"],
|
||||
emits: ["update:modelValue", "dragDone", "dragStart"],
|
||||
setup(props, { emit }) {
|
||||
const intersected = ref<Set<string>>(props.modelValue);
|
||||
const container = ref<HTMLElement>();
|
||||
@@ -60,11 +67,11 @@ export default defineComponent({
|
||||
watch(intersected, (val) => {
|
||||
emit("update:modelValue", val);
|
||||
});
|
||||
watch(props.modelValue, (val)=>{
|
||||
if(intersected.value === val){
|
||||
return;
|
||||
}
|
||||
intersected.value = val;
|
||||
watch(props.modelValue, (val) => {
|
||||
if (intersected.value === val) {
|
||||
return;
|
||||
}
|
||||
intersected.value = val;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
@@ -102,22 +109,22 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
let dismatch = false;
|
||||
for(const oldVal of intersected.value){
|
||||
if(!localIntersected.has(oldVal)){
|
||||
dismatch = true;
|
||||
break;
|
||||
}
|
||||
for (const oldVal of intersected.value) {
|
||||
if (!localIntersected.has(oldVal)) {
|
||||
dismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!dismatch){
|
||||
for(const newVal of localIntersected){
|
||||
if(!intersected.value.has(newVal)){
|
||||
dismatch = true;
|
||||
break;
|
||||
}
|
||||
if (!dismatch) {
|
||||
for (const newVal of localIntersected) {
|
||||
if (!intersected.value.has(newVal)) {
|
||||
dismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(dismatch){
|
||||
intersected.value = localIntersected;
|
||||
if (dismatch) {
|
||||
intersected.value = localIntersected;
|
||||
}
|
||||
}
|
||||
function touchStart(e: TouchEvent) {
|
||||
@@ -128,8 +135,10 @@ export default defineComponent({
|
||||
e.preventDefault();
|
||||
drag(e.touches[0]);
|
||||
}
|
||||
|
||||
let isMine = false;
|
||||
function startDrag(e: MouseEvent | Touch) {
|
||||
containerRect =uContainer.getBoundingClientRect();
|
||||
containerRect = uContainer.getBoundingClientRect();
|
||||
children = uContainer.children;
|
||||
start = getCoords(e);
|
||||
end = start;
|
||||
@@ -139,6 +148,8 @@ export default defineComponent({
|
||||
box.style.left = start.x + "px";
|
||||
uContainer.prepend(box);
|
||||
intersection();
|
||||
isMine = true;
|
||||
emit("dragStart");
|
||||
}
|
||||
function drag(e: MouseEvent | Touch) {
|
||||
end = getCoords(e);
|
||||
@@ -161,7 +172,10 @@ export default defineComponent({
|
||||
document.removeEventListener("mousemove", drag);
|
||||
document.removeEventListener("touchmove", touchMove);
|
||||
box.remove();
|
||||
emit('dragDone', intersected.value);
|
||||
if(isMine){
|
||||
emit("dragDone", intersected.value);
|
||||
}
|
||||
isMine = false;
|
||||
}
|
||||
|
||||
uContainer.addEventListener("mousedown", startDrag);
|
||||
|
||||
Reference in New Issue
Block a user