@epoch32/visage 1.3.0 → 1.3.2
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-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-df5e614s.js → index-awcrz19g.js} +1 -1
- package/dist/{index-061jf06a.js → index-cgepvvd4.js} +1 -1
- package/dist/{index-1xtrtz1t.js → index-dmhmde2z.js} +2 -2
- package/dist/{index-zr6cvset.js → index-dz6n88aq.js} +5 -5
- package/dist/{index-0madxn60.js → index-enc8cmea.js} +1 -1
- package/dist/{index-js199er4.js → index-g1drdx7k.js} +12 -1
- package/dist/{index-grhdtcb5.js → index-k7zd3awa.js} +14 -5
- package/dist/{index-8x8xz80v.js → index-kykzcs7p.js} +5 -4
- package/dist/{index-dvq5f3s8.js → index-vbsc8z8h.js} +121 -52
- 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.d.ts +1 -1
- package/dist/index.js +20 -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/router/router.d.ts +1 -0
- 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/dropdown-menu.d.ts +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +9 -7
- package/dist/ui/overlay.d.ts +53 -0
- package/dist/ui/popover.d.ts +1 -1
- package/dist/ui/utils.d.ts +1 -1
- 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/index.ts +2 -0
- package/src/router/router.tsx +12 -2
- 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/ui/command-dialog.tsx +18 -1
- package/src/ui/dialog.tsx +16 -14
- package/src/ui/dropdown-menu.tsx +17 -14
- package/src/ui/index.ts +4 -0
- package/src/ui/overlay.ts +130 -0
- package/src/ui/popover.tsx +20 -22
- package/src/ui/utils.ts +8 -2
- package/src/virtual/virtualizer.ts +31 -18
package/dist/rpc/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
query3,
|
|
6
6
|
mutation3,
|
|
7
7
|
subscription3
|
|
8
|
-
} from "../index-
|
|
8
|
+
} from "../index-awcrz19g.js";
|
|
9
9
|
import {
|
|
10
10
|
ERROR_CODE_TO_STATUS2,
|
|
11
11
|
STATUS_TO_ERROR_CODE2,
|
|
@@ -19,9 +19,9 @@ import {
|
|
|
19
19
|
validateWithSchema2,
|
|
20
20
|
v2
|
|
21
21
|
} from "../index-2zz0g2n2.js";
|
|
22
|
-
import"../index-
|
|
23
|
-
import"../index-
|
|
24
|
-
import"../index-
|
|
22
|
+
import"../index-5jhzwtdz.js";
|
|
23
|
+
import"../index-65j7a9et.js";
|
|
24
|
+
import"../index-1yhbg34h.js";
|
|
25
25
|
export {
|
|
26
26
|
ERROR_CODE_TO_STATUS2 as ERROR_CODE_TO_STATUS,
|
|
27
27
|
RPCError2 as RPCError,
|
package/dist/server/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
renderChildToString2,
|
|
4
4
|
renderToString2,
|
|
5
5
|
renderToStream2
|
|
6
|
-
} from "../index-
|
|
7
|
-
import"../index-
|
|
6
|
+
} from "../index-y3nprgn7.js";
|
|
7
|
+
import"../index-1yhbg34h.js";
|
|
8
8
|
export {
|
|
9
9
|
hydrate2 as hydrate,
|
|
10
10
|
renderChildToString2 as renderChildToString,
|
package/dist/signals/index.js
CHANGED
package/dist/signals/system.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export declare function setCurrentScope(scope: any): void;
|
|
|
14
14
|
export declare function getCurrentScope(): any;
|
|
15
15
|
export declare function addPendingEffect(eff: any): void;
|
|
16
16
|
export declare function removePendingEffect(eff: any): void;
|
|
17
|
+
export declare function getBatchDepth(): number;
|
|
18
|
+
export declare function getIsFlushing(): boolean;
|
|
17
19
|
export declare function flushEffects(): void;
|
|
18
20
|
/**
|
|
19
21
|
* Batch multiple signal mutations so downstreams and effects update synchronously once at completion.
|
package/dist/style/index.js
CHANGED
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
radius2,
|
|
17
17
|
glass2,
|
|
18
18
|
formatStyleProp2
|
|
19
|
-
} from "../index-
|
|
20
|
-
import"../index-
|
|
21
|
-
import"../index-
|
|
19
|
+
} from "../index-1yhbg34h.js";
|
|
20
|
+
import"../index-xby428n5.js";
|
|
21
|
+
import"../index-3y73zgbk.js";
|
|
22
22
|
import {
|
|
23
23
|
hashString2,
|
|
24
24
|
getScopeId2,
|
|
@@ -32,15 +32,15 @@ import {
|
|
|
32
32
|
css2,
|
|
33
33
|
useStyle2,
|
|
34
34
|
Style2
|
|
35
|
-
} from "../index-
|
|
35
|
+
} from "../index-enc8cmea.js";
|
|
36
36
|
import {
|
|
37
37
|
Stack2,
|
|
38
38
|
Row2,
|
|
39
39
|
Center2,
|
|
40
40
|
Grid2,
|
|
41
41
|
Spacer2
|
|
42
|
-
} from "../index-
|
|
43
|
-
import"../index-
|
|
42
|
+
} from "../index-dmhmde2z.js";
|
|
43
|
+
import"../index-65j7a9et.js";
|
|
44
44
|
export {
|
|
45
45
|
Center2 as Center,
|
|
46
46
|
DIMENSION_KEYWORDS2 as DIMENSION_KEYWORDS,
|
package/dist/template/index.js
CHANGED
package/dist/ui/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { Dialog, DialogClose, DialogContent, type DialogContentProps, DialogDesc
|
|
|
6
6
|
export { DropdownMenu, DropdownMenuContent, DropdownMenuItem, type DropdownMenuItemProps, DropdownMenuPortal, type DropdownMenuProps, DropdownMenuSeparator, DropdownMenuTrigger, useDropdownMenuContext } from "./dropdown-menu";
|
|
7
7
|
export { createIcon, type IconNode, type IconProps } from "./icon";
|
|
8
8
|
export { type BaseLayoutProps, Center, type CenterProps, Grid, type GridProps, Row, type RowProps, Spacer, type SpacerProps, Stack, type StackProps } from "./layout";
|
|
9
|
+
export { type UseDismissOptions, useDismiss } from "./overlay";
|
|
9
10
|
export { Popover, PopoverClose, PopoverContent, type PopoverContentProps, PopoverPortal, type PopoverProps, PopoverTrigger, usePopoverContext } from "./popover";
|
|
10
11
|
export { Switch, type SwitchProps, SwitchThumb, type SwitchThumbProps } from "./switch";
|
|
11
12
|
export { type CreateTabsOptions, createTabs, Tabs, TabsContent, type TabsContentProps, TabsList, type TabsProps, TabsTrigger, useTabsContext } from "./tabs";
|
package/dist/ui/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
BottomBarRoot2,
|
|
13
13
|
BottomBarSeparator2,
|
|
14
14
|
BottomBar2,
|
|
15
|
+
useDismiss2,
|
|
15
16
|
CommandDialog2,
|
|
16
17
|
useCommandContext2,
|
|
17
18
|
CommandInput2,
|
|
@@ -54,7 +55,7 @@ import {
|
|
|
54
55
|
ToastDescription2,
|
|
55
56
|
ToastClose2,
|
|
56
57
|
Toast2
|
|
57
|
-
} from "../index-
|
|
58
|
+
} from "../index-vbsc8z8h.js";
|
|
58
59
|
import {
|
|
59
60
|
createId2,
|
|
60
61
|
createControllableSignal2,
|
|
@@ -73,18 +74,18 @@ import {
|
|
|
73
74
|
Tooltip3,
|
|
74
75
|
SwitchThumb2,
|
|
75
76
|
Switch2
|
|
76
|
-
} from "../index-
|
|
77
|
+
} from "../index-kykzcs7p.js";
|
|
77
78
|
import {
|
|
78
79
|
Stack2,
|
|
79
80
|
Row2,
|
|
80
81
|
Center2,
|
|
81
82
|
Grid2,
|
|
82
83
|
Spacer2
|
|
83
|
-
} from "../index-
|
|
84
|
-
import"../index-
|
|
85
|
-
import"../index-
|
|
86
|
-
import"../index-
|
|
87
|
-
import"../index-
|
|
84
|
+
} from "../index-dmhmde2z.js";
|
|
85
|
+
import"../index-3y73zgbk.js";
|
|
86
|
+
import"../index-xby428n5.js";
|
|
87
|
+
import"../index-65j7a9et.js";
|
|
88
|
+
import"../index-1yhbg34h.js";
|
|
88
89
|
export {
|
|
89
90
|
Accordion2 as Accordion,
|
|
90
91
|
AccordionContent2 as AccordionContent,
|
|
@@ -158,6 +159,7 @@ export {
|
|
|
158
159
|
useBottomBarContext2 as useBottomBarContext,
|
|
159
160
|
useCommandContext2 as useCommandContext,
|
|
160
161
|
useDialogContext2 as useDialogContext,
|
|
162
|
+
useDismiss2 as useDismiss,
|
|
161
163
|
useDropdownMenuContext2 as useDropdownMenuContext,
|
|
162
164
|
usePopoverContext2 as usePopoverContext,
|
|
163
165
|
useTabsContext2 as useTabsContext,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface UseDismissOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Whether dismiss listeners are active.
|
|
4
|
+
* Defaults to true.
|
|
5
|
+
*/
|
|
6
|
+
enabled?: boolean | (() => boolean);
|
|
7
|
+
/**
|
|
8
|
+
* Callback fired when dismissal is requested (Escape, click outside, or close button).
|
|
9
|
+
*/
|
|
10
|
+
onDismiss: () => void;
|
|
11
|
+
/**
|
|
12
|
+
* Close on 'Escape' key press.
|
|
13
|
+
* Defaults to true.
|
|
14
|
+
*/
|
|
15
|
+
escapeKey?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Close on pointer interaction outside the target element(s).
|
|
18
|
+
* Defaults to true.
|
|
19
|
+
*/
|
|
20
|
+
outsidePointer?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Additional element(s) to exclude from outside click detection (e.g. trigger button).
|
|
23
|
+
*/
|
|
24
|
+
outsideIgnore?: Array<HTMLElement | null | undefined> | HTMLElement | null | undefined | (() => Array<HTMLElement | null | undefined>);
|
|
25
|
+
/**
|
|
26
|
+
* Trap keyboard focus inside the container when Tab / Shift+Tab is pressed.
|
|
27
|
+
* Defaults to false.
|
|
28
|
+
*/
|
|
29
|
+
trapFocus?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Close selector for click events bubbling inside the container (e.g. '[data-dialog-close]').
|
|
32
|
+
*/
|
|
33
|
+
closeSelector?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Callback fired when an outside pointer interaction occurs.
|
|
36
|
+
*/
|
|
37
|
+
onOutsidePointer?: (event: MouseEvent | TouchEvent) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Callback fired when an Escape keydown occurs.
|
|
40
|
+
*/
|
|
41
|
+
onEscapeKeyDown?: (event: KeyboardEvent) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Headless hook to manage overlay dismissal:
|
|
45
|
+
* - Outside pointer dismissal (click outside)
|
|
46
|
+
* - Escape key dismissal
|
|
47
|
+
* - Keyboard focus trapping (Tab cycle)
|
|
48
|
+
* - Close button selector dismissal delegation
|
|
49
|
+
*/
|
|
50
|
+
export declare function useDismiss(element: () => HTMLElement | null | undefined, options: UseDismissOptions): {
|
|
51
|
+
handleKeyDown: (e: KeyboardEvent) => void;
|
|
52
|
+
handleClick: (e: MouseEvent) => void;
|
|
53
|
+
};
|
package/dist/ui/popover.d.ts
CHANGED
package/dist/ui/utils.d.ts
CHANGED
|
@@ -18,4 +18,4 @@ export declare function trapFocus(container: HTMLElement, event: KeyboardEvent):
|
|
|
18
18
|
/**
|
|
19
19
|
* Listen for click outside a target element
|
|
20
20
|
*/
|
|
21
|
-
export declare function onClickOutside(elements: Array<HTMLElement | null | undefined> | HTMLElement | null | undefined, handler: (event: MouseEvent | TouchEvent) => void): () => void;
|
|
21
|
+
export declare function onClickOutside(elements: Array<HTMLElement | null | undefined> | HTMLElement | null | undefined | (() => Array<HTMLElement | null | undefined>), handler: (event: MouseEvent | TouchEvent) => void): () => void;
|
package/dist/virtual/index.js
CHANGED
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
createVirtualizer2,
|
|
3
3
|
VirtualList2,
|
|
4
4
|
VirtualGrid2
|
|
5
|
-
} from "../index-
|
|
6
|
-
import"../index-
|
|
7
|
-
import"../index-
|
|
8
|
-
import"../index-
|
|
9
|
-
import"../index-
|
|
5
|
+
} from "../index-x6whpv54.js";
|
|
6
|
+
import"../index-3y73zgbk.js";
|
|
7
|
+
import"../index-xby428n5.js";
|
|
8
|
+
import"../index-65j7a9et.js";
|
|
9
|
+
import"../index-1yhbg34h.js";
|
|
10
10
|
export {
|
|
11
11
|
VirtualGrid2 as VirtualGrid,
|
|
12
12
|
VirtualList2 as VirtualList,
|
package/package.json
CHANGED
package/src/dom/control-flow.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
effect,
|
|
3
|
+
getActiveScope,
|
|
4
|
+
onCleanup,
|
|
5
|
+
runInScope,
|
|
6
|
+
Scope,
|
|
7
|
+
signal,
|
|
8
|
+
untrack,
|
|
9
|
+
type WritableSignal
|
|
10
|
+
} from "../signals";
|
|
2
11
|
import { type Child, insertChild, type ReactiveValue, resolveValue } from "./render";
|
|
3
12
|
|
|
4
13
|
export interface ShowProps<T> {
|
|
@@ -120,7 +129,7 @@ interface ItemRecord<T> {
|
|
|
120
129
|
item: T;
|
|
121
130
|
scope: Scope;
|
|
122
131
|
nodes: Node[];
|
|
123
|
-
indexSignal:
|
|
132
|
+
indexSignal: WritableSignal<number>;
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
/**
|
|
@@ -206,32 +215,31 @@ export function For<T>(props: ForProps<T>): any {
|
|
|
206
215
|
if (!parent) return;
|
|
207
216
|
|
|
208
217
|
const newItems: ItemRecord<T>[] = [];
|
|
209
|
-
const prevMap = new Map<T, ItemRecord<T
|
|
218
|
+
const prevMap = new Map<T, ItemRecord<T>[]>();
|
|
210
219
|
|
|
211
220
|
for (const rec of items) {
|
|
212
|
-
prevMap.
|
|
221
|
+
const group = prevMap.get(rec.item);
|
|
222
|
+
if (group) {
|
|
223
|
+
group.push(rec);
|
|
224
|
+
} else {
|
|
225
|
+
prevMap.set(rec.item, [rec]);
|
|
226
|
+
}
|
|
213
227
|
}
|
|
214
228
|
|
|
215
229
|
// Fast-path: map existing or create new item scopes
|
|
216
230
|
for (let i = 0; i < list.length; i++) {
|
|
217
231
|
const item = list[i]!;
|
|
218
|
-
const
|
|
232
|
+
const existingGroup = prevMap.get(item);
|
|
233
|
+
const existing =
|
|
234
|
+
existingGroup && existingGroup.length > 0 ? existingGroup.shift() : undefined;
|
|
219
235
|
|
|
220
236
|
if (existing) {
|
|
221
|
-
|
|
237
|
+
untrack(() => {
|
|
238
|
+
existing.indexSignal.set(i);
|
|
239
|
+
});
|
|
222
240
|
newItems.push(existing);
|
|
223
|
-
prevMap.delete(item);
|
|
224
241
|
} else {
|
|
225
|
-
|
|
226
|
-
const indexSubscribers = new Set<() => void>();
|
|
227
|
-
const indexSignal = (...args: [number?]): number => {
|
|
228
|
-
if (args.length > 0 && args[0] !== undefined && args[0] !== indexVal) {
|
|
229
|
-
indexVal = args[0];
|
|
230
|
-
for (const sub of Array.from(indexSubscribers)) sub();
|
|
231
|
-
}
|
|
232
|
-
return indexVal;
|
|
233
|
-
};
|
|
234
|
-
|
|
242
|
+
const indexSignal = signal(i, { internal: true });
|
|
235
243
|
const itemScope = new Scope(getActiveScope());
|
|
236
244
|
let itemNodes: Node[] = [];
|
|
237
245
|
|
|
@@ -254,10 +262,12 @@ export function For<T>(props: ForProps<T>): any {
|
|
|
254
262
|
}
|
|
255
263
|
|
|
256
264
|
// Clean up removed items
|
|
257
|
-
for (const [,
|
|
258
|
-
removed
|
|
259
|
-
|
|
260
|
-
|
|
265
|
+
for (const [, remainingGroup] of prevMap) {
|
|
266
|
+
for (const removed of remainingGroup) {
|
|
267
|
+
removed.scope.dispose();
|
|
268
|
+
for (const node of removed.nodes) {
|
|
269
|
+
if (node.parentNode) node.parentNode.removeChild(node);
|
|
270
|
+
}
|
|
261
271
|
}
|
|
262
272
|
}
|
|
263
273
|
|
package/src/form/form.ts
CHANGED
|
@@ -54,6 +54,32 @@ export function createForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
54
54
|
const submitCount = signal(0);
|
|
55
55
|
const customErrors = signal<Partial<Record<keyof T, string>>>({});
|
|
56
56
|
|
|
57
|
+
const getValues = (): T => {
|
|
58
|
+
const res: Record<string, any> = {};
|
|
59
|
+
for (const key in fields) {
|
|
60
|
+
res[key] = (fields as any)[key].value();
|
|
61
|
+
}
|
|
62
|
+
return res as T;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const schemaValidation = computed(() => {
|
|
66
|
+
if (!options.schema) {
|
|
67
|
+
return { success: true, issuesMap: new Map<string, string>() };
|
|
68
|
+
}
|
|
69
|
+
const currentValues = getValues();
|
|
70
|
+
const res = validateWithSchema(options.schema, currentValues);
|
|
71
|
+
const issuesMap = new Map<string, string>();
|
|
72
|
+
if (!res.success && res.issues) {
|
|
73
|
+
for (const issue of res.issues) {
|
|
74
|
+
const pathKey = String(issue.path);
|
|
75
|
+
if (!issuesMap.has(pathKey)) {
|
|
76
|
+
issuesMap.set(pathKey, issue.message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { success: res.success, issuesMap };
|
|
81
|
+
});
|
|
82
|
+
|
|
57
83
|
for (const key in initialValues) {
|
|
58
84
|
const initVal = initialValues[key];
|
|
59
85
|
const valSignal = signal(initVal);
|
|
@@ -74,25 +100,12 @@ export function createForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
74
100
|
|
|
75
101
|
if (!options.schema) return null;
|
|
76
102
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
if (!res.success && res.issues) {
|
|
80
|
-
const match = res.issues.find((i) => i.path === key || i.path === `${key}`);
|
|
81
|
-
return match ? match.message : null;
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
103
|
+
const validation = schemaValidation();
|
|
104
|
+
return validation.issuesMap.get(key) ?? validation.issuesMap.get(String(key)) ?? null;
|
|
84
105
|
})
|
|
85
106
|
};
|
|
86
107
|
}
|
|
87
108
|
|
|
88
|
-
const getValues = (): T => {
|
|
89
|
-
const res: Record<string, any> = {};
|
|
90
|
-
for (const key in fields) {
|
|
91
|
-
res[key] = (fields as any)[key].value();
|
|
92
|
-
}
|
|
93
|
-
return res as T;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
109
|
const errors = computed<Partial<Record<keyof T, string>>>(() => {
|
|
97
110
|
const errMap: Partial<Record<keyof T, string>> = {};
|
|
98
111
|
for (const key in fields) {
|
|
@@ -118,8 +131,7 @@ export function createForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
118
131
|
|
|
119
132
|
const validate = (): boolean => {
|
|
120
133
|
if (!options.schema) return true;
|
|
121
|
-
|
|
122
|
-
return res.success;
|
|
134
|
+
return schemaValidation().success;
|
|
123
135
|
};
|
|
124
136
|
|
|
125
137
|
const handleSubmit = async (e?: Event): Promise<boolean> => {
|
package/src/index.ts
CHANGED
|
@@ -405,11 +405,13 @@ export {
|
|
|
405
405
|
TooltipTrigger,
|
|
406
406
|
toast,
|
|
407
407
|
trapFocus,
|
|
408
|
+
type UseDismissOptions,
|
|
408
409
|
useAccordionContext,
|
|
409
410
|
useAccordionItemContext,
|
|
410
411
|
useBottomBarContext,
|
|
411
412
|
useCommandContext,
|
|
412
413
|
useDialogContext,
|
|
414
|
+
useDismiss,
|
|
413
415
|
useDropdownMenuContext,
|
|
414
416
|
usePopoverContext,
|
|
415
417
|
useTabsContext,
|
package/src/router/router.tsx
CHANGED
|
@@ -253,6 +253,7 @@ export function Routes(props: RoutesProps): Child {
|
|
|
253
253
|
export interface LinkProps {
|
|
254
254
|
href: string;
|
|
255
255
|
class?: string | (() => string);
|
|
256
|
+
className?: string | (() => string);
|
|
256
257
|
activeClass?: string;
|
|
257
258
|
replace?: boolean;
|
|
258
259
|
children: Child;
|
|
@@ -264,7 +265,16 @@ export interface LinkProps {
|
|
|
264
265
|
*/
|
|
265
266
|
export function Link(props: LinkProps): Child {
|
|
266
267
|
const router = useContext(RouterContext);
|
|
267
|
-
const {
|
|
268
|
+
const {
|
|
269
|
+
href,
|
|
270
|
+
class: classProp,
|
|
271
|
+
className: classNameProp,
|
|
272
|
+
activeClass = "active",
|
|
273
|
+
replace,
|
|
274
|
+
children,
|
|
275
|
+
...rest
|
|
276
|
+
} = props;
|
|
277
|
+
const rawClass = classProp ?? classNameProp;
|
|
268
278
|
|
|
269
279
|
const handleClick = (e: MouseEvent) => {
|
|
270
280
|
if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.button !== 0) {
|
|
@@ -286,7 +296,7 @@ export function Link(props: LinkProps): Child {
|
|
|
286
296
|
});
|
|
287
297
|
|
|
288
298
|
const computedClass = computed(() => {
|
|
289
|
-
const baseCls = typeof
|
|
299
|
+
const baseCls = typeof rawClass === "function" ? rawClass() : (rawClass ?? "");
|
|
290
300
|
if (activeClass && isActive()) {
|
|
291
301
|
return baseCls ? `${baseCls} ${activeClass}` : activeClass;
|
|
292
302
|
}
|
package/src/signals/effect.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { Computed } from "./computed";
|
|
|
5
5
|
import type { Scope } from "./scope";
|
|
6
6
|
import {
|
|
7
7
|
addPendingEffect,
|
|
8
|
-
batchDepth,
|
|
9
8
|
flushEffects,
|
|
9
|
+
getBatchDepth,
|
|
10
10
|
getCurrentScope,
|
|
11
11
|
getCurrentSubscriber,
|
|
12
12
|
removePendingEffect,
|
|
@@ -57,7 +57,7 @@ export class Effect implements ISubscriber {
|
|
|
57
57
|
this.state = targetState;
|
|
58
58
|
addPendingEffect(this);
|
|
59
59
|
|
|
60
|
-
if (
|
|
60
|
+
if (getBatchDepth() === 0) {
|
|
61
61
|
flushEffects();
|
|
62
62
|
}
|
|
63
63
|
}
|
package/src/signals/scope.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Visage Reactive Scope & Lifecycle Management
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { getCurrentScope, getCurrentSubscriber, setCurrentScope } from "./system";
|
|
5
5
|
import type { CleanupFn, ISubscriber } from "./types";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -14,7 +14,7 @@ export class Scope {
|
|
|
14
14
|
isDisposed = false;
|
|
15
15
|
context?: Map<symbol, any>;
|
|
16
16
|
|
|
17
|
-
constructor(parent: Scope | null =
|
|
17
|
+
constructor(parent: Scope | null = getCurrentScope()) {
|
|
18
18
|
this.parent = parent;
|
|
19
19
|
if (parent && !parent.isDisposed) {
|
|
20
20
|
parent.cleanups.push(() => this.dispose());
|
|
@@ -72,10 +72,12 @@ export class Scope {
|
|
|
72
72
|
* ```
|
|
73
73
|
*/
|
|
74
74
|
export function onCleanup(fn: CleanupFn): void {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const curSub = getCurrentSubscriber();
|
|
76
|
+
const curScope = getCurrentScope();
|
|
77
|
+
if (curSub && "addCleanup" in curSub) {
|
|
78
|
+
(curSub as any).addCleanup(fn);
|
|
79
|
+
} else if (curScope) {
|
|
80
|
+
curScope.addCleanup(fn);
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
@@ -101,7 +103,7 @@ export function onCleanup(fn: CleanupFn): void {
|
|
|
101
103
|
* @returns The return value of `fn`
|
|
102
104
|
*/
|
|
103
105
|
export function createRoot<T>(fn: (dispose: () => void) => T): T {
|
|
104
|
-
const prevScope =
|
|
106
|
+
const prevScope = getCurrentScope();
|
|
105
107
|
const scope = new Scope(null);
|
|
106
108
|
setCurrentScope(scope);
|
|
107
109
|
try {
|
|
@@ -115,7 +117,7 @@ export function createRoot<T>(fn: (dispose: () => void) => T): T {
|
|
|
115
117
|
* Run a function with a specified reactive scope active.
|
|
116
118
|
*/
|
|
117
119
|
export function runInScope<T>(scope: Scope | null, fn: () => T): T {
|
|
118
|
-
const prevScope =
|
|
120
|
+
const prevScope = getCurrentScope();
|
|
119
121
|
setCurrentScope(scope);
|
|
120
122
|
try {
|
|
121
123
|
return fn();
|
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) {
|