@epoch32/visage 1.3.0 → 1.3.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/dist/api/index.js +4 -4
- package/dist/charts/index.js +5 -5
- package/dist/devtools/index.js +7 -7
- package/dist/dom/index.js +3 -3
- package/dist/form/index.js +2 -2
- package/dist/{index-grhdtcb5.js → index-0qat98zs.js} +3 -3
- package/dist/{index-h6tjqk6e.js → index-1jcd8gjf.js} +28 -17
- package/dist/{index-6kfe1nr4.js → index-1yhbg34h.js} +41 -9
- package/dist/{index-9fvarkrx.js → index-3y73zgbk.js} +1 -1
- package/dist/{index-y9bq7h9g.js → index-5jhzwtdz.js} +2 -2
- package/dist/{index-a4xns6cc.js → index-65j7a9et.js} +1 -1
- package/dist/{index-dyvgtp43.js → index-9bez1tmf.js} +2 -2
- package/dist/{index-dvq5f3s8.js → index-aagpn02x.js} +4 -4
- package/dist/{index-df5e614s.js → index-awcrz19g.js} +1 -1
- package/dist/{index-8x8xz80v.js → index-b7emqmej.js} +3 -3
- package/dist/{index-061jf06a.js → index-cgepvvd4.js} +1 -1
- package/dist/{index-1xtrtz1t.js → index-dmhmde2z.js} +2 -2
- package/dist/{index-0madxn60.js → index-enc8cmea.js} +1 -1
- package/dist/{index-js199er4.js → index-g1drdx7k.js} +12 -1
- package/dist/{index-zr6cvset.js → index-ptj8rt6b.js} +5 -5
- package/dist/{index-8czhpea8.js → index-x6whpv54.js} +30 -19
- package/dist/{index-5s255b3w.js → index-xby428n5.js} +23 -22
- package/dist/{index-x2rs3vab.js → index-y3nprgn7.js} +1 -1
- package/dist/index.js +18 -18
- package/dist/jsx-runtime/index.js +4 -4
- package/dist/query/index.js +3 -3
- package/dist/router/index.js +5 -5
- package/dist/rpc/index.js +4 -4
- package/dist/server/index.js +2 -2
- package/dist/signals/index.js +1 -1
- package/dist/signals/system.d.ts +2 -0
- package/dist/style/index.js +6 -6
- package/dist/template/index.js +2 -2
- package/dist/ui/index.js +7 -7
- package/dist/virtual/index.js +5 -5
- package/package.json +1 -1
- package/src/dom/control-flow.ts +31 -21
- package/src/form/form.ts +29 -17
- package/src/signals/effect.ts +2 -2
- package/src/signals/scope.ts +10 -8
- package/src/signals/store.ts +32 -1
- package/src/signals/system.ts +8 -0
- package/src/template/template.ts +12 -1
- package/src/virtual/virtualizer.ts +31 -18
package/src/signals/store.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { isSignal } from "./types";
|
|
|
5
5
|
const STORE_RAW = Symbol("visage.store.raw");
|
|
6
6
|
const STORE_SIGNALS = Symbol("visage.store.signals");
|
|
7
7
|
const STORE_PROXY = Symbol("visage.store.proxy");
|
|
8
|
+
const STORE_ITERATE = Symbol("visage.store.iterate");
|
|
8
9
|
|
|
9
10
|
export type Store<T> = T;
|
|
10
11
|
|
|
@@ -61,9 +62,29 @@ function createStoreProxy<T extends object>(target: T): T {
|
|
|
61
62
|
return value;
|
|
62
63
|
},
|
|
63
64
|
|
|
65
|
+
has(target, prop) {
|
|
66
|
+
if (prop === STORE_RAW || prop === STORE_PROXY) return true;
|
|
67
|
+
if (typeof prop === "string" || typeof prop === "symbol") {
|
|
68
|
+
const sig = getOrCreatePropertySignal(target, prop);
|
|
69
|
+
sig.get();
|
|
70
|
+
}
|
|
71
|
+
return Reflect.has(target, prop);
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
ownKeys(target) {
|
|
75
|
+
const iterSig = getOrCreatePropertySignal(target, STORE_ITERATE);
|
|
76
|
+
iterSig.get();
|
|
77
|
+
return Reflect.ownKeys(target);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
81
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
82
|
+
},
|
|
83
|
+
|
|
64
84
|
set(target, prop, value, receiver) {
|
|
85
|
+
const hadProp = Reflect.has(target, prop);
|
|
65
86
|
const prevValue = Reflect.get(target, prop, receiver);
|
|
66
|
-
if (Object.is(prevValue, value)) {
|
|
87
|
+
if (hadProp && Object.is(prevValue, value)) {
|
|
67
88
|
return true;
|
|
68
89
|
}
|
|
69
90
|
|
|
@@ -72,10 +93,18 @@ function createStoreProxy<T extends object>(target: T): T {
|
|
|
72
93
|
const sig = getOrCreatePropertySignal(target, prop);
|
|
73
94
|
sig.set(value);
|
|
74
95
|
|
|
96
|
+
// If a new property was added, notify key iteration
|
|
97
|
+
if (!hadProp) {
|
|
98
|
+
const iterSig = getOrCreatePropertySignal(target, STORE_ITERATE);
|
|
99
|
+
iterSig.update((prev: number = 0) => prev + 1);
|
|
100
|
+
}
|
|
101
|
+
|
|
75
102
|
// If array length or property keys changed, notify length/keys
|
|
76
103
|
if (Array.isArray(target) && prop !== "length") {
|
|
77
104
|
const lenSig = getOrCreatePropertySignal(target, "length");
|
|
78
105
|
lenSig.set(target.length);
|
|
106
|
+
const iterSig = getOrCreatePropertySignal(target, STORE_ITERATE);
|
|
107
|
+
iterSig.update((prev: number = 0) => prev + 1);
|
|
79
108
|
}
|
|
80
109
|
}
|
|
81
110
|
return success;
|
|
@@ -87,6 +116,8 @@ function createStoreProxy<T extends object>(target: T): T {
|
|
|
87
116
|
if (hadProp && success) {
|
|
88
117
|
const sig = getOrCreatePropertySignal(target, prop);
|
|
89
118
|
sig.set(undefined);
|
|
119
|
+
const iterSig = getOrCreatePropertySignal(target, STORE_ITERATE);
|
|
120
|
+
iterSig.update((prev: number = 0) => prev + 1);
|
|
90
121
|
}
|
|
91
122
|
return success;
|
|
92
123
|
}
|
package/src/signals/system.ts
CHANGED
|
@@ -59,6 +59,14 @@ export function removePendingEffect(eff: any): void {
|
|
|
59
59
|
ctx.pendingEffects.delete(eff);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export function getBatchDepth(): number {
|
|
63
|
+
return ctx.batchDepth;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getIsFlushing(): boolean {
|
|
67
|
+
return ctx.isFlushing;
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
const MAX_FLUSH_COUNT = 100;
|
|
63
71
|
|
|
64
72
|
export function flushEffects(): void {
|
package/src/template/template.ts
CHANGED
|
@@ -66,11 +66,22 @@ export function html(
|
|
|
66
66
|
|
|
67
67
|
for (const bind of bindings) {
|
|
68
68
|
if (bind.isAttr && bind.attrName) {
|
|
69
|
-
const targetEl = findElementWithAttributeValue(root, bind.attrName, bind.id);
|
|
69
|
+
const targetEl = findElementWithAttributeValue(root as HTMLElement, bind.attrName, bind.id);
|
|
70
70
|
if (targetEl) {
|
|
71
71
|
targetEl.removeAttribute(bind.attrName);
|
|
72
72
|
setAttribute(targetEl, bind.attrName, bind.value);
|
|
73
73
|
}
|
|
74
|
+
} else if (bind.isAttr && !bind.attrName) {
|
|
75
|
+
const propAttr = `data-visage-prop-${bind.id.replace(MARKER_PREFIX, "")}`;
|
|
76
|
+
const targetEl = findElementWithAttributeValue(root as HTMLElement, propAttr, bind.id);
|
|
77
|
+
if (targetEl) {
|
|
78
|
+
targetEl.removeAttribute(propAttr);
|
|
79
|
+
if (typeof bind.value === "object" && bind.value !== null) {
|
|
80
|
+
for (const key of Object.keys(bind.value)) {
|
|
81
|
+
setAttribute(targetEl, key, bind.value[key]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
74
85
|
} else if (!bind.isAttr) {
|
|
75
86
|
const commentNode = findCommentNode(root, bind.id);
|
|
76
87
|
if (commentNode?.parentNode) {
|
|
@@ -56,30 +56,41 @@ export function createVirtualizer(options: VirtualizerOptions): Virtualizer {
|
|
|
56
56
|
return getItemHeight(index);
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
//
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < count; i++) {
|
|
67
|
-
total += getItemSize(i);
|
|
68
|
-
}
|
|
69
|
-
return total;
|
|
70
|
-
});
|
|
59
|
+
// Cached prefix offsets for O(1) offset lookups and true O(log N) binary search
|
|
60
|
+
const offsetsCache: number[] = [0];
|
|
61
|
+
let offsetsValidUpTo = 0;
|
|
62
|
+
|
|
63
|
+
const invalidateOffsetsFrom = (index: number) => {
|
|
64
|
+
offsetsValidUpTo = Math.min(offsetsValidUpTo, index);
|
|
65
|
+
};
|
|
71
66
|
|
|
72
67
|
const getOffsetForIndex = (targetIndex: number): number => {
|
|
68
|
+
if (targetIndex <= 0) return 0;
|
|
73
69
|
if (typeof options.itemHeight === "number" && dynamicSizeCache.size === 0) {
|
|
74
70
|
return targetIndex * options.itemHeight;
|
|
75
71
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
offset += getItemSize(i);
|
|
72
|
+
if (targetIndex <= offsetsValidUpTo) {
|
|
73
|
+
return offsetsCache[targetIndex]!;
|
|
79
74
|
}
|
|
80
|
-
|
|
75
|
+
for (let i = offsetsValidUpTo; i < targetIndex; i++) {
|
|
76
|
+
offsetsCache[i + 1] = (offsetsCache[i] ?? 0) + getItemSize(i);
|
|
77
|
+
}
|
|
78
|
+
offsetsValidUpTo = targetIndex;
|
|
79
|
+
return offsetsCache[targetIndex]!;
|
|
81
80
|
};
|
|
82
81
|
|
|
82
|
+
const measurementVersion = signal(0, { internal: true });
|
|
83
|
+
|
|
84
|
+
// Compute prefix offsets for fast range calculation
|
|
85
|
+
const totalSize = computed(() => {
|
|
86
|
+
measurementVersion();
|
|
87
|
+
const count = getCount();
|
|
88
|
+
if (typeof options.itemHeight === "number" && dynamicSizeCache.size === 0) {
|
|
89
|
+
return count * options.itemHeight;
|
|
90
|
+
}
|
|
91
|
+
return getOffsetForIndex(count);
|
|
92
|
+
});
|
|
93
|
+
|
|
83
94
|
const findIndexAtOffset = (offset: number): number => {
|
|
84
95
|
const count = getCount();
|
|
85
96
|
if (count === 0) return 0;
|
|
@@ -110,12 +121,14 @@ export function createVirtualizer(options: VirtualizerOptions): Virtualizer {
|
|
|
110
121
|
};
|
|
111
122
|
|
|
112
123
|
const startIndex = computed(() => {
|
|
124
|
+
measurementVersion();
|
|
113
125
|
const offset = scrollOffset();
|
|
114
126
|
const rawStart = findIndexAtOffset(offset);
|
|
115
127
|
return Math.max(0, rawStart - overscan);
|
|
116
128
|
});
|
|
117
129
|
|
|
118
130
|
const endIndex = computed(() => {
|
|
131
|
+
measurementVersion();
|
|
119
132
|
const count = getCount();
|
|
120
133
|
if (count === 0) return 0;
|
|
121
134
|
const endOffset = scrollOffset() + (containerSize() || 500);
|
|
@@ -255,8 +268,8 @@ export function createVirtualizer(options: VirtualizerOptions): Virtualizer {
|
|
|
255
268
|
: element.getBoundingClientRect().height;
|
|
256
269
|
if (measuredSize > 0 && dynamicSizeCache.get(index) !== measuredSize) {
|
|
257
270
|
dynamicSizeCache.set(index, measuredSize);
|
|
258
|
-
|
|
259
|
-
|
|
271
|
+
invalidateOffsetsFrom(index);
|
|
272
|
+
measurementVersion.update((v) => v + 1);
|
|
260
273
|
}
|
|
261
274
|
};
|
|
262
275
|
|