@deadragdoll/reactnu 0.1.75 → 0.1.76
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/index.cjs +87 -11
- package/dist/index.js +87 -11
- package/dist/windowing/windowing.types.d.ts +5 -0
- package/docs/ARCHITECTURE.md +2 -0
- package/docs/NuWindowProvider.md +20 -1
- package/docs/Window.md +4 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3124,28 +3124,93 @@ function getNumericStyleValue(value) {
|
|
|
3124
3124
|
}
|
|
3125
3125
|
return void 0;
|
|
3126
3126
|
}
|
|
3127
|
+
function getWindowStorageKey(windowStoreKey) {
|
|
3128
|
+
const normalizedKey = windowStoreKey?.trim();
|
|
3129
|
+
return normalizedKey ? `reactnu.window.${normalizedKey}` : null;
|
|
3130
|
+
}
|
|
3131
|
+
function readStoredWindowSnapshot(windowStoreKey) {
|
|
3132
|
+
const storageKey = getWindowStorageKey(windowStoreKey);
|
|
3133
|
+
if (!storageKey || typeof window === "undefined") {
|
|
3134
|
+
return void 0;
|
|
3135
|
+
}
|
|
3136
|
+
try {
|
|
3137
|
+
const rawValue = window.localStorage.getItem(storageKey);
|
|
3138
|
+
if (!rawValue) {
|
|
3139
|
+
return void 0;
|
|
3140
|
+
}
|
|
3141
|
+
const parsedValue = JSON.parse(rawValue);
|
|
3142
|
+
if (!parsedValue || typeof parsedValue !== "object") {
|
|
3143
|
+
return void 0;
|
|
3144
|
+
}
|
|
3145
|
+
const storedValue = parsedValue;
|
|
3146
|
+
const snapshot = {};
|
|
3147
|
+
for (const property of ["height", "width"]) {
|
|
3148
|
+
const value = storedValue[property];
|
|
3149
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3150
|
+
snapshot[property] = value;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
for (const property of ["left", "top"]) {
|
|
3154
|
+
const value = storedValue[property];
|
|
3155
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
3156
|
+
snapshot[property] = value;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
for (const property of ["maximized", "minimized"]) {
|
|
3160
|
+
const value = storedValue[property];
|
|
3161
|
+
if (typeof value === "boolean") {
|
|
3162
|
+
snapshot[property] = value;
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
return Object.keys(snapshot).length > 0 ? snapshot : void 0;
|
|
3166
|
+
} catch {
|
|
3167
|
+
return void 0;
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
function writeStoredWindowSnapshot(windowStoreKey, snapshot) {
|
|
3171
|
+
const storageKey = getWindowStorageKey(windowStoreKey);
|
|
3172
|
+
if (!storageKey || typeof window === "undefined") {
|
|
3173
|
+
return;
|
|
3174
|
+
}
|
|
3175
|
+
try {
|
|
3176
|
+
window.localStorage.setItem(storageKey, JSON.stringify(snapshot));
|
|
3177
|
+
} catch {
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3127
3180
|
function resolveSavedWindowStyle(definition, mode, ownerCenteredStyle, index) {
|
|
3128
|
-
const snapshot = definition.onOpen?.();
|
|
3129
|
-
const savedStyle =
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3181
|
+
const snapshot = definition.onOpen?.() ?? readStoredWindowSnapshot(definition.windowStoreKey);
|
|
3182
|
+
const savedStyle = {};
|
|
3183
|
+
if (snapshot) {
|
|
3184
|
+
if (snapshot.height !== void 0) {
|
|
3185
|
+
savedStyle.height = snapshot.height;
|
|
3186
|
+
}
|
|
3187
|
+
if (snapshot.left !== void 0) {
|
|
3188
|
+
savedStyle.left = snapshot.left;
|
|
3189
|
+
}
|
|
3190
|
+
if (snapshot.top !== void 0) {
|
|
3191
|
+
savedStyle.top = snapshot.top;
|
|
3192
|
+
}
|
|
3193
|
+
if (snapshot.width !== void 0) {
|
|
3194
|
+
savedStyle.width = snapshot.width;
|
|
3195
|
+
}
|
|
3196
|
+
if (snapshot.left !== void 0 || snapshot.top !== void 0) {
|
|
3197
|
+
savedStyle.transform = "none";
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3136
3200
|
return {
|
|
3137
3201
|
maximized: snapshot?.maximized === true,
|
|
3138
3202
|
minimized: snapshot?.minimized === true,
|
|
3139
|
-
restoreStyle: snapshot?.maximized
|
|
3203
|
+
restoreStyle: snapshot?.maximized ? {
|
|
3140
3204
|
...getDefaultWindowStyle(mode, index),
|
|
3141
3205
|
...ownerCenteredStyle,
|
|
3206
|
+
...definition.style,
|
|
3142
3207
|
...savedStyle
|
|
3143
3208
|
} : void 0,
|
|
3144
3209
|
style: {
|
|
3145
3210
|
...getDefaultWindowStyle(mode, index),
|
|
3146
3211
|
...ownerCenteredStyle,
|
|
3147
|
-
...
|
|
3148
|
-
...
|
|
3212
|
+
...definition.style,
|
|
3213
|
+
...savedStyle
|
|
3149
3214
|
}
|
|
3150
3215
|
};
|
|
3151
3216
|
}
|
|
@@ -3671,6 +3736,17 @@ function NuWindowProvider({
|
|
|
3671
3736
|
(0, import_react19.useEffect)(() => {
|
|
3672
3737
|
onAppModalChange?.(hasAppModal);
|
|
3673
3738
|
}, [hasAppModal, onAppModalChange]);
|
|
3739
|
+
(0, import_react19.useEffect)(() => {
|
|
3740
|
+
for (const windowEntry of windows) {
|
|
3741
|
+
if (!windowEntry.windowStoreKey) {
|
|
3742
|
+
continue;
|
|
3743
|
+
}
|
|
3744
|
+
writeStoredWindowSnapshot(
|
|
3745
|
+
windowEntry.windowStoreKey,
|
|
3746
|
+
resolveManagedWindowSnapshot(windowEntry, windowBoundsById)
|
|
3747
|
+
);
|
|
3748
|
+
}
|
|
3749
|
+
}, [windowBoundsById, windows]);
|
|
3674
3750
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(NuWindowContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: ["nu-window-host", className].filter(Boolean).join(" "), children: [
|
|
3675
3751
|
children,
|
|
3676
3752
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "nu-window-layer", children: renderWindows.map((windowEntry) => {
|
package/dist/index.js
CHANGED
|
@@ -3072,28 +3072,93 @@ function getNumericStyleValue(value) {
|
|
|
3072
3072
|
}
|
|
3073
3073
|
return void 0;
|
|
3074
3074
|
}
|
|
3075
|
+
function getWindowStorageKey(windowStoreKey) {
|
|
3076
|
+
const normalizedKey = windowStoreKey?.trim();
|
|
3077
|
+
return normalizedKey ? `reactnu.window.${normalizedKey}` : null;
|
|
3078
|
+
}
|
|
3079
|
+
function readStoredWindowSnapshot(windowStoreKey) {
|
|
3080
|
+
const storageKey = getWindowStorageKey(windowStoreKey);
|
|
3081
|
+
if (!storageKey || typeof window === "undefined") {
|
|
3082
|
+
return void 0;
|
|
3083
|
+
}
|
|
3084
|
+
try {
|
|
3085
|
+
const rawValue = window.localStorage.getItem(storageKey);
|
|
3086
|
+
if (!rawValue) {
|
|
3087
|
+
return void 0;
|
|
3088
|
+
}
|
|
3089
|
+
const parsedValue = JSON.parse(rawValue);
|
|
3090
|
+
if (!parsedValue || typeof parsedValue !== "object") {
|
|
3091
|
+
return void 0;
|
|
3092
|
+
}
|
|
3093
|
+
const storedValue = parsedValue;
|
|
3094
|
+
const snapshot = {};
|
|
3095
|
+
for (const property of ["height", "width"]) {
|
|
3096
|
+
const value = storedValue[property];
|
|
3097
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3098
|
+
snapshot[property] = value;
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
for (const property of ["left", "top"]) {
|
|
3102
|
+
const value = storedValue[property];
|
|
3103
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
3104
|
+
snapshot[property] = value;
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
for (const property of ["maximized", "minimized"]) {
|
|
3108
|
+
const value = storedValue[property];
|
|
3109
|
+
if (typeof value === "boolean") {
|
|
3110
|
+
snapshot[property] = value;
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
return Object.keys(snapshot).length > 0 ? snapshot : void 0;
|
|
3114
|
+
} catch {
|
|
3115
|
+
return void 0;
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
function writeStoredWindowSnapshot(windowStoreKey, snapshot) {
|
|
3119
|
+
const storageKey = getWindowStorageKey(windowStoreKey);
|
|
3120
|
+
if (!storageKey || typeof window === "undefined") {
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
try {
|
|
3124
|
+
window.localStorage.setItem(storageKey, JSON.stringify(snapshot));
|
|
3125
|
+
} catch {
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3075
3128
|
function resolveSavedWindowStyle(definition, mode, ownerCenteredStyle, index) {
|
|
3076
|
-
const snapshot = definition.onOpen?.();
|
|
3077
|
-
const savedStyle =
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3129
|
+
const snapshot = definition.onOpen?.() ?? readStoredWindowSnapshot(definition.windowStoreKey);
|
|
3130
|
+
const savedStyle = {};
|
|
3131
|
+
if (snapshot) {
|
|
3132
|
+
if (snapshot.height !== void 0) {
|
|
3133
|
+
savedStyle.height = snapshot.height;
|
|
3134
|
+
}
|
|
3135
|
+
if (snapshot.left !== void 0) {
|
|
3136
|
+
savedStyle.left = snapshot.left;
|
|
3137
|
+
}
|
|
3138
|
+
if (snapshot.top !== void 0) {
|
|
3139
|
+
savedStyle.top = snapshot.top;
|
|
3140
|
+
}
|
|
3141
|
+
if (snapshot.width !== void 0) {
|
|
3142
|
+
savedStyle.width = snapshot.width;
|
|
3143
|
+
}
|
|
3144
|
+
if (snapshot.left !== void 0 || snapshot.top !== void 0) {
|
|
3145
|
+
savedStyle.transform = "none";
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3084
3148
|
return {
|
|
3085
3149
|
maximized: snapshot?.maximized === true,
|
|
3086
3150
|
minimized: snapshot?.minimized === true,
|
|
3087
|
-
restoreStyle: snapshot?.maximized
|
|
3151
|
+
restoreStyle: snapshot?.maximized ? {
|
|
3088
3152
|
...getDefaultWindowStyle(mode, index),
|
|
3089
3153
|
...ownerCenteredStyle,
|
|
3154
|
+
...definition.style,
|
|
3090
3155
|
...savedStyle
|
|
3091
3156
|
} : void 0,
|
|
3092
3157
|
style: {
|
|
3093
3158
|
...getDefaultWindowStyle(mode, index),
|
|
3094
3159
|
...ownerCenteredStyle,
|
|
3095
|
-
...
|
|
3096
|
-
...
|
|
3160
|
+
...definition.style,
|
|
3161
|
+
...savedStyle
|
|
3097
3162
|
}
|
|
3098
3163
|
};
|
|
3099
3164
|
}
|
|
@@ -3619,6 +3684,17 @@ function NuWindowProvider({
|
|
|
3619
3684
|
useEffect6(() => {
|
|
3620
3685
|
onAppModalChange?.(hasAppModal);
|
|
3621
3686
|
}, [hasAppModal, onAppModalChange]);
|
|
3687
|
+
useEffect6(() => {
|
|
3688
|
+
for (const windowEntry of windows) {
|
|
3689
|
+
if (!windowEntry.windowStoreKey) {
|
|
3690
|
+
continue;
|
|
3691
|
+
}
|
|
3692
|
+
writeStoredWindowSnapshot(
|
|
3693
|
+
windowEntry.windowStoreKey,
|
|
3694
|
+
resolveManagedWindowSnapshot(windowEntry, windowBoundsById)
|
|
3695
|
+
);
|
|
3696
|
+
}
|
|
3697
|
+
}, [windowBoundsById, windows]);
|
|
3622
3698
|
return /* @__PURE__ */ jsx29(NuWindowContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs17("div", { className: ["nu-window-host", className].filter(Boolean).join(" "), children: [
|
|
3623
3699
|
children,
|
|
3624
3700
|
/* @__PURE__ */ jsx29("div", { className: "nu-window-layer", children: renderWindows.map((windowEntry) => {
|
|
@@ -39,6 +39,11 @@ export type NuManagedWindowDefinition = {
|
|
|
39
39
|
style?: CSSProperties;
|
|
40
40
|
titleButtons?: WindowTitleButtonDefinition[];
|
|
41
41
|
title: string;
|
|
42
|
+
/**
|
|
43
|
+
* Persists this managed window's geometry and minimized/maximized state in
|
|
44
|
+
* localStorage. The key is scoped to `reactnu.window.`.
|
|
45
|
+
*/
|
|
46
|
+
windowStoreKey?: string;
|
|
42
47
|
};
|
|
43
48
|
export type NuManagedWindowControls = {
|
|
44
49
|
bringToFront: () => void;
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -126,6 +126,8 @@ The host owns:
|
|
|
126
126
|
|
|
127
127
|
Managed windows can persist geometry with:
|
|
128
128
|
|
|
129
|
+
- `windowStoreKey?: string` for automatic browser-local persistence under
|
|
130
|
+
`reactnu.window.<key>`
|
|
129
131
|
- `onOpen?: () => NuManagedWindowSnapshot | void`
|
|
130
132
|
- `onClose?: (snapshot) => boolean | void`
|
|
131
133
|
|
package/docs/NuWindowProvider.md
CHANGED
|
@@ -29,7 +29,26 @@ Use `useNuWindowManager()` to access:
|
|
|
29
29
|
|
|
30
30
|
## Managed Window Persistence
|
|
31
31
|
|
|
32
|
-
`
|
|
32
|
+
Set `windowStoreKey` to persist a managed window's last position, size, and
|
|
33
|
+
minimized/maximized state automatically. ReactNU stores the snapshot in
|
|
34
|
+
`localStorage` as `reactnu.window.<key>`. Use a stable, unique key for each
|
|
35
|
+
logical window; re-opening that window with the same key restores its saved
|
|
36
|
+
geometry.
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
windowManager.openWindow({
|
|
40
|
+
content: <Inspector />,
|
|
41
|
+
title: "Inspector",
|
|
42
|
+
windowStoreKey: "workspace.inspector"
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Browser storage is optional: malformed or unavailable storage is ignored.
|
|
47
|
+
`onOpen` remains the explicit override for applications that restore state
|
|
48
|
+
from their own storage, and `onClose` still receives the current snapshot.
|
|
49
|
+
Clear `reactnu.window.workspace.inspector` to reset this example.
|
|
50
|
+
|
|
51
|
+
For host-owned persistence, `NuManagedWindowDefinition` also supports:
|
|
33
52
|
|
|
34
53
|
- `onOpen?: () => NuManagedWindowSnapshot | void`
|
|
35
54
|
- `onClose?: (snapshot: NuManagedWindowSnapshot) => boolean | void`
|
package/docs/Window.md
CHANGED
|
@@ -56,6 +56,10 @@ to `useNuWindowManager().openWindow(...)`; it is resolved by
|
|
|
56
56
|
`NuWindowProvider`, rather than by a standalone `Window` instance. See
|
|
57
57
|
[NuWindowProvider](./NuWindowProvider.md#linked-activation).
|
|
58
58
|
|
|
59
|
+
`windowStoreKey?: string` is also available on a managed window definition.
|
|
60
|
+
It automatically restores and saves that window's geometry in browser storage;
|
|
61
|
+
see [NuWindowProvider](./NuWindowProvider.md#managed-window-persistence).
|
|
62
|
+
|
|
59
63
|
## Notes
|
|
60
64
|
|
|
61
65
|
- Drag and resize are implemented with pointer events and local DOM updates for smoother motion.
|