@deadragdoll/reactnu 0.1.75 → 0.1.77
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 +128 -14
- package/dist/index.js +128 -14
- 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,94 @@ 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
|
+
}
|
|
3180
|
+
var WINDOW_STORE_WRITE_DEBOUNCE_MS = 250;
|
|
3127
3181
|
function resolveSavedWindowStyle(definition, mode, ownerCenteredStyle, index) {
|
|
3128
|
-
const snapshot = definition.onOpen?.();
|
|
3129
|
-
const savedStyle =
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3182
|
+
const snapshot = definition.onOpen?.() ?? readStoredWindowSnapshot(definition.windowStoreKey);
|
|
3183
|
+
const savedStyle = {};
|
|
3184
|
+
if (snapshot) {
|
|
3185
|
+
if (snapshot.height !== void 0) {
|
|
3186
|
+
savedStyle.height = snapshot.height;
|
|
3187
|
+
}
|
|
3188
|
+
if (snapshot.left !== void 0) {
|
|
3189
|
+
savedStyle.left = snapshot.left;
|
|
3190
|
+
}
|
|
3191
|
+
if (snapshot.top !== void 0) {
|
|
3192
|
+
savedStyle.top = snapshot.top;
|
|
3193
|
+
}
|
|
3194
|
+
if (snapshot.width !== void 0) {
|
|
3195
|
+
savedStyle.width = snapshot.width;
|
|
3196
|
+
}
|
|
3197
|
+
if (snapshot.left !== void 0 || snapshot.top !== void 0) {
|
|
3198
|
+
savedStyle.transform = "none";
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3136
3201
|
return {
|
|
3137
3202
|
maximized: snapshot?.maximized === true,
|
|
3138
3203
|
minimized: snapshot?.minimized === true,
|
|
3139
|
-
restoreStyle: snapshot?.maximized
|
|
3204
|
+
restoreStyle: snapshot?.maximized ? {
|
|
3140
3205
|
...getDefaultWindowStyle(mode, index),
|
|
3141
3206
|
...ownerCenteredStyle,
|
|
3207
|
+
...definition.style,
|
|
3142
3208
|
...savedStyle
|
|
3143
3209
|
} : void 0,
|
|
3144
3210
|
style: {
|
|
3145
3211
|
...getDefaultWindowStyle(mode, index),
|
|
3146
3212
|
...ownerCenteredStyle,
|
|
3147
|
-
...
|
|
3148
|
-
...
|
|
3213
|
+
...definition.style,
|
|
3214
|
+
...savedStyle
|
|
3149
3215
|
}
|
|
3150
3216
|
};
|
|
3151
3217
|
}
|
|
@@ -3250,6 +3316,7 @@ function NuWindowProvider({
|
|
|
3250
3316
|
const creationOrderRef = (0, import_react19.useRef)(0);
|
|
3251
3317
|
const idRef = (0, import_react19.useRef)(0);
|
|
3252
3318
|
const windowBoundsByIdRef = (0, import_react19.useRef)({});
|
|
3319
|
+
const pendingWindowStoreWritesRef = (0, import_react19.useRef)(/* @__PURE__ */ new Map());
|
|
3253
3320
|
const [windows, setWindows] = (0, import_react19.useState)([]);
|
|
3254
3321
|
const [windowBoundsById, setWindowBoundsById] = (0, import_react19.useState)({});
|
|
3255
3322
|
const appHostMenuContext = (0, import_react19.useContext)(AppHostMenuContext);
|
|
@@ -3671,6 +3738,53 @@ function NuWindowProvider({
|
|
|
3671
3738
|
(0, import_react19.useEffect)(() => {
|
|
3672
3739
|
onAppModalChange?.(hasAppModal);
|
|
3673
3740
|
}, [hasAppModal, onAppModalChange]);
|
|
3741
|
+
(0, import_react19.useEffect)(() => {
|
|
3742
|
+
const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
|
|
3743
|
+
const windowStoreKeyById = new Map(
|
|
3744
|
+
windows.map((windowEntry) => [windowEntry.id, windowEntry.windowStoreKey])
|
|
3745
|
+
);
|
|
3746
|
+
for (const [id, pending] of pendingWindowStoreWrites) {
|
|
3747
|
+
if (windowStoreKeyById.get(id) === pending.windowStoreKey) {
|
|
3748
|
+
continue;
|
|
3749
|
+
}
|
|
3750
|
+
clearTimeout(pending.timeoutId);
|
|
3751
|
+
pendingWindowStoreWrites.delete(id);
|
|
3752
|
+
writeStoredWindowSnapshot(pending.windowStoreKey, pending.snapshot);
|
|
3753
|
+
}
|
|
3754
|
+
for (const windowEntry of windows) {
|
|
3755
|
+
if (!windowEntry.windowStoreKey) {
|
|
3756
|
+
continue;
|
|
3757
|
+
}
|
|
3758
|
+
const existing = pendingWindowStoreWrites.get(windowEntry.id);
|
|
3759
|
+
if (existing) {
|
|
3760
|
+
clearTimeout(existing.timeoutId);
|
|
3761
|
+
}
|
|
3762
|
+
const windowStoreKey = windowEntry.windowStoreKey;
|
|
3763
|
+
const snapshot = resolveManagedWindowSnapshot(
|
|
3764
|
+
windowEntry,
|
|
3765
|
+
windowBoundsById
|
|
3766
|
+
);
|
|
3767
|
+
const timeoutId = setTimeout(() => {
|
|
3768
|
+
writeStoredWindowSnapshot(windowStoreKey, snapshot);
|
|
3769
|
+
pendingWindowStoreWrites.delete(windowEntry.id);
|
|
3770
|
+
}, WINDOW_STORE_WRITE_DEBOUNCE_MS);
|
|
3771
|
+
pendingWindowStoreWrites.set(windowEntry.id, {
|
|
3772
|
+
timeoutId,
|
|
3773
|
+
windowStoreKey,
|
|
3774
|
+
snapshot
|
|
3775
|
+
});
|
|
3776
|
+
}
|
|
3777
|
+
}, [windowBoundsById, windows]);
|
|
3778
|
+
(0, import_react19.useEffect)(() => {
|
|
3779
|
+
const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
|
|
3780
|
+
return () => {
|
|
3781
|
+
for (const entry of pendingWindowStoreWrites.values()) {
|
|
3782
|
+
clearTimeout(entry.timeoutId);
|
|
3783
|
+
writeStoredWindowSnapshot(entry.windowStoreKey, entry.snapshot);
|
|
3784
|
+
}
|
|
3785
|
+
pendingWindowStoreWrites.clear();
|
|
3786
|
+
};
|
|
3787
|
+
}, []);
|
|
3674
3788
|
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
3789
|
children,
|
|
3676
3790
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "nu-window-layer", children: renderWindows.map((windowEntry) => {
|
|
@@ -4970,7 +5084,7 @@ function NuIconGridItem({
|
|
|
4970
5084
|
icon.contextMenuItems,
|
|
4971
5085
|
icon
|
|
4972
5086
|
);
|
|
4973
|
-
const
|
|
5087
|
+
const { dragRef, isDragging } = useNuDragSource({
|
|
4974
5088
|
disabled: icon.disabled,
|
|
4975
5089
|
getItem: () => ({ data: icon, id: icon.id, type: "icon" }),
|
|
4976
5090
|
onDropAccepted: (result) => {
|
|
@@ -5013,14 +5127,14 @@ function NuIconGridItem({
|
|
|
5013
5127
|
{
|
|
5014
5128
|
"aria-haspopup": contextMenuItems.length > 0 ? "menu" : void 0,
|
|
5015
5129
|
className: "nu-icon-grid__icon",
|
|
5016
|
-
"data-dragging":
|
|
5130
|
+
"data-dragging": isDragging || void 0,
|
|
5017
5131
|
"data-selected": manager.selectedIconId === icon.id || void 0,
|
|
5018
5132
|
disabled: icon.disabled,
|
|
5019
5133
|
onClick: handleClick,
|
|
5020
5134
|
onContextMenu: handleContextMenu,
|
|
5021
5135
|
onDoubleClick: icon.onDoubleClick,
|
|
5022
5136
|
onKeyDown: handleKeyDown,
|
|
5023
|
-
ref:
|
|
5137
|
+
ref: (node) => dragRef(node),
|
|
5024
5138
|
style: { left: icon.position.x, top: icon.position.y },
|
|
5025
5139
|
type: "button",
|
|
5026
5140
|
children: [
|
package/dist/index.js
CHANGED
|
@@ -3072,28 +3072,94 @@ 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
|
+
}
|
|
3128
|
+
var WINDOW_STORE_WRITE_DEBOUNCE_MS = 250;
|
|
3075
3129
|
function resolveSavedWindowStyle(definition, mode, ownerCenteredStyle, index) {
|
|
3076
|
-
const snapshot = definition.onOpen?.();
|
|
3077
|
-
const savedStyle =
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3130
|
+
const snapshot = definition.onOpen?.() ?? readStoredWindowSnapshot(definition.windowStoreKey);
|
|
3131
|
+
const savedStyle = {};
|
|
3132
|
+
if (snapshot) {
|
|
3133
|
+
if (snapshot.height !== void 0) {
|
|
3134
|
+
savedStyle.height = snapshot.height;
|
|
3135
|
+
}
|
|
3136
|
+
if (snapshot.left !== void 0) {
|
|
3137
|
+
savedStyle.left = snapshot.left;
|
|
3138
|
+
}
|
|
3139
|
+
if (snapshot.top !== void 0) {
|
|
3140
|
+
savedStyle.top = snapshot.top;
|
|
3141
|
+
}
|
|
3142
|
+
if (snapshot.width !== void 0) {
|
|
3143
|
+
savedStyle.width = snapshot.width;
|
|
3144
|
+
}
|
|
3145
|
+
if (snapshot.left !== void 0 || snapshot.top !== void 0) {
|
|
3146
|
+
savedStyle.transform = "none";
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3084
3149
|
return {
|
|
3085
3150
|
maximized: snapshot?.maximized === true,
|
|
3086
3151
|
minimized: snapshot?.minimized === true,
|
|
3087
|
-
restoreStyle: snapshot?.maximized
|
|
3152
|
+
restoreStyle: snapshot?.maximized ? {
|
|
3088
3153
|
...getDefaultWindowStyle(mode, index),
|
|
3089
3154
|
...ownerCenteredStyle,
|
|
3155
|
+
...definition.style,
|
|
3090
3156
|
...savedStyle
|
|
3091
3157
|
} : void 0,
|
|
3092
3158
|
style: {
|
|
3093
3159
|
...getDefaultWindowStyle(mode, index),
|
|
3094
3160
|
...ownerCenteredStyle,
|
|
3095
|
-
...
|
|
3096
|
-
...
|
|
3161
|
+
...definition.style,
|
|
3162
|
+
...savedStyle
|
|
3097
3163
|
}
|
|
3098
3164
|
};
|
|
3099
3165
|
}
|
|
@@ -3198,6 +3264,7 @@ function NuWindowProvider({
|
|
|
3198
3264
|
const creationOrderRef = useRef8(0);
|
|
3199
3265
|
const idRef = useRef8(0);
|
|
3200
3266
|
const windowBoundsByIdRef = useRef8({});
|
|
3267
|
+
const pendingWindowStoreWritesRef = useRef8(/* @__PURE__ */ new Map());
|
|
3201
3268
|
const [windows, setWindows] = useState10([]);
|
|
3202
3269
|
const [windowBoundsById, setWindowBoundsById] = useState10({});
|
|
3203
3270
|
const appHostMenuContext = useContext7(AppHostMenuContext);
|
|
@@ -3619,6 +3686,53 @@ function NuWindowProvider({
|
|
|
3619
3686
|
useEffect6(() => {
|
|
3620
3687
|
onAppModalChange?.(hasAppModal);
|
|
3621
3688
|
}, [hasAppModal, onAppModalChange]);
|
|
3689
|
+
useEffect6(() => {
|
|
3690
|
+
const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
|
|
3691
|
+
const windowStoreKeyById = new Map(
|
|
3692
|
+
windows.map((windowEntry) => [windowEntry.id, windowEntry.windowStoreKey])
|
|
3693
|
+
);
|
|
3694
|
+
for (const [id, pending] of pendingWindowStoreWrites) {
|
|
3695
|
+
if (windowStoreKeyById.get(id) === pending.windowStoreKey) {
|
|
3696
|
+
continue;
|
|
3697
|
+
}
|
|
3698
|
+
clearTimeout(pending.timeoutId);
|
|
3699
|
+
pendingWindowStoreWrites.delete(id);
|
|
3700
|
+
writeStoredWindowSnapshot(pending.windowStoreKey, pending.snapshot);
|
|
3701
|
+
}
|
|
3702
|
+
for (const windowEntry of windows) {
|
|
3703
|
+
if (!windowEntry.windowStoreKey) {
|
|
3704
|
+
continue;
|
|
3705
|
+
}
|
|
3706
|
+
const existing = pendingWindowStoreWrites.get(windowEntry.id);
|
|
3707
|
+
if (existing) {
|
|
3708
|
+
clearTimeout(existing.timeoutId);
|
|
3709
|
+
}
|
|
3710
|
+
const windowStoreKey = windowEntry.windowStoreKey;
|
|
3711
|
+
const snapshot = resolveManagedWindowSnapshot(
|
|
3712
|
+
windowEntry,
|
|
3713
|
+
windowBoundsById
|
|
3714
|
+
);
|
|
3715
|
+
const timeoutId = setTimeout(() => {
|
|
3716
|
+
writeStoredWindowSnapshot(windowStoreKey, snapshot);
|
|
3717
|
+
pendingWindowStoreWrites.delete(windowEntry.id);
|
|
3718
|
+
}, WINDOW_STORE_WRITE_DEBOUNCE_MS);
|
|
3719
|
+
pendingWindowStoreWrites.set(windowEntry.id, {
|
|
3720
|
+
timeoutId,
|
|
3721
|
+
windowStoreKey,
|
|
3722
|
+
snapshot
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
}, [windowBoundsById, windows]);
|
|
3726
|
+
useEffect6(() => {
|
|
3727
|
+
const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
|
|
3728
|
+
return () => {
|
|
3729
|
+
for (const entry of pendingWindowStoreWrites.values()) {
|
|
3730
|
+
clearTimeout(entry.timeoutId);
|
|
3731
|
+
writeStoredWindowSnapshot(entry.windowStoreKey, entry.snapshot);
|
|
3732
|
+
}
|
|
3733
|
+
pendingWindowStoreWrites.clear();
|
|
3734
|
+
};
|
|
3735
|
+
}, []);
|
|
3622
3736
|
return /* @__PURE__ */ jsx29(NuWindowContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs17("div", { className: ["nu-window-host", className].filter(Boolean).join(" "), children: [
|
|
3623
3737
|
children,
|
|
3624
3738
|
/* @__PURE__ */ jsx29("div", { className: "nu-window-layer", children: renderWindows.map((windowEntry) => {
|
|
@@ -4935,7 +5049,7 @@ function NuIconGridItem({
|
|
|
4935
5049
|
icon.contextMenuItems,
|
|
4936
5050
|
icon
|
|
4937
5051
|
);
|
|
4938
|
-
const
|
|
5052
|
+
const { dragRef, isDragging } = useNuDragSource({
|
|
4939
5053
|
disabled: icon.disabled,
|
|
4940
5054
|
getItem: () => ({ data: icon, id: icon.id, type: "icon" }),
|
|
4941
5055
|
onDropAccepted: (result) => {
|
|
@@ -4978,14 +5092,14 @@ function NuIconGridItem({
|
|
|
4978
5092
|
{
|
|
4979
5093
|
"aria-haspopup": contextMenuItems.length > 0 ? "menu" : void 0,
|
|
4980
5094
|
className: "nu-icon-grid__icon",
|
|
4981
|
-
"data-dragging":
|
|
5095
|
+
"data-dragging": isDragging || void 0,
|
|
4982
5096
|
"data-selected": manager.selectedIconId === icon.id || void 0,
|
|
4983
5097
|
disabled: icon.disabled,
|
|
4984
5098
|
onClick: handleClick,
|
|
4985
5099
|
onContextMenu: handleContextMenu,
|
|
4986
5100
|
onDoubleClick: icon.onDoubleClick,
|
|
4987
5101
|
onKeyDown: handleKeyDown,
|
|
4988
|
-
ref:
|
|
5102
|
+
ref: (node) => dragRef(node),
|
|
4989
5103
|
style: { left: icon.position.x, top: icon.position.y },
|
|
4990
5104
|
type: "button",
|
|
4991
5105
|
children: [
|
|
@@ -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.
|