@vanduo-oss/framework 1.3.2 → 1.3.3
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 +13 -4
- package/css/components/draggable.css +3 -1
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +112 -18
- package/dist/vanduo.cjs.js.map +2 -2
- package/dist/vanduo.cjs.min.js +4 -4
- package/dist/vanduo.cjs.min.js.map +3 -3
- package/dist/vanduo.css +4 -2
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +112 -18
- package/dist/vanduo.esm.js.map +2 -2
- package/dist/vanduo.esm.min.js +4 -4
- package/dist/vanduo.esm.min.js.map +3 -3
- package/dist/vanduo.js +112 -18
- package/dist/vanduo.js.map +2 -2
- package/dist/vanduo.min.css +2 -2
- package/dist/vanduo.min.css.map +1 -1
- package/dist/vanduo.min.js +4 -4
- package/dist/vanduo.min.js.map +3 -3
- package/js/components/draggable.js +103 -12
- package/js/components/theme-customizer.js +22 -9
- package/js/components/theme-switcher.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Vanduo Framework v1.3.
|
|
1
|
+
# Vanduo Framework v1.3.3
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="vanduo-banner.svg" alt="Vanduo Framework Banner" width="100%">
|
|
@@ -38,6 +38,15 @@ A lightweight, pure HTML/CSS/JS framework with **46+ components** for designing
|
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
+
## What's New in v1.3.3
|
|
42
|
+
|
|
43
|
+
v1.3.3 is a maintenance release with no breaking changes:
|
|
44
|
+
|
|
45
|
+
- **Theme default primary alignment.** `ThemeCustomizer` and `ThemeSwitcher` normalize stale `black`/`amber` primary pairs against `localStorage` and `prefers-color-scheme`, so light, dark, and system modes stay consistent with `data-primary` after reloads and OS theme changes.
|
|
46
|
+
- **Documentation site.** Theme customizer demo storage keys align with framework (`vanduo-radius`, `vanduo-font-preference`); changelog and CDN pins updated to v1.3.3.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
41
50
|
## What's New in v1.3.2
|
|
42
51
|
|
|
43
52
|
v1.3.2 is a component release centered on audio playback, with no breaking changes:
|
|
@@ -89,8 +98,8 @@ The quickest way to get started — no install, no build step. Add two lines to
|
|
|
89
98
|
|
|
90
99
|
**Pin to a specific version** for production:
|
|
91
100
|
```html
|
|
92
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.3.
|
|
93
|
-
<script src="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.3.
|
|
101
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.3.3/dist/vanduo.min.css">
|
|
102
|
+
<script src="https://cdn.jsdelivr.net/gh/vanduo-oss/framework@v1.3.3/dist/vanduo.min.js"></script>
|
|
94
103
|
<script>Vanduo.init();</script>
|
|
95
104
|
```
|
|
96
105
|
|
|
@@ -153,7 +162,7 @@ This project includes an [`llms.txt`](llms.txt) file — a structured markdown s
|
|
|
153
162
|
Use the hardened upload script to attach only approved bundle artifacts from `dist/`:
|
|
154
163
|
|
|
155
164
|
```bash
|
|
156
|
-
pnpm run release:assets -- v1.3.
|
|
165
|
+
pnpm run release:assets -- v1.3.3
|
|
157
166
|
```
|
|
158
167
|
|
|
159
168
|
Notes:
|
|
@@ -187,6 +187,8 @@
|
|
|
187
187
|
border: 1px solid var(--draggable-border-color);
|
|
188
188
|
border-radius: var(--btn-border-radius);
|
|
189
189
|
cursor: grab;
|
|
190
|
+
user-select: none;
|
|
191
|
+
touch-action: none;
|
|
190
192
|
transition: var(--draggable-transition);
|
|
191
193
|
}
|
|
192
194
|
|
|
@@ -224,7 +226,7 @@
|
|
|
224
226
|
pointer-events: none;
|
|
225
227
|
z-index: 9999;
|
|
226
228
|
opacity: 0.9;
|
|
227
|
-
transform:
|
|
229
|
+
transform: none;
|
|
228
230
|
transition: opacity 0.2s ease;
|
|
229
231
|
}
|
|
230
232
|
|
package/dist/build-info.json
CHANGED
package/dist/vanduo.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Vanduo v1.3.
|
|
1
|
+
/*! Vanduo v1.3.3 | Built: 2026-04-10T21:45:12.664Z | git:281f4f6 | development */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -132,7 +132,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
132
132
|
// js/vanduo.js
|
|
133
133
|
(function() {
|
|
134
134
|
"use strict";
|
|
135
|
-
const VANDUO_VERSION = true ? "1.3.
|
|
135
|
+
const VANDUO_VERSION = true ? "1.3.3" : "0.0.0-dev";
|
|
136
136
|
const Vanduo2 = {
|
|
137
137
|
version: VANDUO_VERSION,
|
|
138
138
|
components: {},
|
|
@@ -3831,6 +3831,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
3831
3831
|
loadPreferences: function() {
|
|
3832
3832
|
this.state.theme = this.getStorageValue(this.STORAGE_KEYS.THEME, this.DEFAULTS.THEME);
|
|
3833
3833
|
this.state.primary = this.getStorageValue(this.STORAGE_KEYS.PRIMARY, this.getDefaultPrimary(this.state.theme));
|
|
3834
|
+
this._normalizeDefaultPrimaryIfStaleWithStoredTheme();
|
|
3834
3835
|
this.state.neutral = this.getStorageValue(this.STORAGE_KEYS.NEUTRAL, this.DEFAULTS.NEUTRAL);
|
|
3835
3836
|
this.state.radius = this.getStorageValue(this.STORAGE_KEYS.RADIUS, this.DEFAULTS.RADIUS);
|
|
3836
3837
|
this.state.font = this.getStorageValue(this.STORAGE_KEYS.FONT, this.DEFAULTS.FONT);
|
|
@@ -3916,12 +3917,10 @@ module.exports = __toCommonJS(index_exports);
|
|
|
3916
3917
|
mode = this.DEFAULTS.THEME;
|
|
3917
3918
|
}
|
|
3918
3919
|
this._isApplying = true;
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
if (newDefault !== this.state.primary) {
|
|
3924
|
-
this.applyPrimary(newDefault);
|
|
3920
|
+
if (this.isUsingDefaultPrimary()) {
|
|
3921
|
+
const expected = this.getDefaultPrimary(mode);
|
|
3922
|
+
if (this.state.primary !== expected) {
|
|
3923
|
+
this.applyPrimary(expected);
|
|
3925
3924
|
}
|
|
3926
3925
|
}
|
|
3927
3926
|
this.state.theme = mode;
|
|
@@ -4163,6 +4162,20 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4163
4162
|
isUsingDefaultPrimary: function() {
|
|
4164
4163
|
return this.state.primary === this.DEFAULTS.PRIMARY_LIGHT || this.state.primary === this.DEFAULTS.PRIMARY_DARK;
|
|
4165
4164
|
},
|
|
4165
|
+
/**
|
|
4166
|
+
* When primary is still one of the auto-default palette keys (black/amber) but
|
|
4167
|
+
* localStorage was written under a different theme (or OS changed in system mode),
|
|
4168
|
+
* align in-memory state before applyAllPreferences runs — avoids amber+light / black+dark drift.
|
|
4169
|
+
*/
|
|
4170
|
+
_normalizeDefaultPrimaryIfStaleWithStoredTheme: function() {
|
|
4171
|
+
if (!this.isUsingDefaultPrimary()) {
|
|
4172
|
+
return;
|
|
4173
|
+
}
|
|
4174
|
+
const expected = this.getDefaultPrimary(this.state.theme);
|
|
4175
|
+
if (this.state.primary !== expected) {
|
|
4176
|
+
this.state.primary = expected;
|
|
4177
|
+
}
|
|
4178
|
+
},
|
|
4166
4179
|
bindEvents: function() {
|
|
4167
4180
|
if (this.elements.trigger) {
|
|
4168
4181
|
this.addListener(this.elements.trigger, "click", (e) => {
|
|
@@ -4401,6 +4414,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
4401
4414
|
this._onMediaChange = (_e) => {
|
|
4402
4415
|
if (this.state.preference === "system") {
|
|
4403
4416
|
this.applyTheme();
|
|
4417
|
+
if (window.ThemeCustomizer && typeof window.ThemeCustomizer.applyTheme === "function" && !window.ThemeCustomizer._isApplying) {
|
|
4418
|
+
window.ThemeCustomizer.applyTheme("system");
|
|
4419
|
+
}
|
|
4404
4420
|
}
|
|
4405
4421
|
};
|
|
4406
4422
|
this._mediaQuery.addEventListener("change", this._onMediaChange);
|
|
@@ -5632,6 +5648,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5632
5648
|
touchState: null,
|
|
5633
5649
|
// Feedback element
|
|
5634
5650
|
feedbackElement: null,
|
|
5651
|
+
// Shared selector used by init and touch reorder
|
|
5652
|
+
containerSelector: ".vd-draggable-container, .vd-draggable-container-vertical",
|
|
5635
5653
|
/**
|
|
5636
5654
|
* Initialize draggable components
|
|
5637
5655
|
*/
|
|
@@ -5643,7 +5661,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5643
5661
|
}
|
|
5644
5662
|
this.initDraggable(element);
|
|
5645
5663
|
});
|
|
5646
|
-
const containers = document.querySelectorAll(
|
|
5664
|
+
const containers = document.querySelectorAll(this.containerSelector);
|
|
5647
5665
|
containers.forEach((container) => {
|
|
5648
5666
|
if (!this.instances.has(container)) {
|
|
5649
5667
|
this.initContainer(container);
|
|
@@ -5887,10 +5905,16 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5887
5905
|
*/
|
|
5888
5906
|
handleTouchStart: function(e, element) {
|
|
5889
5907
|
const touch = e.touches[0];
|
|
5908
|
+
const rect = element.getBoundingClientRect();
|
|
5890
5909
|
this.touchState = {
|
|
5891
5910
|
element,
|
|
5892
5911
|
startX: touch.clientX,
|
|
5893
5912
|
startY: touch.clientY,
|
|
5913
|
+
lastX: touch.clientX,
|
|
5914
|
+
lastY: touch.clientY,
|
|
5915
|
+
// Keep preview anchored to the original grab point.
|
|
5916
|
+
offsetX: touch.clientX - rect.left,
|
|
5917
|
+
offsetY: touch.clientY - rect.top,
|
|
5894
5918
|
startTime: Date.now(),
|
|
5895
5919
|
isDragging: false
|
|
5896
5920
|
};
|
|
@@ -5903,6 +5927,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5903
5927
|
handleTouchMove: function(e, element) {
|
|
5904
5928
|
if (!this.touchState) return;
|
|
5905
5929
|
const touch = e.touches[0];
|
|
5930
|
+
this.touchState.lastX = touch.clientX;
|
|
5931
|
+
this.touchState.lastY = touch.clientY;
|
|
5906
5932
|
const deltaX = touch.clientX - this.touchState.startX;
|
|
5907
5933
|
const deltaY = touch.clientY - this.touchState.startY;
|
|
5908
5934
|
if (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10) {
|
|
@@ -5915,7 +5941,10 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5915
5941
|
element,
|
|
5916
5942
|
initialPosition: { x: this.touchState.startX, y: this.touchState.startY },
|
|
5917
5943
|
initialBounds: element.getBoundingClientRect(),
|
|
5918
|
-
data: this.getData(element)
|
|
5944
|
+
data: this.getData(element),
|
|
5945
|
+
// Preserve where inside the element the drag started for accurate ghost positioning.
|
|
5946
|
+
offsetX: this.touchState.offsetX,
|
|
5947
|
+
offsetY: this.touchState.offsetY
|
|
5919
5948
|
};
|
|
5920
5949
|
element.dispatchEvent(new CustomEvent("draggable:start", {
|
|
5921
5950
|
bubbles: true,
|
|
@@ -5937,7 +5966,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5937
5966
|
delta: { x: deltaX, y: deltaY }
|
|
5938
5967
|
}
|
|
5939
5968
|
}));
|
|
5940
|
-
|
|
5969
|
+
this.updateTouchDropZone(touch.clientX, touch.clientY);
|
|
5970
|
+
const container = element.closest(this.containerSelector);
|
|
5941
5971
|
if (container && container.contains(element)) {
|
|
5942
5972
|
this.handleReorder(container, element, touch.clientX, touch.clientY);
|
|
5943
5973
|
}
|
|
@@ -5952,6 +5982,17 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5952
5982
|
handleTouchEnd: function(e, element) {
|
|
5953
5983
|
if (this.touchState && this.touchState.isDragging) {
|
|
5954
5984
|
if (e.cancelable) e.preventDefault();
|
|
5985
|
+
const endTouch = e.changedTouches?.[0];
|
|
5986
|
+
const endPosition = {
|
|
5987
|
+
x: endTouch?.clientX ?? this.touchState.lastX ?? this.touchState.startX,
|
|
5988
|
+
y: endTouch?.clientY ?? this.touchState.lastY ?? this.touchState.startY
|
|
5989
|
+
};
|
|
5990
|
+
const dropZone = this.resolveDropZoneAtPoint(endPosition.x, endPosition.y) || this.touchState.overZone;
|
|
5991
|
+
if (dropZone) {
|
|
5992
|
+
this.dispatchDrop(dropZone, endPosition);
|
|
5993
|
+
} else if (this.touchState.overZone) {
|
|
5994
|
+
this.touchState.overZone.classList.remove("is-drag-over");
|
|
5995
|
+
}
|
|
5955
5996
|
element.classList.remove("is-dragging");
|
|
5956
5997
|
element.classList.add("is-dropped");
|
|
5957
5998
|
element.setAttribute("aria-grabbed", "false");
|
|
@@ -5959,7 +6000,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5959
6000
|
if (this.feedbackElement) {
|
|
5960
6001
|
this.feedbackElement.classList.add("hidden");
|
|
5961
6002
|
}
|
|
5962
|
-
const endTouch = e.changedTouches[0];
|
|
5963
6003
|
const data = this.currentDrag?.data || this.getData(element);
|
|
5964
6004
|
const startX = this.touchState?.startX || 0;
|
|
5965
6005
|
const startY = this.touchState?.startY || 0;
|
|
@@ -5968,10 +6008,10 @@ module.exports = __toCommonJS(index_exports);
|
|
|
5968
6008
|
detail: {
|
|
5969
6009
|
element,
|
|
5970
6010
|
data,
|
|
5971
|
-
position:
|
|
6011
|
+
position: endPosition,
|
|
5972
6012
|
delta: {
|
|
5973
|
-
x:
|
|
5974
|
-
y:
|
|
6013
|
+
x: endPosition.x - startX,
|
|
6014
|
+
y: endPosition.y - startY
|
|
5975
6015
|
}
|
|
5976
6016
|
}
|
|
5977
6017
|
}));
|
|
@@ -6012,6 +6052,58 @@ module.exports = __toCommonJS(index_exports);
|
|
|
6012
6052
|
*/
|
|
6013
6053
|
handleDrop: function(e, zone) {
|
|
6014
6054
|
e.preventDefault();
|
|
6055
|
+
this.dispatchDrop(zone, { x: e.clientX, y: e.clientY });
|
|
6056
|
+
},
|
|
6057
|
+
/**
|
|
6058
|
+
* Resolve a drop zone from viewport coordinates
|
|
6059
|
+
* @param {number} x
|
|
6060
|
+
* @param {number} y
|
|
6061
|
+
* @returns {HTMLElement|null}
|
|
6062
|
+
*/
|
|
6063
|
+
resolveDropZoneAtPoint: function(x, y) {
|
|
6064
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
6065
|
+
if (typeof document.elementsFromPoint === "function") {
|
|
6066
|
+
const stacked = document.elementsFromPoint(x, y);
|
|
6067
|
+
for (const element of stacked) {
|
|
6068
|
+
const zone = element.closest(".vd-drop-zone");
|
|
6069
|
+
if (zone) return zone;
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
const target = document.elementFromPoint(x, y);
|
|
6073
|
+
const targetZone = target ? target.closest(".vd-drop-zone") : null;
|
|
6074
|
+
if (targetZone) return targetZone;
|
|
6075
|
+
const zones = document.querySelectorAll(".vd-drop-zone");
|
|
6076
|
+
for (const zone of zones) {
|
|
6077
|
+
const rect = zone.getBoundingClientRect();
|
|
6078
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
6079
|
+
return zone;
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
return null;
|
|
6083
|
+
},
|
|
6084
|
+
/**
|
|
6085
|
+
* Track and update active drop-zone hover state on touch devices
|
|
6086
|
+
* @param {number} x
|
|
6087
|
+
* @param {number} y
|
|
6088
|
+
*/
|
|
6089
|
+
updateTouchDropZone: function(x, y) {
|
|
6090
|
+
if (!this.touchState) return;
|
|
6091
|
+
const nextZone = this.resolveDropZoneAtPoint(x, y);
|
|
6092
|
+
const prevZone = this.touchState.overZone || null;
|
|
6093
|
+
if (prevZone && prevZone !== nextZone) {
|
|
6094
|
+
prevZone.classList.remove("is-drag-over");
|
|
6095
|
+
}
|
|
6096
|
+
if (nextZone && nextZone !== prevZone) {
|
|
6097
|
+
nextZone.classList.add("is-drag-over");
|
|
6098
|
+
}
|
|
6099
|
+
this.touchState.overZone = nextZone || null;
|
|
6100
|
+
},
|
|
6101
|
+
/**
|
|
6102
|
+
* Dispatch a normalized drop event for mouse and touch flows
|
|
6103
|
+
* @param {HTMLElement} zone
|
|
6104
|
+
* @param {{x:number, y:number}} position
|
|
6105
|
+
*/
|
|
6106
|
+
dispatchDrop: function(zone, position) {
|
|
6015
6107
|
zone.classList.remove("is-drag-over");
|
|
6016
6108
|
zone.dispatchEvent(new CustomEvent("draggable:drop", {
|
|
6017
6109
|
bubbles: true,
|
|
@@ -6019,7 +6111,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
6019
6111
|
zone,
|
|
6020
6112
|
element: this.currentDrag?.element,
|
|
6021
6113
|
data: this.currentDrag?.data,
|
|
6022
|
-
position
|
|
6114
|
+
position
|
|
6023
6115
|
}
|
|
6024
6116
|
}));
|
|
6025
6117
|
},
|
|
@@ -6120,9 +6212,11 @@ module.exports = __toCommonJS(index_exports);
|
|
|
6120
6212
|
this.feedbackElement.innerHTML = "";
|
|
6121
6213
|
const clone = this.currentDrag.element.cloneNode(true);
|
|
6122
6214
|
this.feedbackElement.appendChild(clone);
|
|
6215
|
+
const offsetX = this.currentDrag.offsetX ?? 20;
|
|
6216
|
+
const offsetY = this.currentDrag.offsetY ?? 20;
|
|
6123
6217
|
Object.assign(this.feedbackElement.style, {
|
|
6124
|
-
left: x -
|
|
6125
|
-
top: y -
|
|
6218
|
+
left: x - offsetX + "px",
|
|
6219
|
+
top: y - offsetY + "px",
|
|
6126
6220
|
width: rect.width + "px",
|
|
6127
6221
|
height: rect.height + "px"
|
|
6128
6222
|
});
|