vanduo-framework 1.1.8 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -31
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +720 -111
- package/dist/vanduo.cjs.js.map +3 -3
- package/dist/vanduo.cjs.min.js +11 -11
- package/dist/vanduo.cjs.min.js.map +4 -4
- package/dist/vanduo.css +285 -1
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +720 -111
- package/dist/vanduo.esm.js.map +3 -3
- package/dist/vanduo.esm.min.js +11 -11
- package/dist/vanduo.esm.min.js.map +4 -4
- package/dist/vanduo.js +720 -111
- package/dist/vanduo.js.map +3 -3
- package/dist/vanduo.min.css +1 -1
- package/dist/vanduo.min.js +11 -11
- package/dist/vanduo.min.js.map +4 -4
- package/js/components/code-snippet.js +5 -3
- package/js/components/doc-search.js +90 -73
- package/js/components/grid.js +22 -22
- package/js/components/lazy-load.js +353 -0
- package/js/components/theme-customizer.js +20 -4
- package/js/components/tooltips.js +1 -1
- package/js/index.js +1 -0
- package/js/utils/helpers.js +24 -12
- package/js/vanduo.js +14 -14
- package/package.json +3 -3
package/dist/vanduo.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Vanduo v1.
|
|
1
|
+
/*! Vanduo v1.2.0 | Built: 2026-02-22T21:30:31.940Z | git:64c88fd | development */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -133,7 +133,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
133
133
|
(function() {
|
|
134
134
|
"use strict";
|
|
135
135
|
const Vanduo2 = {
|
|
136
|
-
version: "1.
|
|
136
|
+
version: "1.2.0",
|
|
137
137
|
components: {},
|
|
138
138
|
/**
|
|
139
139
|
* Initialize framework
|
|
@@ -168,7 +168,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
});
|
|
171
|
-
console.log("Vanduo Framework v1.
|
|
171
|
+
console.log("Vanduo Framework v1.2.0 initialized");
|
|
172
172
|
},
|
|
173
173
|
/**
|
|
174
174
|
* Register a component
|
|
@@ -183,7 +183,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
183
183
|
* @param {string} name - Component name
|
|
184
184
|
*/
|
|
185
185
|
reinit: function(name) {
|
|
186
|
-
|
|
186
|
+
const component = this.components[name];
|
|
187
187
|
if (component && component.init && typeof component.init === "function") {
|
|
188
188
|
try {
|
|
189
189
|
component.init();
|
|
@@ -197,9 +197,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
197
197
|
* Uses lifecycle manager for memory leak prevention
|
|
198
198
|
*/
|
|
199
199
|
destroyAll: function() {
|
|
200
|
-
|
|
201
|
-
for (
|
|
202
|
-
|
|
200
|
+
const names = Object.keys(this.components);
|
|
201
|
+
for (let i = 0; i < names.length; i++) {
|
|
202
|
+
const component = this.components[names[i]];
|
|
203
203
|
if (component && component.destroyAll && typeof component.destroyAll === "function") {
|
|
204
204
|
try {
|
|
205
205
|
component.destroyAll();
|
|
@@ -516,7 +516,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
516
516
|
html = this.formatHtml(html);
|
|
517
517
|
html = this.escapeHtml(html);
|
|
518
518
|
html = this.highlightHtml(html);
|
|
519
|
-
|
|
519
|
+
const codeEl = document.createElement("code");
|
|
520
|
+
codeEl.innerHTML = html;
|
|
521
|
+
pane.replaceChildren(codeEl);
|
|
520
522
|
pane.dataset.extracted = "true";
|
|
521
523
|
},
|
|
522
524
|
/**
|
|
@@ -621,7 +623,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
621
623
|
}
|
|
622
624
|
const codeWrapper = document.createElement("div");
|
|
623
625
|
codeWrapper.className = "vd-code-snippet-code";
|
|
624
|
-
codeWrapper.
|
|
626
|
+
codeWrapper.appendChild(code.cloneNode(true));
|
|
625
627
|
code.parentNode.removeChild(code);
|
|
626
628
|
pane.appendChild(lineNumbers);
|
|
627
629
|
pane.appendChild(codeWrapper);
|
|
@@ -1387,20 +1389,20 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1387
1389
|
// js/components/grid.js
|
|
1388
1390
|
(function() {
|
|
1389
1391
|
"use strict";
|
|
1390
|
-
|
|
1392
|
+
const supportsHas = (function() {
|
|
1391
1393
|
try {
|
|
1392
1394
|
return CSS.supports("selector(:has(*))");
|
|
1393
1395
|
} catch (_e) {
|
|
1394
1396
|
return false;
|
|
1395
1397
|
}
|
|
1396
1398
|
})();
|
|
1397
|
-
|
|
1399
|
+
const GridLayout = {
|
|
1398
1400
|
instances: /* @__PURE__ */ new Map(),
|
|
1399
1401
|
/**
|
|
1400
1402
|
* Initialize all grid layout containers
|
|
1401
1403
|
*/
|
|
1402
1404
|
init: function() {
|
|
1403
|
-
|
|
1405
|
+
const containers = document.querySelectorAll("[data-layout-mode]");
|
|
1404
1406
|
containers.forEach(function(container) {
|
|
1405
1407
|
if (this.instances.has(container)) {
|
|
1406
1408
|
return;
|
|
@@ -1414,8 +1416,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1414
1416
|
* @param {HTMLElement} container - Element with data-layout-mode
|
|
1415
1417
|
*/
|
|
1416
1418
|
initContainer: function(container) {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
+
const mode = container.getAttribute("data-layout-mode") || "standard";
|
|
1420
|
+
const cleanupFunctions = [];
|
|
1419
1421
|
this.applyMode(container, mode);
|
|
1420
1422
|
container.setAttribute("role", "region");
|
|
1421
1423
|
container.setAttribute("aria-label", "Grid layout: " + mode + " mode");
|
|
@@ -1428,15 +1430,15 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1428
1430
|
* Initialize toggle buttons that target grid containers
|
|
1429
1431
|
*/
|
|
1430
1432
|
initToggleButtons: function() {
|
|
1431
|
-
|
|
1433
|
+
const toggleButtons = document.querySelectorAll("[data-grid-toggle]");
|
|
1432
1434
|
toggleButtons.forEach(function(button) {
|
|
1433
1435
|
if (button.getAttribute("data-grid-initialized") === "true") {
|
|
1434
1436
|
return;
|
|
1435
1437
|
}
|
|
1436
|
-
|
|
1438
|
+
const clickHandler = function(e) {
|
|
1437
1439
|
e.preventDefault();
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
+
const targetSelector = button.getAttribute("data-grid-toggle");
|
|
1441
|
+
let target;
|
|
1440
1442
|
if (targetSelector) {
|
|
1441
1443
|
target = document.querySelector(targetSelector);
|
|
1442
1444
|
} else {
|
|
@@ -1462,10 +1464,10 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1462
1464
|
*/
|
|
1463
1465
|
applyFibFallback: function(container) {
|
|
1464
1466
|
if (supportsHas) return;
|
|
1465
|
-
|
|
1467
|
+
const rows = container.querySelectorAll(".vd-row, .row");
|
|
1466
1468
|
rows.forEach(function(row) {
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
+
const cols = row.querySelectorAll(':scope > [class*="vd-col-"], :scope > [class*="col-"]');
|
|
1470
|
+
const count = cols.length;
|
|
1469
1471
|
if (count === 1) {
|
|
1470
1472
|
row.style.gridTemplateColumns = "1fr";
|
|
1471
1473
|
} else if (count === 2) {
|
|
@@ -1484,7 +1486,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1484
1486
|
* @param {HTMLElement} container - Grid container
|
|
1485
1487
|
*/
|
|
1486
1488
|
removeFibFallback: function(container) {
|
|
1487
|
-
|
|
1489
|
+
const rows = container.querySelectorAll(".vd-row, .row");
|
|
1488
1490
|
rows.forEach(function(row) {
|
|
1489
1491
|
row.style.gridTemplateColumns = "";
|
|
1490
1492
|
});
|
|
@@ -1505,11 +1507,11 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1505
1507
|
}
|
|
1506
1508
|
container.setAttribute("data-layout-mode", mode);
|
|
1507
1509
|
container.setAttribute("aria-label", "Grid layout: " + mode + " mode");
|
|
1508
|
-
|
|
1510
|
+
const toggleButtons = document.querySelectorAll("[data-grid-toggle]");
|
|
1509
1511
|
toggleButtons.forEach(function(btn) {
|
|
1510
|
-
|
|
1512
|
+
const targetSelector = btn.getAttribute("data-grid-toggle");
|
|
1511
1513
|
if (targetSelector && container.matches(targetSelector)) {
|
|
1512
|
-
|
|
1514
|
+
const isActive = mode === "fibonacci";
|
|
1513
1515
|
if (isActive) {
|
|
1514
1516
|
btn.classList.add("is-active");
|
|
1515
1517
|
} else {
|
|
@@ -1518,11 +1520,11 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1518
1520
|
btn.setAttribute("aria-pressed", isActive ? "true" : "false");
|
|
1519
1521
|
}
|
|
1520
1522
|
});
|
|
1521
|
-
|
|
1523
|
+
const instance = this.instances.get(container);
|
|
1522
1524
|
if (instance) {
|
|
1523
1525
|
instance.mode = mode;
|
|
1524
1526
|
}
|
|
1525
|
-
|
|
1527
|
+
let event;
|
|
1526
1528
|
try {
|
|
1527
1529
|
event = new CustomEvent("grid:modechange", {
|
|
1528
1530
|
bubbles: true,
|
|
@@ -1549,8 +1551,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1549
1551
|
container = document.querySelector(container);
|
|
1550
1552
|
}
|
|
1551
1553
|
if (!container) return;
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
+
const currentMode = container.getAttribute("data-layout-mode") || "standard";
|
|
1555
|
+
const newMode = currentMode === "fibonacci" ? "standard" : "fibonacci";
|
|
1554
1556
|
this.applyMode(container, newMode);
|
|
1555
1557
|
},
|
|
1556
1558
|
/**
|
|
@@ -1583,7 +1585,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1583
1585
|
* @param {HTMLElement} container - Grid container
|
|
1584
1586
|
*/
|
|
1585
1587
|
destroy: function(container) {
|
|
1586
|
-
|
|
1588
|
+
const instance = this.instances.get(container);
|
|
1587
1589
|
if (!instance) return;
|
|
1588
1590
|
instance.cleanup.forEach(function(fn) {
|
|
1589
1591
|
fn();
|
|
@@ -1600,7 +1602,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1600
1602
|
this.instances.forEach(function(instance, container) {
|
|
1601
1603
|
this.destroy(container);
|
|
1602
1604
|
}.bind(this));
|
|
1603
|
-
|
|
1605
|
+
const toggleButtons = document.querySelectorAll('[data-grid-initialized="true"]');
|
|
1604
1606
|
toggleButtons.forEach(function(button) {
|
|
1605
1607
|
if (button._gridCleanup) {
|
|
1606
1608
|
button._gridCleanup();
|
|
@@ -3697,9 +3699,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
3697
3699
|
},
|
|
3698
3700
|
// Default values
|
|
3699
3701
|
DEFAULTS: {
|
|
3700
|
-
PRIMARY_LIGHT: "
|
|
3702
|
+
PRIMARY_LIGHT: "black",
|
|
3701
3703
|
PRIMARY_DARK: "amber",
|
|
3702
|
-
NEUTRAL: "
|
|
3704
|
+
NEUTRAL: "neutral",
|
|
3703
3705
|
RADIUS: "0.5",
|
|
3704
3706
|
FONT: "ubuntu",
|
|
3705
3707
|
THEME: "system"
|
|
@@ -4062,21 +4064,33 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4062
4064
|
* Generate panel HTML
|
|
4063
4065
|
*/
|
|
4064
4066
|
getPanelHTML: function() {
|
|
4067
|
+
const esc = typeof escapeHtml === "function" ? escapeHtml : function(text) {
|
|
4068
|
+
const div = document.createElement("div");
|
|
4069
|
+
div.textContent = String(text ?? "");
|
|
4070
|
+
return div.innerHTML;
|
|
4071
|
+
};
|
|
4072
|
+
const safeColor = function(value) {
|
|
4073
|
+
const normalized = String(value ?? "").trim();
|
|
4074
|
+
if (/^(#[0-9a-fA-F]{3,8}|rgb[a]?\([^)]{1,60}\)|hsl[a]?\([^)]{1,60}\)|var\(--[a-zA-Z0-9_-]{1,40}\))$/.test(normalized)) {
|
|
4075
|
+
return normalized;
|
|
4076
|
+
}
|
|
4077
|
+
return "#000000";
|
|
4078
|
+
};
|
|
4065
4079
|
let primarySwatches = "";
|
|
4066
4080
|
for (const [key, value] of Object.entries(this.PRIMARY_COLORS)) {
|
|
4067
|
-
primarySwatches += `<button class="tc-color-swatch${key === this.state.primary ? " is-active" : ""}" data-color="${key}" style="--swatch-color: ${value.color}" title="${value.name}"></button>`;
|
|
4081
|
+
primarySwatches += `<button class="tc-color-swatch${key === this.state.primary ? " is-active" : ""}" data-color="${esc(key)}" style="--swatch-color: ${safeColor(value.color)}" title="${esc(value.name)}"></button>`;
|
|
4068
4082
|
}
|
|
4069
4083
|
let neutralSwatches = "";
|
|
4070
4084
|
for (const [key, value] of Object.entries(this.NEUTRAL_COLORS)) {
|
|
4071
|
-
neutralSwatches += `<button class="tc-neutral-swatch${key === this.state.neutral ? " is-active" : ""}" data-neutral="${key}" style="--swatch-color: ${value.color}" title="${value.name}"><span>${value.name}</span></button>`;
|
|
4085
|
+
neutralSwatches += `<button class="tc-neutral-swatch${key === this.state.neutral ? " is-active" : ""}" data-neutral="${esc(key)}" style="--swatch-color: ${safeColor(value.color)}" title="${esc(value.name)}"><span>${esc(value.name)}</span></button>`;
|
|
4072
4086
|
}
|
|
4073
4087
|
let radiusButtons = "";
|
|
4074
4088
|
this.RADIUS_OPTIONS.forEach((r) => {
|
|
4075
|
-
radiusButtons += `<button class="tc-radius-btn${r === this.state.radius ? " is-active" : ""}" data-radius="${r}">${r}</button>`;
|
|
4089
|
+
radiusButtons += `<button class="tc-radius-btn${r === this.state.radius ? " is-active" : ""}" data-radius="${esc(r)}">${esc(r)}</button>`;
|
|
4076
4090
|
});
|
|
4077
4091
|
let fontOptions = "";
|
|
4078
4092
|
for (const [key, value] of Object.entries(this.FONT_OPTIONS)) {
|
|
4079
|
-
fontOptions += `<option value="${key}"${key === this.state.font ? " selected" : ""}>${value.name}</option>`;
|
|
4093
|
+
fontOptions += `<option value="${esc(key)}"${key === this.state.font ? " selected" : ""}>${esc(value.name)}</option>`;
|
|
4080
4094
|
}
|
|
4081
4095
|
const modeIcons = {
|
|
4082
4096
|
"system": "ph-desktop",
|
|
@@ -4134,6 +4148,13 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4134
4148
|
/**
|
|
4135
4149
|
* Bind event listeners
|
|
4136
4150
|
*/
|
|
4151
|
+
/**
|
|
4152
|
+
* Check whether the current primary color is one of the auto-defaults
|
|
4153
|
+
* (i.e. the user hasn't explicitly picked a non-default color).
|
|
4154
|
+
*/
|
|
4155
|
+
isUsingDefaultPrimary: function() {
|
|
4156
|
+
return this.state.primary === this.DEFAULTS.PRIMARY_LIGHT || this.state.primary === this.DEFAULTS.PRIMARY_DARK;
|
|
4157
|
+
},
|
|
4137
4158
|
bindEvents: function() {
|
|
4138
4159
|
if (this.elements.trigger) {
|
|
4139
4160
|
this.addListener(this.elements.trigger, "click", (e) => {
|
|
@@ -4143,6 +4164,20 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4143
4164
|
});
|
|
4144
4165
|
}
|
|
4145
4166
|
this.bindPanelEvents();
|
|
4167
|
+
if (window.matchMedia) {
|
|
4168
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
4169
|
+
const handler = () => {
|
|
4170
|
+
if (this.state.theme === "system" && this.isUsingDefaultPrimary()) {
|
|
4171
|
+
const newDefault = this.getDefaultPrimary("system");
|
|
4172
|
+
if (newDefault !== this.state.primary) {
|
|
4173
|
+
this.applyPrimary(newDefault);
|
|
4174
|
+
this.updateUI();
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
};
|
|
4178
|
+
mq.addEventListener("change", handler);
|
|
4179
|
+
this._cleanup.push(() => mq.removeEventListener("change", handler));
|
|
4180
|
+
}
|
|
4146
4181
|
this.addListener(document, "click", (e) => {
|
|
4147
4182
|
if (this.state.isOpen && this.elements.customizer && !this.elements.customizer.contains(e.target)) {
|
|
4148
4183
|
this.close();
|
|
@@ -4704,7 +4739,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4704
4739
|
if (typeof sanitizeHtml === "function") {
|
|
4705
4740
|
return sanitizeHtml(input);
|
|
4706
4741
|
}
|
|
4707
|
-
|
|
4742
|
+
const div = document.createElement("div");
|
|
4708
4743
|
div.textContent = input || "";
|
|
4709
4744
|
return div.innerHTML;
|
|
4710
4745
|
},
|
|
@@ -4943,7 +4978,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4943
4978
|
// js/components/doc-search.js
|
|
4944
4979
|
(function() {
|
|
4945
4980
|
"use strict";
|
|
4946
|
-
|
|
4981
|
+
const DEFAULTS = {
|
|
4947
4982
|
// Behavior
|
|
4948
4983
|
minQueryLength: 2,
|
|
4949
4984
|
maxResults: 10,
|
|
@@ -4990,8 +5025,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4990
5025
|
placeholder: "Search..."
|
|
4991
5026
|
};
|
|
4992
5027
|
function createSearch(options) {
|
|
4993
|
-
|
|
4994
|
-
|
|
5028
|
+
const config = Object.assign({}, DEFAULTS, options || {});
|
|
5029
|
+
const state = {
|
|
4995
5030
|
initialized: false,
|
|
4996
5031
|
index: [],
|
|
4997
5032
|
results: [],
|
|
@@ -5004,6 +5039,21 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5004
5039
|
debounceTimer: null,
|
|
5005
5040
|
boundHandlers: {}
|
|
5006
5041
|
};
|
|
5042
|
+
function safeInvokeCallback(name, fn, ...args) {
|
|
5043
|
+
try {
|
|
5044
|
+
fn(...args);
|
|
5045
|
+
} catch (error) {
|
|
5046
|
+
console.warn('[Vanduo Search] Callback error in "' + name + '":', error);
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
function setResultsHtml(html) {
|
|
5050
|
+
if (!state.resultsContainer) return;
|
|
5051
|
+
try {
|
|
5052
|
+
state.resultsContainer.innerHTML = html;
|
|
5053
|
+
} catch (error) {
|
|
5054
|
+
console.warn("[Vanduo Search] Failed to render results:", error);
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5007
5057
|
function init() {
|
|
5008
5058
|
if (state.initialized) {
|
|
5009
5059
|
return instance;
|
|
@@ -5045,20 +5095,20 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5045
5095
|
});
|
|
5046
5096
|
return;
|
|
5047
5097
|
}
|
|
5048
|
-
|
|
5049
|
-
|
|
5098
|
+
const sections = document.querySelectorAll(config.contentSelector);
|
|
5099
|
+
const categoryMap = buildCategoryMap();
|
|
5050
5100
|
sections.forEach(function(section) {
|
|
5051
|
-
|
|
5101
|
+
const id = section.id;
|
|
5052
5102
|
if (!id) return;
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5103
|
+
const titleEl = section.querySelector(config.titleSelector);
|
|
5104
|
+
const title = titleEl ? titleEl.textContent.replace(/v[\d.]+/g, "").trim() : id;
|
|
5105
|
+
const category = categoryMap[id] || "Documentation";
|
|
5106
|
+
const content = extractContent(section);
|
|
5107
|
+
const keywords = extractKeywords(section, title);
|
|
5108
|
+
const iconEl = titleEl ? titleEl.querySelector("i.ph") : null;
|
|
5109
|
+
let icon = "";
|
|
5060
5110
|
if (iconEl && iconEl.classList) {
|
|
5061
|
-
for (
|
|
5111
|
+
for (let ci = 0; ci < iconEl.classList.length; ci++) {
|
|
5062
5112
|
if (iconEl.classList[ci].indexOf("ph-") === 0) {
|
|
5063
5113
|
icon = iconEl.classList[ci];
|
|
5064
5114
|
break;
|
|
@@ -5078,16 +5128,16 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5078
5128
|
});
|
|
5079
5129
|
}
|
|
5080
5130
|
function buildCategoryMap() {
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5131
|
+
const map = {};
|
|
5132
|
+
let currentCategory = "Documentation";
|
|
5133
|
+
const navItems = document.querySelectorAll(config.navSelector + ", " + config.sectionSelector);
|
|
5084
5134
|
navItems.forEach(function(item) {
|
|
5085
5135
|
if (item.classList.contains("doc-nav-section")) {
|
|
5086
5136
|
currentCategory = item.textContent.trim();
|
|
5087
5137
|
} else {
|
|
5088
|
-
|
|
5138
|
+
const href = item.getAttribute("href");
|
|
5089
5139
|
if (href && href.startsWith("#")) {
|
|
5090
|
-
|
|
5140
|
+
const id = href.substring(1);
|
|
5091
5141
|
map[id] = currentCategory;
|
|
5092
5142
|
}
|
|
5093
5143
|
}
|
|
@@ -5095,35 +5145,35 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5095
5145
|
return map;
|
|
5096
5146
|
}
|
|
5097
5147
|
function extractContent(section) {
|
|
5098
|
-
|
|
5099
|
-
|
|
5148
|
+
const clone = section.cloneNode(true);
|
|
5149
|
+
const toRemove = clone.querySelectorAll(config.excludeFromContent);
|
|
5100
5150
|
toRemove.forEach(function(el) {
|
|
5101
5151
|
el.remove();
|
|
5102
5152
|
});
|
|
5103
|
-
|
|
5153
|
+
let text = clone.textContent || "";
|
|
5104
5154
|
text = text.replace(/\s+/g, " ").trim();
|
|
5105
5155
|
return text.substring(0, config.maxContentLength);
|
|
5106
5156
|
}
|
|
5107
5157
|
function extractKeywords(section, title) {
|
|
5108
|
-
|
|
5158
|
+
const keywords = [];
|
|
5109
5159
|
title.toLowerCase().split(/\s+/).forEach(function(word) {
|
|
5110
5160
|
if (word.length > 2) {
|
|
5111
5161
|
keywords.push(word);
|
|
5112
5162
|
}
|
|
5113
5163
|
});
|
|
5114
|
-
|
|
5164
|
+
const codeBlocks = section.querySelectorAll("code");
|
|
5115
5165
|
codeBlocks.forEach(function(code) {
|
|
5116
|
-
|
|
5117
|
-
|
|
5166
|
+
const text = code.textContent || "";
|
|
5167
|
+
const classMatches = text.match(/\.([\w-]+)/g);
|
|
5118
5168
|
if (classMatches) {
|
|
5119
5169
|
classMatches.forEach(function(match) {
|
|
5120
5170
|
keywords.push(match.substring(1).toLowerCase());
|
|
5121
5171
|
});
|
|
5122
5172
|
}
|
|
5123
5173
|
});
|
|
5124
|
-
|
|
5174
|
+
const dataAttrs = section.querySelectorAll("[data-tooltip], [data-modal]");
|
|
5125
5175
|
dataAttrs.forEach(function(el) {
|
|
5126
|
-
|
|
5176
|
+
const attrs = el.getAttributeNames().filter(function(name) {
|
|
5127
5177
|
return name.startsWith("data-");
|
|
5128
5178
|
});
|
|
5129
5179
|
attrs.forEach(function(attr) {
|
|
@@ -5133,7 +5183,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5133
5183
|
return Array.from(new Set(keywords));
|
|
5134
5184
|
}
|
|
5135
5185
|
function extractKeywordsFromText(text) {
|
|
5136
|
-
|
|
5186
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
5137
5187
|
return words.filter(function(word) {
|
|
5138
5188
|
return word.length > 2;
|
|
5139
5189
|
});
|
|
@@ -5166,9 +5216,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5166
5216
|
}
|
|
5167
5217
|
};
|
|
5168
5218
|
state.boundHandlers.handleResultClick = function(e) {
|
|
5169
|
-
|
|
5219
|
+
const result = e.target.closest(".vd-doc-search-result");
|
|
5170
5220
|
if (result) {
|
|
5171
|
-
|
|
5221
|
+
const index = parseInt(result.dataset.index, 10);
|
|
5172
5222
|
select(index);
|
|
5173
5223
|
}
|
|
5174
5224
|
};
|
|
@@ -5192,7 +5242,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5192
5242
|
}
|
|
5193
5243
|
}
|
|
5194
5244
|
function setupAria() {
|
|
5195
|
-
|
|
5245
|
+
const resultsId = state.resultsContainer.id || "search-results-" + Math.random().toString(36).substr(2, 9);
|
|
5196
5246
|
state.resultsContainer.id = resultsId;
|
|
5197
5247
|
state.input.setAttribute("role", "combobox");
|
|
5198
5248
|
state.input.setAttribute("aria-autocomplete", "list");
|
|
@@ -5202,7 +5252,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5202
5252
|
state.resultsContainer.setAttribute("aria-label", "Search results");
|
|
5203
5253
|
}
|
|
5204
5254
|
function handleInput(e) {
|
|
5205
|
-
|
|
5255
|
+
const query = e.target.value.trim();
|
|
5206
5256
|
if (state.debounceTimer) {
|
|
5207
5257
|
clearTimeout(state.debounceTimer);
|
|
5208
5258
|
}
|
|
@@ -5217,7 +5267,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5217
5267
|
render();
|
|
5218
5268
|
open();
|
|
5219
5269
|
if (typeof config.onSearch === "function") {
|
|
5220
|
-
config.onSearch
|
|
5270
|
+
safeInvokeCallback("onSearch", config.onSearch, query, state.results);
|
|
5221
5271
|
}
|
|
5222
5272
|
}, config.debounceMs);
|
|
5223
5273
|
}
|
|
@@ -5258,15 +5308,15 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5258
5308
|
}
|
|
5259
5309
|
}
|
|
5260
5310
|
function search(query) {
|
|
5261
|
-
|
|
5311
|
+
const terms = query.toLowerCase().split(/\s+/).filter(function(t) {
|
|
5262
5312
|
return t.length > 0;
|
|
5263
5313
|
});
|
|
5264
|
-
|
|
5314
|
+
const scored = [];
|
|
5265
5315
|
state.index.forEach(function(entry) {
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5316
|
+
let score = 0;
|
|
5317
|
+
const titleLower = entry.title.toLowerCase();
|
|
5318
|
+
const categoryLower = entry.category.toLowerCase();
|
|
5319
|
+
const contentLower = entry.content.toLowerCase();
|
|
5270
5320
|
terms.forEach(function(term) {
|
|
5271
5321
|
if (titleLower.includes(term)) {
|
|
5272
5322
|
score += 100;
|
|
@@ -5279,7 +5329,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5279
5329
|
if (categoryLower.includes(term)) {
|
|
5280
5330
|
score += 50;
|
|
5281
5331
|
}
|
|
5282
|
-
|
|
5332
|
+
const keywordMatch = entry.keywords.some(function(k) {
|
|
5283
5333
|
return k.includes(term);
|
|
5284
5334
|
});
|
|
5285
5335
|
if (keywordMatch) {
|
|
@@ -5309,19 +5359,19 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5309
5359
|
}
|
|
5310
5360
|
function render() {
|
|
5311
5361
|
if (state.results.length === 0) {
|
|
5312
|
-
|
|
5362
|
+
setResultsHtml(renderEmpty());
|
|
5313
5363
|
return;
|
|
5314
5364
|
}
|
|
5315
|
-
|
|
5365
|
+
let html = '<ul class="vd-doc-search-results-list" role="listbox">';
|
|
5316
5366
|
state.results.forEach(function(result, index) {
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5367
|
+
const isActive = index === state.activeIndex;
|
|
5368
|
+
const icon = result.icon || getCategoryIcon(result.categorySlug);
|
|
5369
|
+
const excerpt = getExcerpt(result.content, state.query);
|
|
5320
5370
|
html += '<li class="vd-doc-search-result' + (isActive ? " is-active" : "") + '" role="option" id="vd-doc-search-result-' + index + '" data-index="' + index + '" data-category="' + escapeHtml2(result.categorySlug) + '" aria-selected="' + isActive + '"><div class="vd-doc-search-result-icon"><i class="ph ' + escapeHtml2(icon) + '"></i></div><div class="vd-doc-search-result-content"><div class="vd-doc-search-result-title">' + highlight(result.title, state.query) + '</div><div class="vd-doc-search-result-category">' + escapeHtml2(result.category) + '</div><div class="vd-doc-search-result-excerpt">' + highlight(excerpt, state.query) + "</div></div></li>";
|
|
5321
5371
|
});
|
|
5322
5372
|
html += "</ul>";
|
|
5323
5373
|
html += renderFooter();
|
|
5324
|
-
|
|
5374
|
+
setResultsHtml(html);
|
|
5325
5375
|
}
|
|
5326
5376
|
function renderEmpty() {
|
|
5327
5377
|
return '<div class="vd-doc-search-empty"><div class="vd-doc-search-empty-icon"><i class="ph ph-magnifying-glass"></i></div><div class="vd-doc-search-empty-title">' + escapeHtml2(config.emptyTitle) + '</div><div class="vd-doc-search-empty-text">' + escapeHtml2(config.emptyText) + "</div></div>";
|
|
@@ -5333,12 +5383,12 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5333
5383
|
return config.categoryIcons[categorySlug] || config.categoryIcons["default"] || "ph-file-text";
|
|
5334
5384
|
}
|
|
5335
5385
|
function getExcerpt(content, query) {
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
for (
|
|
5341
|
-
|
|
5386
|
+
const terms = query.toLowerCase().split(/\s+/);
|
|
5387
|
+
const contentLower = content.toLowerCase();
|
|
5388
|
+
const excerptLength = 100;
|
|
5389
|
+
let matchPos = -1;
|
|
5390
|
+
for (let i = 0; i < terms.length; i++) {
|
|
5391
|
+
const pos = contentLower.indexOf(terms[i]);
|
|
5342
5392
|
if (pos !== -1 && (matchPos === -1 || pos < matchPos)) {
|
|
5343
5393
|
matchPos = pos;
|
|
5344
5394
|
}
|
|
@@ -5346,9 +5396,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5346
5396
|
if (matchPos === -1) {
|
|
5347
5397
|
return content.substring(0, excerptLength) + "...";
|
|
5348
5398
|
}
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5399
|
+
const start = Math.max(0, matchPos - 30);
|
|
5400
|
+
const end = Math.min(content.length, matchPos + excerptLength);
|
|
5401
|
+
let excerpt = content.substring(start, end);
|
|
5352
5402
|
if (start > 0) {
|
|
5353
5403
|
excerpt = "..." + excerpt;
|
|
5354
5404
|
}
|
|
@@ -5359,24 +5409,24 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5359
5409
|
}
|
|
5360
5410
|
function highlight(text, query) {
|
|
5361
5411
|
if (!query) return escapeHtml2(text);
|
|
5362
|
-
|
|
5412
|
+
const terms = query.toLowerCase().split(/\s+/).filter(function(t) {
|
|
5363
5413
|
return t.length > 0;
|
|
5364
5414
|
});
|
|
5365
|
-
|
|
5415
|
+
let escaped = escapeHtml2(text);
|
|
5366
5416
|
terms.forEach(function(term) {
|
|
5367
5417
|
if (term.length > 50) return;
|
|
5368
|
-
|
|
5418
|
+
const regex = new RegExp("(" + term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + ")", "gi");
|
|
5369
5419
|
escaped = escaped.replace(regex, "<" + config.highlightTag + ">$1</" + config.highlightTag + ">");
|
|
5370
5420
|
});
|
|
5371
5421
|
return escaped;
|
|
5372
5422
|
}
|
|
5373
5423
|
function escapeHtml2(text) {
|
|
5374
|
-
|
|
5424
|
+
const div = document.createElement("div");
|
|
5375
5425
|
div.textContent = text;
|
|
5376
5426
|
return div.innerHTML;
|
|
5377
5427
|
}
|
|
5378
5428
|
function navigate(direction) {
|
|
5379
|
-
|
|
5429
|
+
let newIndex = state.activeIndex + direction;
|
|
5380
5430
|
if (newIndex < 0) {
|
|
5381
5431
|
newIndex = state.results.length - 1;
|
|
5382
5432
|
} else if (newIndex >= state.results.length) {
|
|
@@ -5385,13 +5435,13 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5385
5435
|
setActiveIndex(newIndex);
|
|
5386
5436
|
}
|
|
5387
5437
|
function setActiveIndex(index) {
|
|
5388
|
-
|
|
5438
|
+
const prevActive = state.resultsContainer.querySelector(".vd-doc-search-result.is-active");
|
|
5389
5439
|
if (prevActive) {
|
|
5390
5440
|
prevActive.classList.remove("is-active");
|
|
5391
5441
|
prevActive.setAttribute("aria-selected", "false");
|
|
5392
5442
|
}
|
|
5393
5443
|
state.activeIndex = index;
|
|
5394
|
-
|
|
5444
|
+
const newActive = state.resultsContainer.querySelector('[data-index="' + index + '"]');
|
|
5395
5445
|
if (newActive) {
|
|
5396
5446
|
newActive.classList.add("is-active");
|
|
5397
5447
|
newActive.setAttribute("aria-selected", "true");
|
|
@@ -5400,16 +5450,16 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5400
5450
|
}
|
|
5401
5451
|
}
|
|
5402
5452
|
function select(index) {
|
|
5403
|
-
|
|
5453
|
+
const result = state.results[index];
|
|
5404
5454
|
if (!result) return;
|
|
5405
5455
|
close();
|
|
5406
5456
|
state.input.value = "";
|
|
5407
5457
|
state.query = "";
|
|
5408
5458
|
if (typeof config.onSelect === "function") {
|
|
5409
|
-
config.onSelect
|
|
5459
|
+
safeInvokeCallback("onSelect", config.onSelect, result);
|
|
5410
5460
|
return;
|
|
5411
5461
|
}
|
|
5412
|
-
|
|
5462
|
+
const section = document.querySelector(result.url);
|
|
5413
5463
|
if (section) {
|
|
5414
5464
|
section.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
5415
5465
|
window.history.pushState(null, "", result.url);
|
|
@@ -5417,7 +5467,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5417
5467
|
}
|
|
5418
5468
|
}
|
|
5419
5469
|
function updateSidebarActive(sectionId) {
|
|
5420
|
-
|
|
5470
|
+
const navLinks = document.querySelectorAll(config.navSelector);
|
|
5421
5471
|
navLinks.forEach(function(link) {
|
|
5422
5472
|
link.classList.remove("active");
|
|
5423
5473
|
if (link.getAttribute("href") === "#" + sectionId) {
|
|
@@ -5431,7 +5481,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5431
5481
|
state.resultsContainer.classList.add("is-open");
|
|
5432
5482
|
state.input.setAttribute("aria-expanded", "true");
|
|
5433
5483
|
if (typeof config.onOpen === "function") {
|
|
5434
|
-
config.onOpen
|
|
5484
|
+
safeInvokeCallback("onOpen", config.onOpen);
|
|
5435
5485
|
}
|
|
5436
5486
|
}
|
|
5437
5487
|
function close() {
|
|
@@ -5442,7 +5492,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5442
5492
|
state.input.setAttribute("aria-expanded", "false");
|
|
5443
5493
|
state.input.removeAttribute("aria-activedescendant");
|
|
5444
5494
|
if (typeof config.onClose === "function") {
|
|
5445
|
-
config.onClose
|
|
5495
|
+
safeInvokeCallback("onClose", config.onClose);
|
|
5446
5496
|
}
|
|
5447
5497
|
}
|
|
5448
5498
|
function destroy() {
|
|
@@ -5456,7 +5506,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5456
5506
|
clearTimeout(state.debounceTimer);
|
|
5457
5507
|
}
|
|
5458
5508
|
if (state.resultsContainer) {
|
|
5459
|
-
|
|
5509
|
+
setResultsHtml("");
|
|
5460
5510
|
}
|
|
5461
5511
|
}
|
|
5462
5512
|
function rebuild() {
|
|
@@ -5471,7 +5521,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5471
5521
|
function getIndex() {
|
|
5472
5522
|
return state.index.slice();
|
|
5473
5523
|
}
|
|
5474
|
-
|
|
5524
|
+
const instance = {
|
|
5475
5525
|
init,
|
|
5476
5526
|
destroy,
|
|
5477
5527
|
rebuild,
|
|
@@ -5484,12 +5534,12 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5484
5534
|
};
|
|
5485
5535
|
return instance;
|
|
5486
5536
|
}
|
|
5487
|
-
|
|
5537
|
+
const Search = {
|
|
5488
5538
|
// Factory method — creates and auto-initializes a new independent instance.
|
|
5489
5539
|
// Always returns the instance so callers retain a reference even if the
|
|
5490
5540
|
// DOM container is not yet available (they can retry init() later).
|
|
5491
5541
|
create: function(options) {
|
|
5492
|
-
|
|
5542
|
+
const instance = createSearch(options);
|
|
5493
5543
|
if (instance) {
|
|
5494
5544
|
instance.init();
|
|
5495
5545
|
}
|
|
@@ -5563,6 +5613,565 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5563
5613
|
window.VanduoDocSearch = Search;
|
|
5564
5614
|
})();
|
|
5565
5615
|
|
|
5616
|
+
// js/components/draggable.js
|
|
5617
|
+
(function() {
|
|
5618
|
+
"use strict";
|
|
5619
|
+
const Draggable = {
|
|
5620
|
+
// Store initialized draggables and their cleanup functions
|
|
5621
|
+
instances: /* @__PURE__ */ new Map(),
|
|
5622
|
+
// Store current drag state
|
|
5623
|
+
currentDrag: null,
|
|
5624
|
+
// Store touch state
|
|
5625
|
+
touchState: null,
|
|
5626
|
+
// Feedback element
|
|
5627
|
+
feedbackElement: null,
|
|
5628
|
+
/**
|
|
5629
|
+
* Initialize draggable components
|
|
5630
|
+
*/
|
|
5631
|
+
init: function() {
|
|
5632
|
+
const draggables = document.querySelectorAll(".vd-draggable, [data-draggable]");
|
|
5633
|
+
draggables.forEach((element) => {
|
|
5634
|
+
if (this.instances.has(element)) {
|
|
5635
|
+
return;
|
|
5636
|
+
}
|
|
5637
|
+
this.initDraggable(element);
|
|
5638
|
+
});
|
|
5639
|
+
const containers = document.querySelectorAll(".vd-draggable-container, .vd-draggable-container-vertical");
|
|
5640
|
+
containers.forEach((container) => {
|
|
5641
|
+
if (!this.instances.has(container)) {
|
|
5642
|
+
this.initContainer(container);
|
|
5643
|
+
}
|
|
5644
|
+
});
|
|
5645
|
+
const dropZones = document.querySelectorAll(".vd-drop-zone");
|
|
5646
|
+
dropZones.forEach((zone) => {
|
|
5647
|
+
if (!this.instances.has(zone)) {
|
|
5648
|
+
this.initDropZone(zone);
|
|
5649
|
+
}
|
|
5650
|
+
});
|
|
5651
|
+
this.createFeedbackElement();
|
|
5652
|
+
},
|
|
5653
|
+
/**
|
|
5654
|
+
* Initialize a single draggable element
|
|
5655
|
+
* @param {HTMLElement} element - Draggable element
|
|
5656
|
+
*/
|
|
5657
|
+
initDraggable: function(element) {
|
|
5658
|
+
const cleanupFunctions = [];
|
|
5659
|
+
if (!element.hasAttribute("draggable")) {
|
|
5660
|
+
element.setAttribute("draggable", "true");
|
|
5661
|
+
}
|
|
5662
|
+
if (!element.hasAttribute("tabindex")) {
|
|
5663
|
+
element.setAttribute("tabindex", "0");
|
|
5664
|
+
}
|
|
5665
|
+
element.setAttribute("role", "option");
|
|
5666
|
+
element.setAttribute("aria-roledescription", "draggable item");
|
|
5667
|
+
element.setAttribute("aria-grabbed", "false");
|
|
5668
|
+
const dragStartHandler = (e) => {
|
|
5669
|
+
this.handleDragStart(e, element);
|
|
5670
|
+
};
|
|
5671
|
+
element.addEventListener("dragstart", dragStartHandler);
|
|
5672
|
+
cleanupFunctions.push(() => element.removeEventListener("dragstart", dragStartHandler));
|
|
5673
|
+
const dragHandler = (e) => {
|
|
5674
|
+
this.handleDrag(e, element);
|
|
5675
|
+
};
|
|
5676
|
+
element.addEventListener("drag", dragHandler);
|
|
5677
|
+
cleanupFunctions.push(() => element.removeEventListener("drag", dragHandler));
|
|
5678
|
+
const dragEndHandler = (e) => {
|
|
5679
|
+
this.handleDragEnd(e, element);
|
|
5680
|
+
};
|
|
5681
|
+
element.addEventListener("dragend", dragEndHandler);
|
|
5682
|
+
cleanupFunctions.push(() => element.removeEventListener("dragend", dragEndHandler));
|
|
5683
|
+
const touchStartHandler = (e) => {
|
|
5684
|
+
this.handleTouchStart(e, element);
|
|
5685
|
+
};
|
|
5686
|
+
element.addEventListener("touchstart", touchStartHandler);
|
|
5687
|
+
cleanupFunctions.push(() => element.removeEventListener("touchstart", touchStartHandler));
|
|
5688
|
+
const touchMoveHandler = (e) => {
|
|
5689
|
+
this.handleTouchMove(e, element);
|
|
5690
|
+
};
|
|
5691
|
+
element.addEventListener("touchmove", touchMoveHandler);
|
|
5692
|
+
cleanupFunctions.push(() => element.removeEventListener("touchmove", touchMoveHandler));
|
|
5693
|
+
const touchEndHandler = (e) => {
|
|
5694
|
+
this.handleTouchEnd(e, element);
|
|
5695
|
+
};
|
|
5696
|
+
element.addEventListener("touchend", touchEndHandler);
|
|
5697
|
+
cleanupFunctions.push(() => element.removeEventListener("touchend", touchEndHandler));
|
|
5698
|
+
const touchCancelHandler = (e) => {
|
|
5699
|
+
this.handleTouchEnd(e, element);
|
|
5700
|
+
};
|
|
5701
|
+
element.addEventListener("touchcancel", touchCancelHandler);
|
|
5702
|
+
cleanupFunctions.push(() => element.removeEventListener("touchcancel", touchCancelHandler));
|
|
5703
|
+
const keydownHandler = (e) => {
|
|
5704
|
+
this.handleKeydown(e, element);
|
|
5705
|
+
};
|
|
5706
|
+
element.addEventListener("keydown", keydownHandler);
|
|
5707
|
+
cleanupFunctions.push(() => element.removeEventListener("keydown", keydownHandler));
|
|
5708
|
+
this.instances.set(element, { cleanup: cleanupFunctions });
|
|
5709
|
+
},
|
|
5710
|
+
/**
|
|
5711
|
+
* Initialize a draggable container
|
|
5712
|
+
* @param {HTMLElement} container - Draggable container
|
|
5713
|
+
*/
|
|
5714
|
+
initContainer: function(container) {
|
|
5715
|
+
container.setAttribute("role", "listbox");
|
|
5716
|
+
container.setAttribute("aria-label", container.getAttribute("aria-label") || "Draggable items");
|
|
5717
|
+
const items = container.querySelectorAll(".vd-draggable-item");
|
|
5718
|
+
items.forEach((item) => {
|
|
5719
|
+
if (!this.instances.has(item)) {
|
|
5720
|
+
this.initDraggable(item);
|
|
5721
|
+
}
|
|
5722
|
+
});
|
|
5723
|
+
const cleanupFunctions = [];
|
|
5724
|
+
const dragEnterHandler = (e) => {
|
|
5725
|
+
e.preventDefault();
|
|
5726
|
+
e.dataTransfer.dropEffect = "move";
|
|
5727
|
+
};
|
|
5728
|
+
const dragOverHandler = (e) => {
|
|
5729
|
+
e.preventDefault();
|
|
5730
|
+
e.dataTransfer.dropEffect = "move";
|
|
5731
|
+
if (!this.currentDrag) return;
|
|
5732
|
+
const draggingEl = this.currentDrag.element;
|
|
5733
|
+
if (!container.contains(draggingEl)) return;
|
|
5734
|
+
if (e.clientX === 0 && e.clientY === 0) return;
|
|
5735
|
+
this.handleReorder(container, draggingEl, e.clientX, e.clientY);
|
|
5736
|
+
};
|
|
5737
|
+
const dropHandler = (e) => {
|
|
5738
|
+
e.preventDefault();
|
|
5739
|
+
};
|
|
5740
|
+
container.addEventListener("dragenter", dragEnterHandler);
|
|
5741
|
+
container.addEventListener("dragover", dragOverHandler);
|
|
5742
|
+
container.addEventListener("drop", dropHandler);
|
|
5743
|
+
cleanupFunctions.push(() => {
|
|
5744
|
+
container.removeEventListener("dragenter", dragEnterHandler);
|
|
5745
|
+
container.removeEventListener("dragover", dragOverHandler);
|
|
5746
|
+
container.removeEventListener("drop", dropHandler);
|
|
5747
|
+
});
|
|
5748
|
+
this.instances.set(container, { cleanup: cleanupFunctions });
|
|
5749
|
+
},
|
|
5750
|
+
/**
|
|
5751
|
+
* Initialize a drop zone
|
|
5752
|
+
* @param {HTMLElement} zone - Drop zone element
|
|
5753
|
+
*/
|
|
5754
|
+
initDropZone: function(zone) {
|
|
5755
|
+
const cleanupFunctions = [];
|
|
5756
|
+
zone.setAttribute("role", "region");
|
|
5757
|
+
zone.setAttribute("aria-dropeffect", "move");
|
|
5758
|
+
if (!zone.hasAttribute("aria-label")) {
|
|
5759
|
+
zone.setAttribute("aria-label", "Drop zone");
|
|
5760
|
+
}
|
|
5761
|
+
const dragOverHandler = (e) => {
|
|
5762
|
+
e.preventDefault();
|
|
5763
|
+
this.handleDragOver(e, zone);
|
|
5764
|
+
};
|
|
5765
|
+
zone.addEventListener("dragover", dragOverHandler);
|
|
5766
|
+
cleanupFunctions.push(() => zone.removeEventListener("dragover", dragOverHandler));
|
|
5767
|
+
const dragEnterHandler = (e) => {
|
|
5768
|
+
e.preventDefault();
|
|
5769
|
+
this.handleDragEnter(e, zone);
|
|
5770
|
+
};
|
|
5771
|
+
zone.addEventListener("dragenter", dragEnterHandler);
|
|
5772
|
+
cleanupFunctions.push(() => zone.removeEventListener("dragenter", dragEnterHandler));
|
|
5773
|
+
const dragLeaveHandler = (e) => {
|
|
5774
|
+
this.handleDragLeave(e, zone);
|
|
5775
|
+
};
|
|
5776
|
+
zone.addEventListener("dragleave", dragLeaveHandler);
|
|
5777
|
+
cleanupFunctions.push(() => zone.removeEventListener("dragleave", dragLeaveHandler));
|
|
5778
|
+
const dropHandler = (e) => {
|
|
5779
|
+
e.preventDefault();
|
|
5780
|
+
this.handleDrop(e, zone);
|
|
5781
|
+
};
|
|
5782
|
+
zone.addEventListener("drop", dropHandler);
|
|
5783
|
+
cleanupFunctions.push(() => zone.removeEventListener("drop", dropHandler));
|
|
5784
|
+
this.instances.set(zone, { cleanup: cleanupFunctions });
|
|
5785
|
+
},
|
|
5786
|
+
/**
|
|
5787
|
+
* Create feedback element for drag operations
|
|
5788
|
+
*/
|
|
5789
|
+
createFeedbackElement: function() {
|
|
5790
|
+
if (!this.feedbackElement) {
|
|
5791
|
+
const existing = document.querySelector(".vd-drag-feedback");
|
|
5792
|
+
if (existing) {
|
|
5793
|
+
this.feedbackElement = existing;
|
|
5794
|
+
return;
|
|
5795
|
+
}
|
|
5796
|
+
this.feedbackElement = document.createElement("div");
|
|
5797
|
+
this.feedbackElement.className = "vd-drag-feedback hidden";
|
|
5798
|
+
this.feedbackElement.setAttribute("role", "presentation");
|
|
5799
|
+
document.body.appendChild(this.feedbackElement);
|
|
5800
|
+
}
|
|
5801
|
+
},
|
|
5802
|
+
/**
|
|
5803
|
+
* Handle drag start event
|
|
5804
|
+
* @param {DragEvent} e - Drag event
|
|
5805
|
+
* @param {HTMLElement} element - Draggable element
|
|
5806
|
+
*/
|
|
5807
|
+
handleDragStart: function(e, element) {
|
|
5808
|
+
element.classList.add("is-dragging");
|
|
5809
|
+
element.setAttribute("aria-grabbed", "true");
|
|
5810
|
+
this.currentDrag = {
|
|
5811
|
+
element,
|
|
5812
|
+
initialPosition: { x: e.clientX, y: e.clientY },
|
|
5813
|
+
initialBounds: element.getBoundingClientRect(),
|
|
5814
|
+
data: this.getData(element)
|
|
5815
|
+
};
|
|
5816
|
+
e.dataTransfer.effectAllowed = "move";
|
|
5817
|
+
e.dataTransfer.setData("text/plain", this.currentDrag.data);
|
|
5818
|
+
element.dispatchEvent(new CustomEvent("draggable:start", {
|
|
5819
|
+
bubbles: true,
|
|
5820
|
+
detail: {
|
|
5821
|
+
element,
|
|
5822
|
+
data: this.currentDrag.data,
|
|
5823
|
+
position: { x: e.clientX, y: e.clientY }
|
|
5824
|
+
}
|
|
5825
|
+
}));
|
|
5826
|
+
},
|
|
5827
|
+
/**
|
|
5828
|
+
* Handle drag event
|
|
5829
|
+
* @param {DragEvent} e - Drag event
|
|
5830
|
+
* @param {HTMLElement} element - Draggable element
|
|
5831
|
+
*/
|
|
5832
|
+
handleDrag: function(e, element) {
|
|
5833
|
+
if (!this.currentDrag) return;
|
|
5834
|
+
element.dispatchEvent(new CustomEvent("draggable:drag", {
|
|
5835
|
+
bubbles: true,
|
|
5836
|
+
detail: {
|
|
5837
|
+
element,
|
|
5838
|
+
data: this.currentDrag.data,
|
|
5839
|
+
position: { x: e.clientX, y: e.clientY },
|
|
5840
|
+
delta: {
|
|
5841
|
+
x: e.clientX - this.currentDrag.initialPosition.x,
|
|
5842
|
+
y: e.clientY - this.currentDrag.initialPosition.y
|
|
5843
|
+
}
|
|
5844
|
+
}
|
|
5845
|
+
}));
|
|
5846
|
+
},
|
|
5847
|
+
/**
|
|
5848
|
+
* Handle drag end event
|
|
5849
|
+
* @param {DragEvent} e - Drag event
|
|
5850
|
+
* @param {HTMLElement} element - Draggable element
|
|
5851
|
+
*/
|
|
5852
|
+
handleDragEnd: function(e, element) {
|
|
5853
|
+
element.classList.remove("is-dragging");
|
|
5854
|
+
element.classList.add("is-dropped");
|
|
5855
|
+
setTimeout(() => element.classList.remove("is-dropped"), 300);
|
|
5856
|
+
element.setAttribute("aria-grabbed", "false");
|
|
5857
|
+
if (this.feedbackElement) {
|
|
5858
|
+
this.feedbackElement.classList.add("hidden");
|
|
5859
|
+
}
|
|
5860
|
+
const data = this.currentDrag?.data || this.getData(element);
|
|
5861
|
+
const initialPos = this.currentDrag?.initialPosition || { x: 0, y: 0 };
|
|
5862
|
+
element.dispatchEvent(new CustomEvent("draggable:end", {
|
|
5863
|
+
bubbles: true,
|
|
5864
|
+
detail: {
|
|
5865
|
+
element,
|
|
5866
|
+
data,
|
|
5867
|
+
position: { x: e.clientX, y: e.clientY },
|
|
5868
|
+
delta: {
|
|
5869
|
+
x: e.clientX - initialPos.x,
|
|
5870
|
+
y: e.clientY - initialPos.y
|
|
5871
|
+
}
|
|
5872
|
+
}
|
|
5873
|
+
}));
|
|
5874
|
+
this.currentDrag = null;
|
|
5875
|
+
},
|
|
5876
|
+
/**
|
|
5877
|
+
* Handle touch start event (for mobile)
|
|
5878
|
+
* @param {TouchEvent} e - Touch event
|
|
5879
|
+
* @param {HTMLElement} element - Draggable element
|
|
5880
|
+
*/
|
|
5881
|
+
handleTouchStart: function(e, element) {
|
|
5882
|
+
const touch = e.touches[0];
|
|
5883
|
+
this.touchState = {
|
|
5884
|
+
element,
|
|
5885
|
+
startX: touch.clientX,
|
|
5886
|
+
startY: touch.clientY,
|
|
5887
|
+
startTime: Date.now(),
|
|
5888
|
+
isDragging: false
|
|
5889
|
+
};
|
|
5890
|
+
},
|
|
5891
|
+
/**
|
|
5892
|
+
* Handle touch move event (for mobile)
|
|
5893
|
+
* @param {TouchEvent} e - Touch event
|
|
5894
|
+
* @param {HTMLElement} element - Draggable element
|
|
5895
|
+
*/
|
|
5896
|
+
handleTouchMove: function(e, element) {
|
|
5897
|
+
if (!this.touchState) return;
|
|
5898
|
+
const touch = e.touches[0];
|
|
5899
|
+
const deltaX = touch.clientX - this.touchState.startX;
|
|
5900
|
+
const deltaY = touch.clientY - this.touchState.startY;
|
|
5901
|
+
if (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10) {
|
|
5902
|
+
e.preventDefault();
|
|
5903
|
+
if (!this.touchState.isDragging) {
|
|
5904
|
+
this.touchState.isDragging = true;
|
|
5905
|
+
element.classList.add("is-dragging");
|
|
5906
|
+
element.setAttribute("aria-grabbed", "true");
|
|
5907
|
+
this.currentDrag = {
|
|
5908
|
+
element,
|
|
5909
|
+
initialPosition: { x: this.touchState.startX, y: this.touchState.startY },
|
|
5910
|
+
initialBounds: element.getBoundingClientRect(),
|
|
5911
|
+
data: this.getData(element)
|
|
5912
|
+
};
|
|
5913
|
+
element.dispatchEvent(new CustomEvent("draggable:start", {
|
|
5914
|
+
bubbles: true,
|
|
5915
|
+
detail: {
|
|
5916
|
+
element,
|
|
5917
|
+
data: this.currentDrag.data,
|
|
5918
|
+
position: { x: touch.clientX, y: touch.clientY }
|
|
5919
|
+
}
|
|
5920
|
+
}));
|
|
5921
|
+
}
|
|
5922
|
+
this.updateFeedback(touch.clientX, touch.clientY);
|
|
5923
|
+
if (this.currentDrag) {
|
|
5924
|
+
element.dispatchEvent(new CustomEvent("draggable:drag", {
|
|
5925
|
+
bubbles: true,
|
|
5926
|
+
detail: {
|
|
5927
|
+
element,
|
|
5928
|
+
data: this.currentDrag.data,
|
|
5929
|
+
position: { x: touch.clientX, y: touch.clientY },
|
|
5930
|
+
delta: { x: deltaX, y: deltaY }
|
|
5931
|
+
}
|
|
5932
|
+
}));
|
|
5933
|
+
const container = element.closest(".vd-draggable-container");
|
|
5934
|
+
if (container && container.contains(element)) {
|
|
5935
|
+
this.handleReorder(container, element, touch.clientX, touch.clientY);
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
},
|
|
5940
|
+
/**
|
|
5941
|
+
* Handle touch end event (for mobile)
|
|
5942
|
+
* @param {TouchEvent} e - Touch event
|
|
5943
|
+
* @param {HTMLElement} element - Draggable element
|
|
5944
|
+
*/
|
|
5945
|
+
handleTouchEnd: function(e, element) {
|
|
5946
|
+
if (this.touchState && this.touchState.isDragging) {
|
|
5947
|
+
e.preventDefault();
|
|
5948
|
+
element.classList.remove("is-dragging");
|
|
5949
|
+
element.classList.add("is-dropped");
|
|
5950
|
+
element.setAttribute("aria-grabbed", "false");
|
|
5951
|
+
setTimeout(() => element.classList.remove("is-dropped"), 300);
|
|
5952
|
+
if (this.feedbackElement) {
|
|
5953
|
+
this.feedbackElement.classList.add("hidden");
|
|
5954
|
+
}
|
|
5955
|
+
const endTouch = e.changedTouches[0];
|
|
5956
|
+
const data = this.currentDrag?.data || this.getData(element);
|
|
5957
|
+
const startX = this.touchState?.startX || 0;
|
|
5958
|
+
const startY = this.touchState?.startY || 0;
|
|
5959
|
+
element.dispatchEvent(new CustomEvent("draggable:end", {
|
|
5960
|
+
bubbles: true,
|
|
5961
|
+
detail: {
|
|
5962
|
+
element,
|
|
5963
|
+
data,
|
|
5964
|
+
position: { x: endTouch.clientX, y: endTouch.clientY },
|
|
5965
|
+
delta: {
|
|
5966
|
+
x: endTouch.clientX - startX,
|
|
5967
|
+
y: endTouch.clientY - startY
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
}));
|
|
5971
|
+
}
|
|
5972
|
+
this.touchState = null;
|
|
5973
|
+
this.currentDrag = null;
|
|
5974
|
+
},
|
|
5975
|
+
/**
|
|
5976
|
+
* Handle drag over event
|
|
5977
|
+
* @param {DragEvent} e - Drag event
|
|
5978
|
+
* @param {HTMLElement} _zone - Drop zone element
|
|
5979
|
+
*/
|
|
5980
|
+
handleDragOver: function(e, _zone) {
|
|
5981
|
+
e.preventDefault();
|
|
5982
|
+
e.dataTransfer.dropEffect = "move";
|
|
5983
|
+
},
|
|
5984
|
+
/**
|
|
5985
|
+
* Handle drag enter event
|
|
5986
|
+
* @param {DragEvent} e - Drag event
|
|
5987
|
+
* @param {HTMLElement} zone - Drop zone element
|
|
5988
|
+
*/
|
|
5989
|
+
handleDragEnter: function(e, zone) {
|
|
5990
|
+
e.preventDefault();
|
|
5991
|
+
zone.classList.add("is-drag-over");
|
|
5992
|
+
},
|
|
5993
|
+
/**
|
|
5994
|
+
* Handle drag leave event
|
|
5995
|
+
* @param {DragEvent} e - Drag event
|
|
5996
|
+
* @param {HTMLElement} zone - Drop zone element
|
|
5997
|
+
*/
|
|
5998
|
+
handleDragLeave: function(e, zone) {
|
|
5999
|
+
zone.classList.remove("is-drag-over");
|
|
6000
|
+
},
|
|
6001
|
+
/**
|
|
6002
|
+
* Handle drop event
|
|
6003
|
+
* @param {DragEvent} e - Drag event
|
|
6004
|
+
* @param {HTMLElement} zone - Drop zone element
|
|
6005
|
+
*/
|
|
6006
|
+
handleDrop: function(e, zone) {
|
|
6007
|
+
e.preventDefault();
|
|
6008
|
+
zone.classList.remove("is-drag-over");
|
|
6009
|
+
zone.dispatchEvent(new CustomEvent("draggable:drop", {
|
|
6010
|
+
bubbles: true,
|
|
6011
|
+
detail: {
|
|
6012
|
+
zone,
|
|
6013
|
+
element: this.currentDrag?.element,
|
|
6014
|
+
data: this.currentDrag?.data,
|
|
6015
|
+
position: { x: e.clientX, y: e.clientY }
|
|
6016
|
+
}
|
|
6017
|
+
}));
|
|
6018
|
+
},
|
|
6019
|
+
/**
|
|
6020
|
+
* Reorder elements in container based on cursor position
|
|
6021
|
+
* @param {HTMLElement} container
|
|
6022
|
+
* @param {HTMLElement} element
|
|
6023
|
+
* @param {number} clientX
|
|
6024
|
+
* @param {number} clientY
|
|
6025
|
+
*/
|
|
6026
|
+
handleReorder: function(container, element, clientX, clientY) {
|
|
6027
|
+
const isVertical = container.classList.contains("vd-draggable-container-vertical");
|
|
6028
|
+
const siblings = [...container.querySelectorAll(".vd-draggable-item:not(.is-dragging), .vd-draggable:not(.is-dragging)")];
|
|
6029
|
+
const nextSibling = siblings.reduce((closest, child) => {
|
|
6030
|
+
const box = child.getBoundingClientRect();
|
|
6031
|
+
const offset = isVertical ? clientY - box.top - box.height / 2 : clientX - box.left - box.width / 2;
|
|
6032
|
+
if (offset < 0 && offset > closest.offset) {
|
|
6033
|
+
return { offset, element: child };
|
|
6034
|
+
} else {
|
|
6035
|
+
return closest;
|
|
6036
|
+
}
|
|
6037
|
+
}, { offset: Number.NEGATIVE_INFINITY }).element;
|
|
6038
|
+
if (nextSibling == null) {
|
|
6039
|
+
container.appendChild(element);
|
|
6040
|
+
} else {
|
|
6041
|
+
container.insertBefore(element, nextSibling);
|
|
6042
|
+
}
|
|
6043
|
+
},
|
|
6044
|
+
/**
|
|
6045
|
+
* Handle keyboard events
|
|
6046
|
+
* @param {KeyboardEvent} e - Keyboard event
|
|
6047
|
+
* @param {HTMLElement} element - Draggable element
|
|
6048
|
+
*/
|
|
6049
|
+
handleKeydown: function(e, element) {
|
|
6050
|
+
switch (e.key) {
|
|
6051
|
+
case "Enter":
|
|
6052
|
+
case " ":
|
|
6053
|
+
e.preventDefault();
|
|
6054
|
+
element.click();
|
|
6055
|
+
break;
|
|
6056
|
+
case "Escape":
|
|
6057
|
+
if (element.classList.contains("is-dragging")) {
|
|
6058
|
+
element.classList.remove("is-dragging");
|
|
6059
|
+
element.setAttribute("aria-grabbed", "false");
|
|
6060
|
+
if (this.feedbackElement) {
|
|
6061
|
+
this.feedbackElement.classList.add("hidden");
|
|
6062
|
+
}
|
|
6063
|
+
this.currentDrag = null;
|
|
6064
|
+
}
|
|
6065
|
+
break;
|
|
6066
|
+
case "ArrowUp":
|
|
6067
|
+
case "ArrowLeft": {
|
|
6068
|
+
e.preventDefault();
|
|
6069
|
+
const prev = element.previousElementSibling;
|
|
6070
|
+
if (prev && (prev.classList.contains("vd-draggable") || prev.classList.contains("vd-draggable-item"))) {
|
|
6071
|
+
element.parentNode.insertBefore(element, prev);
|
|
6072
|
+
element.focus();
|
|
6073
|
+
element.dispatchEvent(new CustomEvent("draggable:reorder", {
|
|
6074
|
+
bubbles: true,
|
|
6075
|
+
detail: { element, direction: "up" }
|
|
6076
|
+
}));
|
|
6077
|
+
}
|
|
6078
|
+
break;
|
|
6079
|
+
}
|
|
6080
|
+
case "ArrowDown":
|
|
6081
|
+
case "ArrowRight": {
|
|
6082
|
+
e.preventDefault();
|
|
6083
|
+
const next = element.nextElementSibling;
|
|
6084
|
+
if (next && (next.classList.contains("vd-draggable") || next.classList.contains("vd-draggable-item"))) {
|
|
6085
|
+
element.parentNode.insertBefore(next, element);
|
|
6086
|
+
element.focus();
|
|
6087
|
+
element.dispatchEvent(new CustomEvent("draggable:reorder", {
|
|
6088
|
+
bubbles: true,
|
|
6089
|
+
detail: { element, direction: "down" }
|
|
6090
|
+
}));
|
|
6091
|
+
}
|
|
6092
|
+
break;
|
|
6093
|
+
}
|
|
6094
|
+
}
|
|
6095
|
+
},
|
|
6096
|
+
/**
|
|
6097
|
+
* Get data from draggable element
|
|
6098
|
+
* @param {HTMLElement} element - Draggable element
|
|
6099
|
+
* @returns {string} Data associated with the element
|
|
6100
|
+
*/
|
|
6101
|
+
getData: function(element) {
|
|
6102
|
+
return element.dataset.draggable || element.textContent.trim();
|
|
6103
|
+
},
|
|
6104
|
+
/**
|
|
6105
|
+
* Update drag feedback element
|
|
6106
|
+
* @param {number} x - Current X coordinate
|
|
6107
|
+
* @param {number} y - Current Y coordinate
|
|
6108
|
+
*/
|
|
6109
|
+
updateFeedback: function(x, y) {
|
|
6110
|
+
if (!this.currentDrag) return;
|
|
6111
|
+
this.feedbackElement.classList.remove("hidden");
|
|
6112
|
+
const rect = this.currentDrag.initialBounds;
|
|
6113
|
+
this.feedbackElement.innerHTML = "";
|
|
6114
|
+
const clone = this.currentDrag.element.cloneNode(true);
|
|
6115
|
+
this.feedbackElement.appendChild(clone);
|
|
6116
|
+
Object.assign(this.feedbackElement.style, {
|
|
6117
|
+
left: x - 20 + "px",
|
|
6118
|
+
top: y - 20 + "px",
|
|
6119
|
+
width: rect.width + "px",
|
|
6120
|
+
height: rect.height + "px"
|
|
6121
|
+
});
|
|
6122
|
+
},
|
|
6123
|
+
/**
|
|
6124
|
+
* Make an element draggable programmatically
|
|
6125
|
+
* @param {HTMLElement|string} element - Element or selector
|
|
6126
|
+
* @param {Object} options - Configuration options
|
|
6127
|
+
*/
|
|
6128
|
+
makeDraggable: function(element, options = {}) {
|
|
6129
|
+
const el = typeof element === "string" ? document.querySelector(element) : element;
|
|
6130
|
+
if (el && !this.instances.has(el)) {
|
|
6131
|
+
el.classList.add("vd-draggable");
|
|
6132
|
+
el.setAttribute("draggable", "true");
|
|
6133
|
+
if (options.data) {
|
|
6134
|
+
el.dataset.draggable = options.data;
|
|
6135
|
+
}
|
|
6136
|
+
this.initDraggable(el);
|
|
6137
|
+
}
|
|
6138
|
+
},
|
|
6139
|
+
/**
|
|
6140
|
+
* Remove draggable functionality from an element
|
|
6141
|
+
* @param {HTMLElement|string} element - Element or selector
|
|
6142
|
+
*/
|
|
6143
|
+
removeDraggable: function(element) {
|
|
6144
|
+
const el = typeof element === "string" ? document.querySelector(element) : element;
|
|
6145
|
+
if (el && this.instances.has(el)) {
|
|
6146
|
+
const instance = this.instances.get(el);
|
|
6147
|
+
instance.cleanup.forEach((fn) => fn());
|
|
6148
|
+
this.instances.delete(el);
|
|
6149
|
+
el.classList.remove("vd-draggable");
|
|
6150
|
+
el.removeAttribute("draggable");
|
|
6151
|
+
el.removeAttribute("data-draggable");
|
|
6152
|
+
}
|
|
6153
|
+
},
|
|
6154
|
+
/**
|
|
6155
|
+
* Destroy a draggable instance and clean up event listeners
|
|
6156
|
+
* @param {HTMLElement} element - Draggable element
|
|
6157
|
+
*/
|
|
6158
|
+
destroy: function(element) {
|
|
6159
|
+
this.removeDraggable(element);
|
|
6160
|
+
},
|
|
6161
|
+
/**
|
|
6162
|
+
* Destroy all draggable instances
|
|
6163
|
+
*/
|
|
6164
|
+
destroyAll: function() {
|
|
6165
|
+
const instances = Array.from(this.instances.keys());
|
|
6166
|
+
instances.forEach((element) => this.destroy(element));
|
|
6167
|
+
}
|
|
6168
|
+
};
|
|
6169
|
+
if (typeof window.Vanduo !== "undefined") {
|
|
6170
|
+
window.Vanduo.register("draggable", Draggable);
|
|
6171
|
+
}
|
|
6172
|
+
window.VanduoDraggable = Draggable;
|
|
6173
|
+
})();
|
|
6174
|
+
|
|
5566
6175
|
// js/index.js
|
|
5567
6176
|
var Vanduo = window.Vanduo;
|
|
5568
6177
|
var index_default = Vanduo;
|