Skip to main content

DHTMLX Kanban. Disabling drag and drop to specific columns example

Some columns should only be populated by specific roles or through automated processes. For instance, only testers should move cards to "Done", or only a CI pipeline should mark tasks as deployed. Disabling drag-and-drop to specific columns enforces these workflow rules directly in the UI.

Live example

const { Kanban, template } = kanban;
const { columns, cards } = getData();

const board = new Kanban("#root", {
  columns,
  cards,
});

let startCol;

board.api.on("start-drag-card", ({ columnId }) => { 
  startCol = columnId;
  if (startCol != "done")
    board.api.exec("update-column", {
      id: "done",
      column: {
        overlay: template(`
                    <div class="blockOverlay disable-drop">
                        <span class="disable-drop-header">Drop is not allowed</span>
                        <span class="disable-drop-description">Only testers can move cards to this
                            column</span>
                    </div>`),
      },
    });
});

board.api.intercept("drag-card", ({ columnId }) => {
  if (startCol != "done" && columnId == "done") return false;
});

board.api.intercept("move-card", ({ columnId }) => {
  if (startCol != "done")
    board.api.exec("update-column", {
      id: "done",
      column: {
        overlay: null,
      },
    });
  if (columnId == "done") return false;
});    
<!-- custom styles -->

<style>
.disable-drop {
		text-align: center;
		position: absolute;
		top: 0;
		height: 100%;
		width: 100%;
		display: flex;
		justify-content: center;
		flex-direction: column;
		background: rgba(247, 247, 247, 0.8);
		border: 1px dashed #ededed;
		border-radius: 6px;
	}
	.disable-drop-header {
		font-weight: 500;
	}
	.disable-drop-description {
		color: var(--wx-color-font-alt);
		padding: 0 28px;
	}
	.wx-collapsed .disable-drop-description {
		display: none;
	}
	.wx-collapsed .disable-drop-header {
		display: flex;
		align-items: center;
		writing-mode: tb-rl;
		transform: rotate(180deg);
	}
</style>

The code uses three event handlers working together. on("start-drag-card") records the source column and applies a visual overlay on the "Done" column using update-column with a template() overlay. intercept("drag-card") returns false when the target is "Done" and the source isn't, preventing the card from entering the column during drag. intercept("move-card") blocks the final move and removes the overlay. The overlay uses template() to render a "Drop is not allowed" message with custom styling.

Solution overview

  1. Listen to start-drag-card to track the source column and show a blocking overlay on restricted columns
  2. Intercept drag-card to return false when dragging to a restricted column. This prevents the visual drag feedback
  3. Intercept move-card to block the actual move and clean up the overlay (set overlay: null)
  4. Style the overlay with CSS for a clear visual indication

Key points

  • Three-event pattern: Blocking drops properly requires handling start-drag-card, drag-card, and move-card. Skipping any of these leaves gaps in the UX
  • Column overlays are dynamic: update-column with an overlay property renders content over the column. Set to null to remove
  • template() for overlays: The overlay content must be wrapped in template() for proper rendering

API reference

  • api.on: Subscribes to events
  • api.intercept: Intercepts and blocks events
  • template: Creates reactive templates for overlays and custom rendering

Additional resources