完成信号源显示,信号源拖拽开窗,替换窗口

This commit is contained in:
fangxiang 2022-04-19 14:44:07 +08:00
parent 74abf865fd
commit 267d827fdb
17 changed files with 1061 additions and 19 deletions

View File

@ -19,6 +19,7 @@
"qrcode.vue": "^3.3.3", "qrcode.vue": "^3.3.3",
"quasar": "^2.6.2", "quasar": "^2.6.2",
"reconnecting-websocket": "^4.4.0", "reconnecting-websocket": "^4.4.0",
"sortablejs": "^1.15.0",
"ts-md5": "^1.2.11", "ts-md5": "^1.2.11",
"v-viewer": "^3.0.9", "v-viewer": "^3.0.9",
"vconsole": "^3.14.6", "vconsole": "^3.14.6",

View File

@ -32,4 +32,5 @@ export namespace EventNamesDefine {
export const CurrentConnectDisconnect = "onCurrentConnectDisconnect"; export const CurrentConnectDisconnect = "onCurrentConnectDisconnect";
export const CurrentConnectConnected = "onCurrentConnectConnected"; export const CurrentConnectConnected = "onCurrentConnectConnected";
export const CurrentClientChanged = "onCurrentClientChanged"; export const CurrentClientChanged = "onCurrentClientChanged";
export const DropToWall = "onDropToWall";
} }

View File

@ -44,11 +44,35 @@
</div> </div>
<q-separator /> <q-separator />
<q-tab-panels v-model="tab" animated class="fit"> <q-tab-panels v-model="tab" animated class="fit text-black">
<q-tab-panel name="signal_source" class="full-width __panel_item"> <q-tab-panel
signal_sourcesignal_sourcesignal_sourcesignal_source name="signal_source"
class="full-width __panel_item q-pa-xs"
>
<q-scroll-area class="fit q-pr-md">
<draggable
class="row q-px-md"
v-model="signal_sources"
group="signal_source"
item-key="uuid"
:move="() => false"
@drop="onSignalSourceDrop($event)"
>
<template #item="{ element }">
<div class="col-4 q-pr-sm q-pb-sm">
<q-btn
class="col-row fit overflow-hidden bg-accent text-white"
:icon="getItemIcon(element.window_type)"
>
<span class="col q-ml-md text-left">
{{ element.name }}
</span>
</q-btn>
</div>
</template>
</draggable>
</q-scroll-area>
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="singal_polling" class="full-width __panel_item"> <q-tab-panel name="singal_polling" class="full-width __panel_item">
singal_pollingsingal_pollingsingal_pollingsingal_polling singal_pollingsingal_pollingsingal_pollingsingal_polling
</q-tab-panel> </q-tab-panel>
@ -132,14 +156,18 @@
</style> </style>
<script lang="ts"> <script lang="ts">
import { defineComponent, Ref, ref } from "vue"; import { defineComponent, Ref, ref, computed, watch } from "vue";
import { useQuasar } from "quasar"; import { useQuasar } from "quasar";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useStore } from "src/store"; import { useStore } from "src/store";
import draggable from "src/third_lib/vuedraggable/vuedraggable";
import { SignalSourceEntity } from "src/entities/SignalSourceEntity";
import { Common } from "src/common/Common";
import EventBus, { EventNamesDefine } from "src/common/EventBus";
export default defineComponent({ export default defineComponent({
name: "PadBottomBarPage", name: "PadBottomBarPage",
components: {}, components: { draggable },
setup() { setup() {
const $store = useStore(); const $store = useStore();
const $q = useQuasar(); const $q = useQuasar();
@ -148,14 +176,61 @@ export default defineComponent({
const paused = ref(false); const paused = ref(false);
const muted = ref(false); const muted = ref(false);
const signal_sources = computed({
get: () => $store.state.signal_sources,
set: (val) => null,
});
const modes = computed({
get: () => $store.state.modes,
set: (val) => null,
});
const pollings = computed({
get: () => $store.state.pollings,
set: (val) => null,
});
const plans = computed({
get: () => $store.state.plans,
set: (val) => null,
});
const tab = ref("signal_source"); const tab = ref("signal_source");
return { return {
tab, tab,
paused, paused,
muted, muted,
signal_sources,
modes,
pollings,
plans,
loga(a: any) { loga(a: any) {
console.log(a); console.log(a);
}, },
getItemIcon(item_type: string) {
return Common.getSignalSourceIcon(item_type);
},
onSignalSourceDrop(evt: any) {
console.log(evt);
if (
evt.data &&
evt.event &&
evt.event.changedTouches &&
evt.event.changedTouches.length
) {
const touchX = evt.event.changedTouches[0].pageX;
const touchY = evt.event.changedTouches[0].pageY;
EventBus.getInstance().emit(EventNamesDefine.DropToWall, {
data: evt.data,
type: "signal_source",
pos: {
x: touchX,
y: touchY,
},
});
}
},
}; };
}, },
}); });

View File

@ -11,7 +11,7 @@
}" }"
class="wall_content" class="wall_content"
> >
<div id="windows" style="position: absolute"> <div id="windows" ref="dom_windows" style="position: absolute">
<vue3-resize-drag <vue3-resize-drag
:w="item.width * ($refs.wall_content?.clientWidth ?? 0)" :w="item.width * ($refs.wall_content?.clientWidth ?? 0)"
:h="item.height * ($refs.wall_content?.clientHeight ?? 0)" :h="item.height * ($refs.wall_content?.clientHeight ?? 0)"
@ -189,6 +189,7 @@ export default defineComponent({
const wall: Ref<any> = ref(null); const wall: Ref<any> = ref(null);
const wall_content: Ref<any> = ref(null); const wall_content: Ref<any> = ref(null);
const dom_windows: Ref<any> = ref(null);
const file_manage_dialog: Ref<any> = ref(null); const file_manage_dialog: Ref<any> = ref(null);
@ -534,6 +535,171 @@ export default defineComponent({
} }
}; };
interface _IDropToWall {
data: any;
type: string;
pos: {
x: number;
y: number;
};
}
EventBus.getInstance().on(
EventNamesDefine.DropToWall,
(evt: _IDropToWall) => {
if (evt && evt.data) {
// const x =
// evt.pos.x -
// (wall.value.parentElement?.offsetLeft ?? 0) -
// wall_content.value.offsetLeft;
// const y =
// evt.pos.y -
// (wall.value.parentElement?.offsetTop ?? 0) -
// wall_content.value.offsetTop;
const dom = document.elementFromPoint(
evt.pos.x,
evt.pos.y
) as HTMLElement;
const signal_sources = GlobalData.getInstance().signal_source.filter(
(item) => (item as any)?.uuid == evt.data.uuid
);
if (dom) {
if (dom.classList.contains("wall_item_flag")) {
//
// 1
const x =
(dom.offsetLeft - wall_content.value.offsetLeft - 1) /
wall_content.value.clientWidth;
const y =
(dom.offsetTop - wall_content.value.offsetTop - 1) /
wall_content.value.clientHeight;
const width = dom.offsetWidth / wall_content.value.clientWidth;
const height = dom.offsetHeight / wall_content.value.clientHeight;
switch (evt.type) {
case "polling":
GlobalData.getInstance()
.getCurrentClient()
?.openPolling(
new Protocol.OpenPollingRequestEntity(
evt.data.uuid,
x,
y,
width,
height
)
);
break;
case "signal_source" /**OpenPollingRequestEntity */:
if (signal_sources.length) {
const signal_source = signal_sources[0];
if (signal_source) {
GlobalData.getInstance()
.getCurrentClient()
?.openWindow(
new Protocol.OpenWindowRequestEntity(
signal_source.uuid,
x,
y,
width,
height
)
);
}
}
break;
}
} else if (dom.classList.contains("window_flag")) {
//
const rep_uuid = dom.getAttribute("uuid");
if (rep_uuid) {
let window = $store.state.windows.find(
(item) => item.uuid == rep_uuid
);
if (window) {
let client = GlobalData.getInstance().getCurrentClient();
if (client) {
let x = window.x;
let y = window.y;
let width = window.width;
let height = window.height;
client.closeWindow(window.window_id);
const open_window_request =
new Protocol.OpenPollingRequestEntity(
evt.data.uuid,
x,
y,
width,
height
);
open_window_request.muted = window.muted;
open_window_request.volume = window.volume;
open_window_request.paused = window.paused;
open_window_request.play_speed = window.play_speed;
setTimeout(() => {
if (!window) {
return;
}
switch (evt.type) {
case "polling":
{
const open_polling_request =
new Protocol.OpenPollingRequestEntity(
evt.data.uuid,
x,
y,
width,
height
);
open_polling_request.muted = window.muted;
open_polling_request.volume = window.volume;
open_polling_request.paused = window.paused;
open_polling_request.play_speed = window.play_speed;
GlobalData.getInstance()
.getCurrentClient()
?.openPolling(open_polling_request);
}
break;
case "signal_source":
{
if (signal_sources.length) {
const signal_source = signal_sources[0];
if (signal_source) {
const open_window_request =
new Protocol.OpenWindowRequestEntity(
signal_source.uuid,
x,
y,
width,
height
);
open_window_request.muted = window.muted;
open_window_request.volume = window.volume;
open_window_request.paused = window.paused;
open_window_request.play_speed =
window.play_speed;
client?.openWindow(open_window_request);
}
}
}
break;
}
}, 50);
}
}
}
}
}
}
}
);
return { return {
wall, wall,
file_manage_dialog, file_manage_dialog,
@ -550,6 +716,7 @@ export default defineComponent({
last_context_menu_pos_y, last_context_menu_pos_y,
windows, windows,
wall_content, wall_content,
dom_windows,
// functions // functions
moveWindow, moveWindow,
@ -699,18 +866,6 @@ export default defineComponent({
if (event && event.changedTouches && event.changedTouches.length) { if (event && event.changedTouches && event.changedTouches.length) {
last_context_menu_pos_x.value = event.changedTouches[0].pageX; last_context_menu_pos_x.value = event.changedTouches[0].pageX;
last_context_menu_pos_y.value = event.changedTouches[0].pageY; last_context_menu_pos_y.value = event.changedTouches[0].pageY;
console.log(event.changedTouches[0]);
console.log(
last_context_menu_pos_x.value -
(wall.value.parentElement?.offsetLeft ?? 0) -
wall_content.value.offsetLeft
);
console.log(
last_context_menu_pos_y.value -
(wall.value.parentElement?.offsetTop ?? 0) -
wall_content.value.offsetTop
);
} }
}, },
async openWindowByLocalFile(event: MouseEvent) { async openWindowByLocalFile(event: MouseEvent) {

View File

@ -0,0 +1 @@
根据源仓库 https://github.com/zzz0908/vue3-resize-drag 修改而来

View File

@ -0,0 +1,47 @@
import { camelize } from "../util/string";
import { events, isReadOnly } from "./sortableEvents";
import { isHtmlAttribute } from "../util/tags";
function project(entries) {
return entries.reduce((res, [key, value]) => {
res[key] = value;
return res;
}, {});
}
function getComponentAttributes({ $attrs, componentData = {} }) {
const attributes = project(
Object.entries($attrs).filter(([key, _]) => isHtmlAttribute(key))
);
return {
...attributes,
...componentData
};
}
function createSortableOption({ $attrs, callBackBuilder }) {
const options = project(getValidSortableEntries($attrs));
Object.entries(callBackBuilder).forEach(([eventType, eventBuilder]) => {
events[eventType].forEach(event => {
options[`on${event}`] = eventBuilder(event);
});
});
const draggable = `[data-draggable]${options.draggable || ""}`;
return {
...options,
draggable
};
}
function getValidSortableEntries(value) {
return Object.entries(value)
.filter(([key, _]) => !isHtmlAttribute(key))
.map(([key, value]) => [camelize(key), value])
.filter(([key, _]) => !isReadOnly(key));
}
export {
getComponentAttributes,
createSortableOption,
getValidSortableEntries
};

View File

@ -0,0 +1,69 @@
const getHtmlElementFromNode = ({ el }) => el;
const addContext = (domElement, context) =>
(domElement.__draggable_context = context);
const getContext = domElement => domElement.__draggable_context;
class ComponentStructure {
constructor({
nodes: { header, default: defaultNodes, footer },
root,
realList
}) {
this.defaultNodes = defaultNodes;
this.children = [...header, ...defaultNodes, ...footer];
this.externalComponent = root.externalComponent;
this.rootTransition = root.transition;
this.tag = root.tag;
this.realList = realList;
}
get _isRootComponent() {
return this.externalComponent || this.rootTransition;
}
render(h, attributes) {
const { tag, children, _isRootComponent } = this;
const option = !_isRootComponent ? children : { default: () => children };
return h(tag, attributes, option);
}
updated() {
const { defaultNodes, realList } = this;
defaultNodes.forEach((node, index) => {
addContext(getHtmlElementFromNode(node), {
element: realList[index],
index
});
});
}
getUnderlyingVm(domElement) {
return getContext(domElement);
}
getVmIndexFromDomIndex(domIndex, element) {
const { defaultNodes } = this;
const { length } = defaultNodes;
const domChildren = element.children;
const domElement = domChildren.item(domIndex);
if (domElement === null) {
return length;
}
const context = getContext(domElement);
if (context) {
return context.index;
}
if (length === 0) {
return 0;
}
const firstDomListElement = getHtmlElementFromNode(defaultNodes[0]);
const indexFirstDomListElement = [...domChildren].findIndex(
element => element === firstDomListElement
);
return domIndex < indexFirstDomListElement ? 0 : length;
}
}
export { ComponentStructure };

View File

@ -0,0 +1,56 @@
import { ComponentStructure } from "./componentStructure";
import { isHtmlTag, isTransition } from "../util/tags";
import { resolveComponent, TransitionGroup } from "vue";
function getSlot(slots, key) {
const slotValue = slots[key];
return slotValue ? slotValue() : [];
}
function computeNodes({ $slots, realList, getKey }) {
const normalizedList = realList || [];
const [header, footer] = ["header", "footer"].map(name =>
getSlot($slots, name)
);
const { item } = $slots;
if (!item) {
throw new Error("draggable element must have an item slot");
}
const defaultNodes = normalizedList.flatMap((element, index) =>
item({ element, index }).map(node => {
node.key = getKey(element);
node.props = { ...(node.props || {}), "data-draggable": true };
return node;
})
);
if (defaultNodes.length !== normalizedList.length) {
throw new Error("Item slot must have only one child");
}
return {
header,
footer,
default: defaultNodes
};
}
function getRootInformation(tag) {
const transition = isTransition(tag);
const externalComponent = !isHtmlTag(tag) && !transition;
return {
transition,
externalComponent,
tag: externalComponent
? resolveComponent(tag)
: transition
? TransitionGroup
: tag
};
}
function computeComponentStructure({ $slots, tag, realList, getKey }) {
const nodes = computeNodes({ $slots, realList, getKey });
const root = getRootInformation(tag);
return new ComponentStructure({ nodes, root, realList });
}
export { computeComponentStructure };

View File

@ -0,0 +1,18 @@
const manageAndEmit = ["Start", "Add", "Remove", "Update", "End"];
const emit = ["Choose", "Unchoose", "Sort", "Filter", "Clone"];
const manage = ["Move"];
const eventHandlerNames = [manage, manageAndEmit, emit]
.flatMap(events => events)
.map(evt => `on${evt}`);
const events = {
manage,
manageAndEmit,
emit
};
function isReadOnly(eventName) {
return eventHandlerNames.indexOf(eventName) !== -1;
}
export { events, isReadOnly };

View File

@ -0,0 +1 @@
根据源仓库 https://github.com/SortableJS/vue.draggable.next 修改而来

View File

@ -0,0 +1,9 @@
function getConsole() {
if (typeof window !== "undefined") {
return window.console;
}
return global.console;
}
const console = getConsole();
export { console };

View File

@ -0,0 +1,15 @@
function removeNode(node) {
if (node.parentElement !== null) {
node.parentElement.removeChild(node);
}
}
function insertNodeAt(fatherNode, node, position) {
const refNode =
position === 0
? fatherNode.children[0]
: fatherNode.children[position - 1].nextSibling;
fatherNode.insertBefore(node, refNode);
}
export { insertNodeAt, removeNode };

View File

@ -0,0 +1,12 @@
function cached(fn) {
const cache = Object.create(null);
return function cachedFn(str) {
const hit = cache[str];
return hit || (cache[str] = fn(str));
};
}
const regex = /-(\w)/g;
const camelize = cached(str => str.replace(regex, (_, c) => c.toUpperCase()));
export { camelize };

View File

@ -0,0 +1,138 @@
const tags = [
"a",
"abbr",
"address",
"area",
"article",
"aside",
"audio",
"b",
"base",
"bdi",
"bdo",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"cite",
"code",
"col",
"colgroup",
"data",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"div",
"dl",
"dt",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
"img",
"input",
"ins",
"kbd",
"label",
"legend",
"li",
"link",
"main",
"map",
"mark",
"math",
"menu",
"menuitem",
"meta",
"meter",
"nav",
"noscript",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"pre",
"progress",
"q",
"rb",
"rp",
"rt",
"rtc",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"slot",
"small",
"source",
"span",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"u",
"ul",
"var",
"video",
"wbr"
];
function isHtmlTag(name) {
return tags.includes(name);
}
function isTransition(name) {
return ["transition-group", "TransitionGroup"].includes(name);
}
function isHtmlAttribute(value) {
return (
["id", "class", "role", "style"].includes(value) ||
value.startsWith("data-") ||
value.startsWith("aria-") ||
value.startsWith("on")
);
}
export { isHtmlTag, isHtmlAttribute, isTransition };

View File

@ -0,0 +1,74 @@
declare const draggableComponent: import("vue").DefineComponent<{
list: {
type: ArrayConstructor;
required: boolean;
default: any;
};
modelValue: {
type: ArrayConstructor;
required: boolean;
default: any;
};
itemKey: {
type: (FunctionConstructor | StringConstructor)[];
required: boolean;
};
clone: {
type: FunctionConstructor;
default: (original: any) => any;
};
tag: {
type: StringConstructor;
default: string;
};
move: {
type: FunctionConstructor;
default: any;
};
componentData: {
type: ObjectConstructor;
required: boolean;
default: any;
};
}, unknown, {
error: boolean;
}, {
realList(): any;
getKey(): any;
}, {
getUnderlyingVm(domElement: any): any;
getUnderlyingPotencialDraggableComponent(htmElement: any): any;
emitChanges(evt: any): void;
alterList(onList: any): void;
spliceList(): void;
updatePosition(oldIndex: any, newIndex: any): void;
getRelatedContextFromMoveEvent({ to, related }: {
to: any;
related: any;
}): any;
getVmIndexFromDomIndex(domIndex: any): any;
onDragStart(evt: any): void;
onDragAdd(evt: any): void;
onDragRemove(evt: any): void;
onDragUpdate(evt: any): void;
computeFutureIndex(relatedContext: any, evt: any): any;
onDragMove(evt: any, originalEvent: any): any;
onDragEnd(): void;
}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, any[], any, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<{
move: Function;
tag: string;
clone: Function;
list: unknown[];
modelValue: unknown[];
componentData: Record<string, any>;
} & {
itemKey?: string | Function;
}>, {
move: Function;
tag: string;
clone: Function;
list: unknown[];
modelValue: unknown[];
componentData: Record<string, any>;
}>;
export default draggableComponent;

View File

@ -0,0 +1,365 @@
import Sortable from "sortablejs";
import { insertNodeAt, removeNode } from "./util/htmlHelper";
import { console } from "./util/console";
import {
getComponentAttributes,
createSortableOption,
getValidSortableEntries,
} from "./core/componentBuilderHelper";
import { computeComponentStructure } from "./core/renderHelper";
import { events } from "./core/sortableEvents";
import { h, defineComponent, nextTick } from "vue";
function emit(evtName, evtData) {
nextTick(() => this.$emit(evtName.toLowerCase(), evtData));
}
function manage(evtName) {
return (evtData, originalElement) => {
if (this.realList !== null) {
return this[`onDrag${evtName}`](evtData, originalElement);
}
};
}
function manageAndEmit(evtName) {
const delegateCallBack = manage.call(this, evtName);
return (evtData, originalElement) => {
delegateCallBack.call(this, evtData, originalElement);
emit.call(this, evtName, evtData);
};
}
let draggingElement = null;
const props = {
list: {
type: Array,
required: false,
default: null,
},
modelValue: {
type: Array,
required: false,
default: null,
},
itemKey: {
type: [String, Function],
required: true,
},
clone: {
type: Function,
default: (original) => {
return original;
},
},
tag: {
type: String,
default: "div",
},
move: {
type: Function,
default: null,
},
componentData: {
type: Object,
required: false,
default: null,
},
};
const emits = [
"update:modelValue",
"change",
"drop",
...[...events.manageAndEmit, ...events.emit].map((evt) => evt.toLowerCase()),
];
const draggableComponent = defineComponent({
name: "draggable",
inheritAttrs: false,
props,
emits,
data() {
return {
error: false,
};
},
render() {
try {
this.error = false;
const { $slots, $attrs, tag, componentData, realList, getKey } = this;
const componentStructure = computeComponentStructure({
$slots,
tag,
realList,
getKey,
});
this.componentStructure = componentStructure;
const attributes = getComponentAttributes({ $attrs, componentData });
return componentStructure.render(h, attributes);
} catch (err) {
this.error = true;
return h("pre", { style: { color: "red" } }, err.stack);
}
},
created() {
if (this.list !== null && this.modelValue !== null) {
console.error(
"modelValue and list props are mutually exclusive! Please set one or another."
);
}
},
mounted() {
if (this.error) {
return;
}
const self = this;
const { $attrs, $el, componentStructure } = this;
componentStructure.updated();
const sortableOptions = createSortableOption({
$attrs,
callBackBuilder: {
manageAndEmit: (event) => manageAndEmit.call(this, event),
emit: (event) => emit.bind(this, event),
manage: (event) => manage.call(this, event),
},
});
const targetDomElement = $el.nodeType === 1 ? $el : $el.parentElement;
function AfxTestPlugin() {
function AfxTest() {
this.defaults = {};
}
AfxTest.prototype = {
dropGlobal: function dropGlobal(evt) {
console.log("oooooooooooooooooooo");
console.log(evt);
try {
self.$emit("drop", {
data: evt.dragEl.__draggable_context.element,
event: evt.originalEvent,
});
} catch {}
},
};
function _extends() {
_extends =
Object.assign ||
function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
return _extends(AfxTest, {
pluginName: "afxtest",
});
}
try {
Sortable.mount(new AfxTestPlugin());
} catch {}
sortableOptions.fallbackOnBody = true;
this._sortable = new Sortable(targetDomElement, sortableOptions);
this.targetDomElement = targetDomElement;
targetDomElement.__draggable_component__ = this;
},
updated() {
this.componentStructure.updated();
},
beforeUnmount() {
if (this._sortable !== undefined) this._sortable.destroy();
},
computed: {
realList() {
const { list } = this;
return list ? list : this.modelValue;
},
getKey() {
const { itemKey } = this;
if (typeof itemKey === "function") {
return itemKey;
}
return (element) => element[itemKey];
},
},
watch: {
$attrs: {
handler(newOptionValue) {
const { _sortable } = this;
if (!_sortable) return;
getValidSortableEntries(newOptionValue).forEach(([key, value]) => {
_sortable.option(key, value);
});
},
deep: true,
},
},
methods: {
getUnderlyingVm(domElement) {
return this.componentStructure.getUnderlyingVm(domElement) || null;
},
getUnderlyingPotencialDraggableComponent(htmElement) {
//TODO check case where you need to see component children
return htmElement.__draggable_component__;
},
emitChanges(evt) {
nextTick(() => this.$emit("change", evt));
},
alterList(onList) {
if (this.list) {
onList(this.list);
return;
}
const newList = [...this.modelValue];
onList(newList);
this.$emit("update:modelValue", newList);
},
spliceList() {
const spliceList = (list) => list.splice(...arguments);
this.alterList(spliceList);
},
updatePosition(oldIndex, newIndex) {
const updatePosition = (list) =>
list.splice(newIndex, 0, list.splice(oldIndex, 1)[0]);
this.alterList(updatePosition);
},
getRelatedContextFromMoveEvent({ to, related }) {
const component = this.getUnderlyingPotencialDraggableComponent(to);
if (!component) {
return { component };
}
const list = component.realList;
const context = { list, component };
if (to !== related && list) {
const destination = component.getUnderlyingVm(related) || {};
return { ...destination, ...context };
}
return context;
},
getVmIndexFromDomIndex(domIndex) {
return this.componentStructure.getVmIndexFromDomIndex(
domIndex,
this.targetDomElement
);
},
onDragStart(evt) {
this.context = this.getUnderlyingVm(evt.item);
evt.item._underlying_vm_ = this.clone(this.context.element);
draggingElement = evt.item;
},
onDragAdd(evt) {
const element = evt.item._underlying_vm_;
if (element === undefined) {
return;
}
removeNode(evt.item);
const newIndex = this.getVmIndexFromDomIndex(evt.newIndex);
this.spliceList(newIndex, 0, element);
const added = { element, newIndex };
this.emitChanges({ added });
},
onDragRemove(evt) {
insertNodeAt(this.$el, evt.item, evt.oldIndex);
if (evt.pullMode === "clone") {
removeNode(evt.clone);
return;
}
const { index: oldIndex, element } = this.context;
this.spliceList(oldIndex, 1);
const removed = { element, oldIndex };
this.emitChanges({ removed });
},
onDragUpdate(evt) {
removeNode(evt.item);
insertNodeAt(evt.from, evt.item, evt.oldIndex);
const oldIndex = this.context.index;
const newIndex = this.getVmIndexFromDomIndex(evt.newIndex);
this.updatePosition(oldIndex, newIndex);
const moved = { element: this.context.element, oldIndex, newIndex };
this.emitChanges({ moved });
},
computeFutureIndex(relatedContext, evt) {
if (!relatedContext.element) {
return 0;
}
const domChildren = [...evt.to.children].filter(
(el) => el.style["display"] !== "none"
);
const currentDomIndex = domChildren.indexOf(evt.related);
const currentIndex =
relatedContext.component.getVmIndexFromDomIndex(currentDomIndex);
const draggedInList = domChildren.indexOf(draggingElement) !== -1;
return draggedInList || !evt.willInsertAfter
? currentIndex
: currentIndex + 1;
},
onDragMove(evt, originalEvent) {
const { move, realList } = this;
if (!move || !realList) {
return true;
}
const relatedContext = this.getRelatedContextFromMoveEvent(evt);
const futureIndex = this.computeFutureIndex(relatedContext, evt);
const draggedContext = {
...this.context,
futureIndex,
};
const sendEvent = {
...evt,
relatedContext,
draggedContext,
};
return move(sendEvent, originalEvent);
},
onDragEnd() {
draggingElement = null;
},
},
});
export default draggableComponent;

View File

@ -6669,6 +6669,11 @@ sort-keys@^2.0.0:
dependencies: dependencies:
is-plain-obj "^1.0.0" is-plain-obj "^1.0.0"
sortablejs@^1.15.0:
version "1.15.0"
resolved "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.0.tgz#53230b8aa3502bb77a29e2005808ffdb4a5f7e2a"
integrity sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==
source-list-map@^2.0.0: source-list-map@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.npmmirror.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" resolved "https://registry.npmmirror.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"