如何通过 ctrl 复制事件,在 FullCalendar v5 中拖放 - 仅限纯 javascript

How to copy events via ctrl, drag & drop in FullCalendar v5 - pure javascript only

我想在 FullCalendar v5 中实现 CTRL + Drag & Drop 仅使用 PURE JavaScript。

我对 主题 进行了研究,发现它在 FC github 上作为 new feature UI request 进行了讨论。几乎没有关于如何做到这一点的建议,甚至是可行的建议。

arshaw posted on Aug 19, 2015

You can use eventDrop to create this feature. jsEvent has a ctrlKey
on which you can test. Copy the event, received as a parameter, in a new variable.
revertFunc to make go back, and then apply renderEvent with the new variable created.

chris-verclytte posted on Apr 11, 2016 对我没有任何作用

If it can help, here is a little trick I use waiting for this new feature to be integrated.
In eventDrop callback

    // If no alt key pressed we move the event, else we duplicate it
    if (!jsEvent.altKey) {
        // Handle drag'n'drop copy
    } else {
        // Handle drag'n'drop duplication
        // Here I add the event to event source
        _addEventToSelectedSource(event.start, event.end);
        // "Disable" the default behavior using revertFunc to avoid event moving
         revertFunc();
    }
The only problem with this is that the copied event disappears during drag'n'drop due to https://github.com/fullcalendar/fullcalendar/blob/master/src/common/Grid.events.js#L273

我喜欢 AllyMurray posted on Jul 13, 2018

的最佳解决方案
For anyone that comes across this issue, I have created a solution that should give you a starting point to work from. It uses the same approach as external events and leaves the original event in place.


https://codepen.io/ally-murray/full/JBdaBV/

但我不知道如何在纯 javascript 中实现此解决方案。

有人能帮忙吗?我更喜欢复制的工作方式,即按 CTRL 键表示复制,这样原始事件就保持在原来的位置。

jsFiddle

我有一个可行的最小解决方案。如果按住 Ctrl 键,它会在原始日期克隆移动的事件。

要测试此代码段,只需在测试前点击页面顶部的输入,否则结果 iframe 没有焦点并且不会触发 keydownkeyup 事件。

// Beginning of the workaround for this: https://github.com/fullcalendar/fullcalendar/blob/3e89de5d8206c32b6be326133b6787d54c6fd66c/packages/interaction/src/dnd/PointerDragging.ts#L306
const ctrlKeyDescriptor = Object.getOwnPropertyDescriptor(
  MouseEvent.prototype,
  'ctrlKey'
);

// Always return false for event.ctrlKey when event is of type MouseEvent
ctrlKeyDescriptor.get = function() {
  return false;
};

Object.defineProperty(MouseEvent.prototype, 'ctrlKey', ctrlKeyDescriptor);
// End of the workaround

let calendarEl = document.getElementById('calendar-container');

// Hold the ctrlKey state, emit events to the subscribers when it changes
const ctrlKeyChanged = (function() {
  let ctrlHeld = false;
  let subscriptions = [];
  ['keydown', 'keyup'].forEach(x =>
    document.addEventListener(x, e => {
      // emit only when the key state has changed
      if (ctrlHeld !== e.ctrlKey) subscriptions.forEach(fun => fun(e.ctrlKey));

      ctrlHeld = e.ctrlKey;
    })
  );

  function subscribe(callback) {
    subscriptions.push(callback);
    callback(ctrlHeld); // Emit the current state (case when Ctrl is already being held)
  }

  function unsubscribe(callback) {
    const index = subscriptions.indexOf(callback);
    subscriptions.splice(index, 1);
  }

  return { subscribe, unsubscribe };
})();

const extractEventProperties = ({ title, start, end, allDay }) => ({
  title,
  start,
  end,
  allDay
});

const callbackKey = Symbol();

let calendar = new FullCalendar.Calendar(calendarEl, {
  editable: true,
  droppable: true,
  eventDragStart: e => {
    let event;
    const callback = ctrlKey => {
      if (ctrlKey) {
        event = calendar.addEvent(extractEventProperties(e.event));
      } else {
        event && event.remove();
      }
    };
    ctrlKeyChanged.subscribe(callback);
    // store the callback for further unsubscribe
    e.event.setExtendedProp(callbackKey, callback); 
  },
  // stop listening when the event has been dropped
  eventDragStop: e => ctrlKeyChanged.unsubscribe(e.event.extendedProps[callbackKey]),
  events: [
    {
      title: 'event1',
      start: new Date,
      allDay: true // will make the time show
    },
    {
      title: 'event2',
      start: new Date().setDate(new Date().getDate() + 1),
      end: new Date().setDate(new Date().getDate() + 1)
    },
    {
      title: 'event3',
      start: new Date().setDate(new Date().getDate() - 1),
      allDay: true
    }
  ]
});

calendar.render();
<link href="https://cdn.jsdelivr.net/npm/fullcalendar@5.6.0/main.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.6.0/main.min.js"></script>

<input placeholder="Click here to give the focus to the result iframe">
<div id="calendar-container"></div>

主要的困难是 Fullcalendar 在按住 Ctrl 键时禁用拖动行为:

// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
function isPrimaryMouseButton(ev: MouseEvent) {
  return ev.button === 0 && !ev.ctrlKey
}

你可以在官方仓库中看到这段代码here which is being called here

解决方法包括改变 MouseEvent.prototype 以便在访问 ctrlKey.

时总是 return false

如果您有兴趣,我已经提供了解决方案: