@tearleads/windowing 0.1.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.
Files changed (148) hide show
  1. package/README.md +155 -0
  2. package/createRequiredContext.d.ts +16 -0
  3. package/createRequiredContext.js +22 -0
  4. package/createRequiredContext.js.map +1 -0
  5. package/index.d.ts +13 -0
  6. package/index.js +17 -0
  7. package/index.js.map +1 -0
  8. package/menu/Menu.css +105 -0
  9. package/menu/Menu.d.ts +11 -0
  10. package/menu/Menu.js +175 -0
  11. package/menu/Menu.js.map +1 -0
  12. package/menu/MenuItem.d.ts +9 -0
  13. package/menu/MenuItem.js +5 -0
  14. package/menu/MenuItem.js.map +1 -0
  15. package/menu/useContextMenuState.d.ts +20 -0
  16. package/menu/useContextMenuState.js +45 -0
  17. package/menu/useContextMenuState.js.map +1 -0
  18. package/menu/useMenuKeyboard.d.ts +2 -0
  19. package/menu/useMenuKeyboard.js +90 -0
  20. package/menu/useMenuKeyboard.js.map +1 -0
  21. package/package.json +27 -0
  22. package/sidebar/SidebarResize.css +31 -0
  23. package/sidebar/SidebarResize.d.ts +18 -0
  24. package/sidebar/SidebarResize.js +115 -0
  25. package/sidebar/SidebarResize.js.map +1 -0
  26. package/tokens.css +63 -0
  27. package/window/CurrentWindowContext.d.ts +27 -0
  28. package/window/CurrentWindowContext.js +31 -0
  29. package/window/CurrentWindowContext.js.map +1 -0
  30. package/window/Window.css +32 -0
  31. package/window/Window.d.ts +11 -0
  32. package/window/Window.js +206 -0
  33. package/window/Window.js.map +1 -0
  34. package/window/WindowBody.css +21 -0
  35. package/window/WindowBody.d.ts +8 -0
  36. package/window/WindowBody.js +10 -0
  37. package/window/WindowBody.js.map +1 -0
  38. package/window/WindowChromeActions.d.ts +55 -0
  39. package/window/WindowChromeActions.js +61 -0
  40. package/window/WindowChromeActions.js.map +1 -0
  41. package/window/WindowCloseButton.d.ts +6 -0
  42. package/window/WindowCloseButton.js +9 -0
  43. package/window/WindowCloseButton.js.map +1 -0
  44. package/window/WindowMaximizeButton.d.ts +3 -0
  45. package/window/WindowMaximizeButton.js +5 -0
  46. package/window/WindowMaximizeButton.js.map +1 -0
  47. package/window/WindowMenuBar.css +63 -0
  48. package/window/WindowMenuBar.d.ts +14 -0
  49. package/window/WindowMenuBar.js +37 -0
  50. package/window/WindowMenuBar.js.map +1 -0
  51. package/window/WindowMenuContext.d.ts +46 -0
  52. package/window/WindowMenuContext.js +208 -0
  53. package/window/WindowMenuContext.js.map +1 -0
  54. package/window/WindowMinimizeButton.d.ts +3 -0
  55. package/window/WindowMinimizeButton.js +5 -0
  56. package/window/WindowMinimizeButton.js.map +1 -0
  57. package/window/WindowResizeHandle.css +65 -0
  58. package/window/WindowResizeHandle.d.ts +5 -0
  59. package/window/WindowResizeHandle.js +6 -0
  60. package/window/WindowResizeHandle.js.map +1 -0
  61. package/window/WindowSidebar.css +37 -0
  62. package/window/WindowSidebar.d.ts +6 -0
  63. package/window/WindowSidebar.js +9 -0
  64. package/window/WindowSidebar.js.map +1 -0
  65. package/window/WindowSidebarContext.d.ts +14 -0
  66. package/window/WindowSidebarContext.js +43 -0
  67. package/window/WindowSidebarContext.js.map +1 -0
  68. package/window/WindowStateProvider/WindowStateProvider.d.ts +2 -0
  69. package/window/WindowStateProvider/WindowStateProvider.js +16 -0
  70. package/window/WindowStateProvider/WindowStateProvider.js.map +1 -0
  71. package/window/WindowStateProvider/context.d.ts +4 -0
  72. package/window/WindowStateProvider/context.js +7 -0
  73. package/window/WindowStateProvider/context.js.map +1 -0
  74. package/window/WindowStateProvider/index.d.ts +5 -0
  75. package/window/WindowStateProvider/index.js +5 -0
  76. package/window/WindowStateProvider/index.js.map +1 -0
  77. package/window/WindowStateProvider/types.d.ts +58 -0
  78. package/window/WindowStateProvider/types.js +2 -0
  79. package/window/WindowStateProvider/types.js.map +1 -0
  80. package/window/WindowStateProvider/useWindowActions.d.ts +1 -0
  81. package/window/WindowStateProvider/useWindowActions.js +3 -0
  82. package/window/WindowStateProvider/useWindowActions.js.map +1 -0
  83. package/window/WindowStateProvider/useWindowStateActions.d.ts +24 -0
  84. package/window/WindowStateProvider/useWindowStateActions.js +176 -0
  85. package/window/WindowStateProvider/useWindowStateActions.js.map +1 -0
  86. package/window/WindowStateProvider/useWindowStateData.d.ts +1 -0
  87. package/window/WindowStateProvider/useWindowStateData.js +3 -0
  88. package/window/WindowStateProvider/useWindowStateData.js.map +1 -0
  89. package/window/WindowStateProvider/util/bringWindowToFront.d.ts +2 -0
  90. package/window/WindowStateProvider/util/bringWindowToFront.js +21 -0
  91. package/window/WindowStateProvider/util/bringWindowToFront.js.map +1 -0
  92. package/window/WindowStateProvider/util/createWindowEntry.d.ts +3 -0
  93. package/window/WindowStateProvider/util/createWindowEntry.js +20 -0
  94. package/window/WindowStateProvider/util/createWindowEntry.js.map +1 -0
  95. package/window/WindowStateProvider/util/findAdjacentWindow.d.ts +2 -0
  96. package/window/WindowStateProvider/util/findAdjacentWindow.js +22 -0
  97. package/window/WindowStateProvider/util/findAdjacentWindow.js.map +1 -0
  98. package/window/WindowStateProvider/util/findTopWindow.d.ts +3 -0
  99. package/window/WindowStateProvider/util/findTopWindow.js +7 -0
  100. package/window/WindowStateProvider/util/findTopWindow.js.map +1 -0
  101. package/window/WindowStateProvider/util/getMaxWindowZIndex.d.ts +2 -0
  102. package/window/WindowStateProvider/util/getMaxWindowZIndex.js +4 -0
  103. package/window/WindowStateProvider/util/getMaxWindowZIndex.js.map +1 -0
  104. package/window/WindowStateProvider/util/index.d.ts +6 -0
  105. package/window/WindowStateProvider/util/index.js +7 -0
  106. package/window/WindowStateProvider/util/index.js.map +1 -0
  107. package/window/WindowStateProvider/util/swapWindowZIndexes.d.ts +2 -0
  108. package/window/WindowStateProvider/util/swapWindowZIndexes.js +21 -0
  109. package/window/WindowStateProvider/util/swapWindowZIndexes.js.map +1 -0
  110. package/window/WindowStateProvider/util/updateWindowFlag.d.ts +2 -0
  111. package/window/WindowStateProvider/util/updateWindowFlag.js +4 -0
  112. package/window/WindowStateProvider/util/updateWindowFlag.js.map +1 -0
  113. package/window/WindowStatusBar.css +40 -0
  114. package/window/WindowStatusBar.d.ts +4 -0
  115. package/window/WindowStatusBar.js +20 -0
  116. package/window/WindowStatusBar.js.map +1 -0
  117. package/window/WindowTitleBar.css +88 -0
  118. package/window/WindowTitleBar.d.ts +18 -0
  119. package/window/WindowTitleBar.js +36 -0
  120. package/window/WindowTitleBar.js.map +1 -0
  121. package/window/WindowToolBar.css +75 -0
  122. package/window/WindowToolBar.d.ts +38 -0
  123. package/window/WindowToolBar.js +68 -0
  124. package/window/WindowToolBar.js.map +1 -0
  125. package/window/useWindowGeometry.d.ts +16 -0
  126. package/window/useWindowGeometry.js +243 -0
  127. package/window/useWindowGeometry.js.map +1 -0
  128. package/window/useWindowItemRegistration.d.ts +10 -0
  129. package/window/useWindowItemRegistration.js +46 -0
  130. package/window/useWindowItemRegistration.js.map +1 -0
  131. package/window/useWindowItemRegistry.d.ts +12 -0
  132. package/window/useWindowItemRegistry.js +45 -0
  133. package/window/useWindowItemRegistry.js.map +1 -0
  134. package/window/useWindowKeyboardGeometry.d.ts +21 -0
  135. package/window/useWindowKeyboardGeometry.js +139 -0
  136. package/window/useWindowKeyboardGeometry.js.map +1 -0
  137. package/window/useWindowLayout.d.ts +18 -0
  138. package/window/useWindowLayout.js +115 -0
  139. package/window/useWindowLayout.js.map +1 -0
  140. package/window/useWindowToolbarReservationRegistry.d.ts +8 -0
  141. package/window/useWindowToolbarReservationRegistry.js +19 -0
  142. package/window/useWindowToolbarReservationRegistry.js.map +1 -0
  143. package/window/windowGeometry.d.ts +36 -0
  144. package/window/windowGeometry.js +54 -0
  145. package/window/windowGeometry.js.map +1 -0
  146. package/window/windowMenuRegistrations.d.ts +18 -0
  147. package/window/windowMenuRegistrations.js +34 -0
  148. package/window/windowMenuRegistrations.js.map +1 -0
@@ -0,0 +1,206 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useId, useMemo, useRef, useState, } from "react";
3
+ import "./Window.css";
4
+ import { CurrentWindowProvider } from "./CurrentWindowContext.js";
5
+ import { useWindowGeometry } from "./useWindowGeometry.js";
6
+ import { useFocusWindowOnShow, useWindowGeometryMenuItems, } from "./useWindowKeyboardGeometry.js";
7
+ import { WindowBody } from "./WindowBody.js";
8
+ import { WindowMenuBar } from "./WindowMenuBar.js";
9
+ import { useWindowFileMenuItems, useWindowViewMenuItems, WindowMenuProvider, } from "./WindowMenuContext.js";
10
+ import { WindowResizeHandle } from "./WindowResizeHandle.js";
11
+ import { hasWindowSidebar, useWindowSidebar, WindowSidebarProvider, } from "./WindowSidebarContext.js";
12
+ import { findTopWindow, useWindowActions as useWindowStateActions, useWindowStateData, } from "./WindowStateProvider/index.js";
13
+ import { WindowStatusBar } from "./WindowStatusBar.js";
14
+ import { WindowTitleBar } from "./WindowTitleBar.js";
15
+ import { WindowToolBar } from "./WindowToolBar.js";
16
+ const WINDOW_STATUS_MESSAGE_DURATION_MS = 2500;
17
+ export function Window({ ContentBoundary, windowId }) {
18
+ const { windowMap, windows } = useWindowStateData();
19
+ const entry = windowMap.get(windowId);
20
+ const isTop = findTopWindow(windows, (candidate) => !candidate.minimized)?.id ===
21
+ windowId;
22
+ if (!entry)
23
+ return null;
24
+ return (_jsx(WindowInner, { ContentBoundary: ContentBoundary, entry: entry, isTop: isTop }));
25
+ }
26
+ function useWindowActions(entry, fileMenuItems, viewMenuItems, geometryMenuItems, hasSidebar) {
27
+ const { close, minimize, moveBackward, moveForward, toggleMaximize } = useWindowStateActions();
28
+ const [showStatusBar, setShowStatusBar] = useState(true);
29
+ const [showSidebar, setShowSidebar] = useState(entry.initialShowSidebar ?? true);
30
+ const handleClose = useCallback(() => close(entry.id), [close, entry.id]);
31
+ const handleMinimize = useCallback(() => minimize(entry.id), [entry.id, minimize]);
32
+ const handleMoveForward = useCallback(() => moveForward(entry.id), [entry.id, moveForward]);
33
+ const handleMoveBackward = useCallback(() => moveBackward(entry.id), [entry.id, moveBackward]);
34
+ const handleMaximize = useCallback(() => toggleMaximize(entry.id), [entry.id, toggleMaximize]);
35
+ const toggleStatusBar = useCallback(() => setShowStatusBar((previous) => !previous), []);
36
+ const toggleSidebar = useCallback(() => setShowSidebar((previous) => !previous), []);
37
+ const menus = useMemo(() => [
38
+ {
39
+ label: "File",
40
+ items: [
41
+ ...fileMenuItems,
42
+ { id: "close", label: "Close", onClick: handleClose },
43
+ ],
44
+ },
45
+ {
46
+ label: "View",
47
+ items: [
48
+ ...viewMenuItems,
49
+ ...geometryMenuItems,
50
+ {
51
+ id: "toggle-status-bar",
52
+ label: `${showStatusBar ? "Hide" : "Show"} Status Bar`,
53
+ onClick: toggleStatusBar,
54
+ },
55
+ ...(hasSidebar
56
+ ? [
57
+ {
58
+ id: "toggle-sidebar",
59
+ label: `${showSidebar ? "Hide" : "Show"} Sidebar`,
60
+ onClick: toggleSidebar,
61
+ },
62
+ ]
63
+ : []),
64
+ ],
65
+ },
66
+ ], [
67
+ geometryMenuItems,
68
+ hasSidebar,
69
+ handleClose,
70
+ fileMenuItems,
71
+ showSidebar,
72
+ showStatusBar,
73
+ toggleSidebar,
74
+ toggleStatusBar,
75
+ viewMenuItems,
76
+ ]);
77
+ return {
78
+ handleClose,
79
+ handleMaximize,
80
+ handleMinimize,
81
+ handleMoveBackward,
82
+ handleMoveForward,
83
+ menus,
84
+ showSidebar,
85
+ showStatusBar,
86
+ };
87
+ }
88
+ function getWindowStyle(maximized, position, size, zIndex) {
89
+ if (maximized) {
90
+ return { top: 0, left: 0, width: "100%", height: "100%", zIndex };
91
+ }
92
+ if (!position) {
93
+ // Hidden until laid out; a requested size applies already so the first
94
+ // clamp measures the window at the size it will show.
95
+ return {
96
+ visibility: "hidden",
97
+ zIndex,
98
+ ...(size ? { width: size.width, height: size.height } : {}),
99
+ };
100
+ }
101
+ return {
102
+ left: position.x,
103
+ top: position.y,
104
+ zIndex,
105
+ ...(size ? { width: size.width, height: size.height } : {}),
106
+ };
107
+ }
108
+ function useWindowStatusMessage() {
109
+ const [statusText, setStatusText] = useState("");
110
+ const statusTimeoutRef = useRef(null);
111
+ const showStatusMessage = useCallback((message) => {
112
+ if (statusTimeoutRef.current) {
113
+ clearTimeout(statusTimeoutRef.current);
114
+ }
115
+ setStatusText(message);
116
+ statusTimeoutRef.current = setTimeout(() => {
117
+ setStatusText("");
118
+ statusTimeoutRef.current = null;
119
+ }, WINDOW_STATUS_MESSAGE_DURATION_MS);
120
+ }, []);
121
+ useEffect(() => {
122
+ return () => {
123
+ if (statusTimeoutRef.current) {
124
+ clearTimeout(statusTimeoutRef.current);
125
+ }
126
+ };
127
+ }, []);
128
+ return { showStatusMessage, statusText };
129
+ }
130
+ // Refcounted so overlapping overlays (one closing as another opens) never leave
131
+ // the row hidden, and never flash it back for a frame between the two.
132
+ function useWindowToolbarSuppression() {
133
+ const [suppressionCount, setSuppressionCount] = useState(0);
134
+ const suppressToolbar = useCallback(() => {
135
+ setSuppressionCount((count) => count + 1);
136
+ return () => setSuppressionCount((count) => Math.max(0, count - 1));
137
+ }, []);
138
+ return { suppressToolbar, toolbarSuppressed: suppressionCount > 0 };
139
+ }
140
+ function WindowResizeHandles({ handleResizePointerDown, }) {
141
+ return (_jsxs(_Fragment, { children: [_jsx(WindowResizeHandle, { corner: "se", onPointerDown: handleResizePointerDown }), _jsx(WindowResizeHandle, { corner: "sw", onPointerDown: handleResizePointerDown }), _jsx(WindowResizeHandle, { corner: "ne", onPointerDown: handleResizePointerDown }), _jsx(WindowResizeHandle, { corner: "nw", onPointerDown: handleResizePointerDown })] }));
142
+ }
143
+ function WindowInner({ ContentBoundary, entry, isTop }) {
144
+ return (_jsx(WindowMenuProvider, { children: _jsx(WindowSidebarProvider, { children: _jsx(WindowInnerContent, { ContentBoundary: ContentBoundary, entry: entry, isTop: isTop }) }) }));
145
+ }
146
+ // The bars above the body: title, menus, and the toolbar row a full-pane overlay
147
+ // can stand down (see `useWindowToolbarSuppression`).
148
+ function WindowChrome({ actions, entry, onGoBack, onPointerDown, titleId, toolbarSuppressed, }) {
149
+ return (_jsxs(_Fragment, { children: [_jsx(WindowTitleBar, { title: entry.title, titleId: titleId, onPointerDown: onPointerDown, onMinimize: actions.handleMinimize, onMaximize: actions.handleMaximize, onClose: actions.handleClose, onMoveForward: actions.handleMoveForward, onMoveBackward: actions.handleMoveBackward }), _jsx(WindowMenuBar, { menus: actions.menus }), !toolbarSuppressed && (_jsx(WindowToolBar, { canGoBack: (entry.routeHistory?.length ?? 0) > 0, showHistoryBack: entry.appId !== undefined, onGoBack: onGoBack }))] }));
150
+ }
151
+ // Handlers on the window root: its Back caret, raising it on any press inside,
152
+ // and keeping background context menus from opening under window-local ones.
153
+ function useWindowRootHandlers(windowId) {
154
+ // The toolbar renders above the route boundary, so the window's own Back stack
155
+ // is threaded in from here rather than read from context.
156
+ const { bringToFront, goBackRoute } = useWindowStateActions();
157
+ const handleGoBack = useCallback(() => {
158
+ goBackRoute(windowId);
159
+ }, [windowId, goBackRoute]);
160
+ const handleWindowPointerDown = useCallback(() => {
161
+ bringToFront(windowId);
162
+ }, [bringToFront, windowId]);
163
+ const handleWindowContextMenu = useCallback((event) => {
164
+ // Keep background pane context menus from opening underneath window-local menus.
165
+ event.stopPropagation();
166
+ }, []);
167
+ const windowContextMenuTrapProps = {
168
+ onContextMenu: handleWindowContextMenu,
169
+ };
170
+ return { handleGoBack, handleWindowPointerDown, windowContextMenuTrapProps };
171
+ }
172
+ function WindowInnerContent({ ContentBoundary, entry, isTop, }) {
173
+ const { maximized, minimized, zIndex, component: Component } = entry;
174
+ const windowRef = useRef(null);
175
+ const titleId = useId();
176
+ const [overlayHost, setOverlayHost] = useState(null);
177
+ const fileMenuItems = useWindowFileMenuItems();
178
+ const viewMenuItems = useWindowViewMenuItems();
179
+ const { sidebar } = useWindowSidebar();
180
+ const hasSidebar = hasWindowSidebar(sidebar);
181
+ const { handlePointerDown, handleResizePointerDown, position, size, ...stepped } = useWindowGeometry(entry, maximized, windowRef);
182
+ const { showStatusMessage, statusText } = useWindowStatusMessage();
183
+ const geometryMenuItems = useWindowGeometryMenuItems(stepped, { maximized, minimized, windowRef }, showStatusMessage);
184
+ const actions = useWindowActions(entry, fileMenuItems, viewMenuItems, geometryMenuItems, hasSidebar);
185
+ // A maximized window fills its surface without a laid-out position.
186
+ useFocusWindowOnShow(windowRef, {
187
+ isTop,
188
+ shown: !minimized && (maximized || position !== null),
189
+ });
190
+ const { suppressToolbar, toolbarSuppressed } = useWindowToolbarSuppression();
191
+ const { handleGoBack, handleWindowPointerDown, windowContextMenuTrapProps } = useWindowRootHandlers(entry.id);
192
+ const style = getWindowStyle(maximized, position, size, zIndex);
193
+ if (minimized) {
194
+ return null;
195
+ }
196
+ // A section labelled by its title is a region landmark. Windows are
197
+ // non-modal and freely arranged, so they are not dialogs; hosts keep that
198
+ // role for the modals they open inside a window.
199
+ return (_jsxs("section", { ref: windowRef, "aria-labelledby": titleId, className: maximized ? "window window--maximized" : "window", tabIndex: -1, ...windowContextMenuTrapProps, onPointerDownCapture: handleWindowPointerDown, style: style, children: [_jsx(WindowChrome, { actions: actions, entry: entry, onGoBack: handleGoBack, onPointerDown: handlePointerDown, titleId: titleId, toolbarSuppressed: toolbarSuppressed }), _jsx(CurrentWindowProvider, { close: actions.handleClose, id: entry.id, overlayHost: overlayHost, showStatusMessage: showStatusMessage, suppressToolbar: suppressToolbar, children: _jsx(WindowBodyWithSidebar, { overlayHostRef: setOverlayHost, showSidebar: actions.showSidebar, children: ContentBoundary ? (_jsx(ContentBoundary, { entry: entry, children: Component && _jsx(Component, {}) })) : (Component && _jsx(Component, {})) }) }), _jsx(WindowStatusBar, { text: statusText, visible: actions.showStatusBar }), !maximized && (_jsx(WindowResizeHandles, { handleResizePointerDown: handleResizePointerDown }))] }));
200
+ }
201
+ function WindowBodyWithSidebar({ showSidebar, overlayHostRef, children, }) {
202
+ const { sidebar } = useWindowSidebar();
203
+ const hasSidebar = hasWindowSidebar(sidebar);
204
+ return (_jsx(WindowBody, { contentRef: overlayHostRef, showSidebar: showSidebar && hasSidebar, sidebar: sidebar, children: children }));
205
+ }
206
+ //# sourceMappingURL=Window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Window.js","sourceRoot":"","sources":["../../../../../../../Users/a2f0/github/tearleads5/packages/windowing/src/window/Window.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n type ComponentType,\n type CSSProperties,\n type HTMLAttributes,\n type PropsWithChildren,\n type MouseEvent as ReactMouseEvent,\n type PointerEvent as ReactPointerEvent,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport \"./Window.css\";\nimport { CurrentWindowProvider } from \"./CurrentWindowContext\";\nimport { useWindowGeometry } from \"./useWindowGeometry\";\nimport {\n useFocusWindowOnShow,\n useWindowGeometryMenuItems,\n} from \"./useWindowKeyboardGeometry\";\nimport { WindowBody } from \"./WindowBody\";\nimport { WindowMenuBar, type WindowMenuItem } from \"./WindowMenuBar\";\nimport {\n useWindowFileMenuItems,\n useWindowViewMenuItems,\n WindowMenuProvider,\n} from \"./WindowMenuContext\";\nimport type { ResizeCorner } from \"./WindowResizeHandle\";\nimport { WindowResizeHandle } from \"./WindowResizeHandle\";\nimport {\n hasWindowSidebar,\n useWindowSidebar,\n WindowSidebarProvider,\n} from \"./WindowSidebarContext\";\nimport {\n findTopWindow,\n useWindowActions as useWindowStateActions,\n useWindowStateData,\n type WindowEntry,\n type WindowPosition,\n type WindowSize,\n} from \"./WindowStateProvider\";\nimport { WindowStatusBar } from \"./WindowStatusBar\";\nimport { WindowTitleBar } from \"./WindowTitleBar\";\nimport { WindowToolBar } from \"./WindowToolBar\";\n\n// Wraps a window's content so the host can give it app-specific context (a\n// route, an error boundary) without the window layer knowing about apps.\nexport type WindowContentBoundary = ComponentType<\n PropsWithChildren<{ entry: WindowEntry }>\n>;\n\ninterface WindowProps {\n ContentBoundary?: WindowContentBoundary | undefined;\n windowId: string;\n}\n\nconst WINDOW_STATUS_MESSAGE_DURATION_MS = 2500;\n\nexport function Window({ ContentBoundary, windowId }: WindowProps) {\n const { windowMap, windows } = useWindowStateData();\n const entry = windowMap.get(windowId);\n const isTop =\n findTopWindow(windows, (candidate) => !candidate.minimized)?.id ===\n windowId;\n\n if (!entry) return null;\n\n return (\n <WindowInner\n ContentBoundary={ContentBoundary}\n entry={entry}\n isTop={isTop}\n />\n );\n}\n\nfunction useWindowActions(\n entry: WindowEntry,\n fileMenuItems: WindowMenuItem[],\n viewMenuItems: WindowMenuItem[],\n geometryMenuItems: WindowMenuItem[],\n hasSidebar: boolean,\n) {\n const { close, minimize, moveBackward, moveForward, toggleMaximize } =\n useWindowStateActions();\n const [showStatusBar, setShowStatusBar] = useState(true);\n const [showSidebar, setShowSidebar] = useState(\n entry.initialShowSidebar ?? true,\n );\n const handleClose = useCallback(() => close(entry.id), [close, entry.id]);\n const handleMinimize = useCallback(\n () => minimize(entry.id),\n [entry.id, minimize],\n );\n const handleMoveForward = useCallback(\n () => moveForward(entry.id),\n [entry.id, moveForward],\n );\n const handleMoveBackward = useCallback(\n () => moveBackward(entry.id),\n [entry.id, moveBackward],\n );\n const handleMaximize = useCallback(\n () => toggleMaximize(entry.id),\n [entry.id, toggleMaximize],\n );\n const toggleStatusBar = useCallback(\n () => setShowStatusBar((previous) => !previous),\n [],\n );\n const toggleSidebar = useCallback(\n () => setShowSidebar((previous) => !previous),\n [],\n );\n const menus = useMemo(\n () => [\n {\n label: \"File\",\n items: [\n ...fileMenuItems,\n { id: \"close\", label: \"Close\", onClick: handleClose },\n ],\n },\n {\n label: \"View\",\n items: [\n ...viewMenuItems,\n ...geometryMenuItems,\n {\n id: \"toggle-status-bar\",\n label: `${showStatusBar ? \"Hide\" : \"Show\"} Status Bar`,\n onClick: toggleStatusBar,\n },\n ...(hasSidebar\n ? [\n {\n id: \"toggle-sidebar\",\n label: `${showSidebar ? \"Hide\" : \"Show\"} Sidebar`,\n onClick: toggleSidebar,\n },\n ]\n : []),\n ],\n },\n ],\n [\n geometryMenuItems,\n hasSidebar,\n handleClose,\n fileMenuItems,\n showSidebar,\n showStatusBar,\n toggleSidebar,\n toggleStatusBar,\n viewMenuItems,\n ],\n );\n\n return {\n handleClose,\n handleMaximize,\n handleMinimize,\n handleMoveBackward,\n handleMoveForward,\n menus,\n showSidebar,\n showStatusBar,\n };\n}\n\nfunction getWindowStyle(\n maximized: boolean,\n position: WindowPosition | null,\n size: WindowSize | null,\n zIndex: number,\n): CSSProperties | undefined {\n if (maximized) {\n return { top: 0, left: 0, width: \"100%\", height: \"100%\", zIndex };\n }\n if (!position) {\n // Hidden until laid out; a requested size applies already so the first\n // clamp measures the window at the size it will show.\n return {\n visibility: \"hidden\",\n zIndex,\n ...(size ? { width: size.width, height: size.height } : {}),\n };\n }\n\n return {\n left: position.x,\n top: position.y,\n zIndex,\n ...(size ? { width: size.width, height: size.height } : {}),\n };\n}\n\nfunction useWindowStatusMessage() {\n const [statusText, setStatusText] = useState(\"\");\n const statusTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const showStatusMessage = useCallback((message: string) => {\n if (statusTimeoutRef.current) {\n clearTimeout(statusTimeoutRef.current);\n }\n setStatusText(message);\n statusTimeoutRef.current = setTimeout(() => {\n setStatusText(\"\");\n statusTimeoutRef.current = null;\n }, WINDOW_STATUS_MESSAGE_DURATION_MS);\n }, []);\n\n useEffect(() => {\n return () => {\n if (statusTimeoutRef.current) {\n clearTimeout(statusTimeoutRef.current);\n }\n };\n }, []);\n\n return { showStatusMessage, statusText };\n}\n\n// Refcounted so overlapping overlays (one closing as another opens) never leave\n// the row hidden, and never flash it back for a frame between the two.\nfunction useWindowToolbarSuppression() {\n const [suppressionCount, setSuppressionCount] = useState(0);\n const suppressToolbar = useCallback(() => {\n setSuppressionCount((count) => count + 1);\n return () => setSuppressionCount((count) => Math.max(0, count - 1));\n }, []);\n\n return { suppressToolbar, toolbarSuppressed: suppressionCount > 0 };\n}\n\nfunction WindowResizeHandles({\n handleResizePointerDown,\n}: {\n handleResizePointerDown: (\n event: ReactPointerEvent,\n corner: ResizeCorner,\n ) => void;\n}) {\n return (\n <>\n <WindowResizeHandle corner=\"se\" onPointerDown={handleResizePointerDown} />\n <WindowResizeHandle corner=\"sw\" onPointerDown={handleResizePointerDown} />\n <WindowResizeHandle corner=\"ne\" onPointerDown={handleResizePointerDown} />\n <WindowResizeHandle corner=\"nw\" onPointerDown={handleResizePointerDown} />\n </>\n );\n}\n\ninterface WindowInnerProps {\n ContentBoundary?: WindowContentBoundary | undefined;\n entry: WindowEntry;\n // Whether this is the foremost visible window, the one that takes focus.\n isTop: boolean;\n}\n\nfunction WindowInner({ ContentBoundary, entry, isTop }: WindowInnerProps) {\n return (\n <WindowMenuProvider>\n <WindowSidebarProvider>\n <WindowInnerContent\n ContentBoundary={ContentBoundary}\n entry={entry}\n isTop={isTop}\n />\n </WindowSidebarProvider>\n </WindowMenuProvider>\n );\n}\n\n// The bars above the body: title, menus, and the toolbar row a full-pane overlay\n// can stand down (see `useWindowToolbarSuppression`).\nfunction WindowChrome({\n actions,\n entry,\n onGoBack,\n onPointerDown,\n titleId,\n toolbarSuppressed,\n}: {\n actions: ReturnType<typeof useWindowActions>;\n entry: WindowEntry;\n onGoBack: () => void;\n onPointerDown: (event: ReactPointerEvent) => void;\n titleId: string;\n toolbarSuppressed: boolean;\n}) {\n return (\n <>\n <WindowTitleBar\n title={entry.title}\n titleId={titleId}\n onPointerDown={onPointerDown}\n onMinimize={actions.handleMinimize}\n onMaximize={actions.handleMaximize}\n onClose={actions.handleClose}\n onMoveForward={actions.handleMoveForward}\n onMoveBackward={actions.handleMoveBackward}\n />\n <WindowMenuBar menus={actions.menus} />\n {!toolbarSuppressed && (\n <WindowToolBar\n canGoBack={(entry.routeHistory?.length ?? 0) > 0}\n showHistoryBack={entry.appId !== undefined}\n onGoBack={onGoBack}\n />\n )}\n </>\n );\n}\n\n// Handlers on the window root: its Back caret, raising it on any press inside,\n// and keeping background context menus from opening under window-local ones.\nfunction useWindowRootHandlers(windowId: string) {\n // The toolbar renders above the route boundary, so the window's own Back stack\n // is threaded in from here rather than read from context.\n const { bringToFront, goBackRoute } = useWindowStateActions();\n const handleGoBack = useCallback(() => {\n goBackRoute(windowId);\n }, [windowId, goBackRoute]);\n const handleWindowPointerDown = useCallback(() => {\n bringToFront(windowId);\n }, [bringToFront, windowId]);\n const handleWindowContextMenu = useCallback((event: ReactMouseEvent) => {\n // Keep background pane context menus from opening underneath window-local menus.\n event.stopPropagation();\n }, []);\n const windowContextMenuTrapProps: Pick<\n HTMLAttributes<HTMLElement>,\n \"onContextMenu\"\n > = {\n onContextMenu: handleWindowContextMenu,\n };\n\n return { handleGoBack, handleWindowPointerDown, windowContextMenuTrapProps };\n}\n\nfunction WindowInnerContent({\n ContentBoundary,\n entry,\n isTop,\n}: WindowInnerProps) {\n const { maximized, minimized, zIndex, component: Component } = entry;\n const windowRef = useRef<HTMLElement>(null);\n const titleId = useId();\n const [overlayHost, setOverlayHost] = useState<HTMLElement | null>(null);\n const fileMenuItems = useWindowFileMenuItems();\n const viewMenuItems = useWindowViewMenuItems();\n const { sidebar } = useWindowSidebar();\n const hasSidebar = hasWindowSidebar(sidebar);\n const {\n handlePointerDown,\n handleResizePointerDown,\n position,\n size,\n ...stepped\n } = useWindowGeometry(entry, maximized, windowRef);\n const { showStatusMessage, statusText } = useWindowStatusMessage();\n const geometryMenuItems = useWindowGeometryMenuItems(\n stepped,\n { maximized, minimized, windowRef },\n showStatusMessage,\n );\n const actions = useWindowActions(\n entry,\n fileMenuItems,\n viewMenuItems,\n geometryMenuItems,\n hasSidebar,\n );\n // A maximized window fills its surface without a laid-out position.\n useFocusWindowOnShow(windowRef, {\n isTop,\n shown: !minimized && (maximized || position !== null),\n });\n const { suppressToolbar, toolbarSuppressed } = useWindowToolbarSuppression();\n const { handleGoBack, handleWindowPointerDown, windowContextMenuTrapProps } =\n useWindowRootHandlers(entry.id);\n const style = getWindowStyle(maximized, position, size, zIndex);\n\n if (minimized) {\n return null;\n }\n\n // A section labelled by its title is a region landmark. Windows are\n // non-modal and freely arranged, so they are not dialogs; hosts keep that\n // role for the modals they open inside a window.\n return (\n <section\n ref={windowRef}\n aria-labelledby={titleId}\n className={maximized ? \"window window--maximized\" : \"window\"}\n tabIndex={-1}\n {...windowContextMenuTrapProps}\n onPointerDownCapture={handleWindowPointerDown}\n style={style}\n >\n <WindowChrome\n actions={actions}\n entry={entry}\n onGoBack={handleGoBack}\n onPointerDown={handlePointerDown}\n titleId={titleId}\n toolbarSuppressed={toolbarSuppressed}\n />\n <CurrentWindowProvider\n close={actions.handleClose}\n id={entry.id}\n overlayHost={overlayHost}\n showStatusMessage={showStatusMessage}\n suppressToolbar={suppressToolbar}\n >\n <WindowBodyWithSidebar\n overlayHostRef={setOverlayHost}\n showSidebar={actions.showSidebar}\n >\n {ContentBoundary ? (\n <ContentBoundary entry={entry}>\n {Component && <Component />}\n </ContentBoundary>\n ) : (\n Component && <Component />\n )}\n </WindowBodyWithSidebar>\n </CurrentWindowProvider>\n <WindowStatusBar text={statusText} visible={actions.showStatusBar} />\n {!maximized && (\n <WindowResizeHandles\n handleResizePointerDown={handleResizePointerDown}\n />\n )}\n </section>\n );\n}\n\nfunction WindowBodyWithSidebar({\n showSidebar,\n overlayHostRef,\n children,\n}: PropsWithChildren<{\n overlayHostRef: (element: HTMLDivElement | null) => void;\n showSidebar: boolean;\n}>) {\n const { sidebar } = useWindowSidebar();\n const hasSidebar = hasWindowSidebar(sidebar);\n\n return (\n <WindowBody\n contentRef={overlayHostRef}\n showSidebar={showSidebar && hasSidebar}\n sidebar={sidebar}\n >\n {children}\n </WindowBody>\n );\n}\n"]}
@@ -0,0 +1,21 @@
1
+ .window-body {
2
+ display: flex;
3
+ flex: 1;
4
+ min-height: 0;
5
+ overflow: hidden;
6
+ }
7
+
8
+ .window-body-content {
9
+ position: relative;
10
+ flex: 1;
11
+ min-width: 0;
12
+ min-height: 0;
13
+ overflow: hidden;
14
+ }
15
+
16
+ .window-body-content-scroll {
17
+ height: 100%;
18
+ padding: var(--space-md);
19
+ overflow: auto;
20
+ scrollbar-gutter: var(--scrollbar-gutter);
21
+ }
@@ -0,0 +1,8 @@
1
+ import type { PropsWithChildren, Ref } from "react";
2
+ interface WindowBodyProps {
3
+ contentRef?: Ref<HTMLDivElement> | undefined;
4
+ showSidebar?: boolean;
5
+ sidebar?: React.ReactNode;
6
+ }
7
+ export declare function WindowBody({ contentRef, showSidebar, sidebar, children, }: PropsWithChildren<WindowBodyProps>): import("react").JSX.Element;
8
+ export {};
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import "./WindowBody.css";
3
+ import { WindowSidebar } from "./WindowSidebar.js";
4
+ // The content pane is programmatically focusable (`tabIndex={-1}`, so never in
5
+ // the tab order): an overlay that covered it lands focus here when the control
6
+ // that opened the overlay is gone by the time it closes.
7
+ export function WindowBody({ contentRef, showSidebar = false, sidebar, children, }) {
8
+ return (_jsx("div", { className: "window-body", children: showSidebar ? (_jsx(WindowSidebar, { contentRef: contentRef, sidebar: sidebar, children: children })) : (_jsx("div", { className: "window-body-content", ref: contentRef, tabIndex: -1, children: _jsx("div", { className: "window-body-content-scroll", children: children }) })) }));
9
+ }
10
+ //# sourceMappingURL=WindowBody.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WindowBody.js","sourceRoot":"","sources":["../../../../../../../Users/a2f0/github/tearleads5/packages/windowing/src/window/WindowBody.tsx"],"names":[],"mappings":";AACA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAQhD,+EAA+E;AAC/E,+EAA+E;AAC/E,yDAAyD;AACzD,MAAM,UAAU,UAAU,CAAC,EACzB,UAAU,EACV,WAAW,GAAG,KAAK,EACnB,OAAO,EACP,QAAQ,GAC2B;IACnC,OAAO,CACL,cAAK,SAAS,EAAC,aAAa,YACzB,WAAW,CAAC,CAAC,CAAC,CACb,KAAC,aAAa,IAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,YACpD,QAAQ,GACK,CACjB,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,qBAAqB,EAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,YAChE,cAAK,SAAS,EAAC,4BAA4B,YAAE,QAAQ,GAAO,GACxD,CACP,GACG,CACP,CAAC;AACJ,CAAC","sourcesContent":["import type { PropsWithChildren, Ref } from \"react\";\nimport \"./WindowBody.css\";\nimport { WindowSidebar } from \"./WindowSidebar\";\n\ninterface WindowBodyProps {\n contentRef?: Ref<HTMLDivElement> | undefined;\n showSidebar?: boolean;\n sidebar?: React.ReactNode;\n}\n\n// The content pane is programmatically focusable (`tabIndex={-1}`, so never in\n// the tab order): an overlay that covered it lands focus here when the control\n// that opened the overlay is gone by the time it closes.\nexport function WindowBody({\n contentRef,\n showSidebar = false,\n sidebar,\n children,\n}: PropsWithChildren<WindowBodyProps>) {\n return (\n <div className=\"window-body\">\n {showSidebar ? (\n <WindowSidebar contentRef={contentRef} sidebar={sidebar}>\n {children}\n </WindowSidebar>\n ) : (\n <div className=\"window-body-content\" ref={contentRef} tabIndex={-1}>\n <div className=\"window-body-content-scroll\">{children}</div>\n </div>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,55 @@
1
+ import type { ReactNode } from "react";
2
+ import type { WindowTitleBarAction } from "./WindowTitleBar.js";
3
+ export interface WindowTitleBarActionInput {
4
+ disabled?: boolean;
5
+ icon: ReactNode;
6
+ id: string;
7
+ label: string;
8
+ onClick: () => unknown;
9
+ /**
10
+ * Reserves the slot immediately left of the lowest-priority toolbar action.
11
+ * If multiple actions request it, the highest-priority action wins, with the
12
+ * action id providing a deterministic tie-break. A sole action stays sole.
13
+ */
14
+ placement?: "penultimate";
15
+ priority?: number;
16
+ }
17
+ export interface WindowBackActionInput {
18
+ disabled?: boolean;
19
+ label: string;
20
+ onClick: () => unknown;
21
+ priority?: number;
22
+ }
23
+ export interface RegisteredWindowTitleBarAction {
24
+ disabled: boolean;
25
+ icon: ReactNode;
26
+ id: string;
27
+ label: string;
28
+ onClick: () => unknown;
29
+ placement: "penultimate" | null;
30
+ priority: number;
31
+ }
32
+ export interface RegisteredWindowBackAction {
33
+ disabled: boolean;
34
+ label: string;
35
+ onClick: () => unknown;
36
+ priority: number;
37
+ }
38
+ export interface WindowBackAction {
39
+ disabled?: boolean;
40
+ label: string;
41
+ onClick: () => void;
42
+ }
43
+ /**
44
+ * The registered title-bar actions rendered as icon buttons — the shared markup
45
+ * behind the windowed toolbar row and the routed app bar, which differ only in
46
+ * their button class.
47
+ */
48
+ export declare function WindowTitleBarActionButtons({ actions, className, }: {
49
+ actions: ReadonlyArray<WindowTitleBarAction>;
50
+ className: string;
51
+ }): import("react").JSX.Element;
52
+ export declare function createTitleBarActions(items: ReadonlyMap<object, RegisteredWindowTitleBarAction>): WindowTitleBarAction[];
53
+ export declare function createRegisteredTitleBarAction(item: WindowTitleBarActionInput, onClick: () => unknown): RegisteredWindowTitleBarAction;
54
+ export declare function createRegisteredBackAction(item: WindowBackActionInput, onClick: () => unknown): RegisteredWindowBackAction;
55
+ export declare function createBackAction(item: RegisteredWindowBackAction | null): WindowBackAction | null;
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * The registered title-bar actions rendered as icon buttons — the shared markup
4
+ * behind the windowed toolbar row and the routed app bar, which differ only in
5
+ * their button class.
6
+ */
7
+ export function WindowTitleBarActionButtons({ actions, className, }) {
8
+ return (_jsx(_Fragment, { children: actions.map((action) => (_jsx("button", { "aria-label": action.label, className: className, disabled: action.disabled, title: action.label, type: "button", onClick: action.onClick, children: action.icon }, action.id))) }));
9
+ }
10
+ export function createTitleBarActions(items) {
11
+ const ordered = Array.from(items.values()).sort((left, right) => right.priority - left.priority ||
12
+ (left.id < right.id ? -1 : left.id > right.id ? 1 : 0));
13
+ const penultimateIndex = ordered.findIndex((item) => item.placement === "penultimate");
14
+ if (penultimateIndex >= 0 && ordered.length > 1) {
15
+ const [penultimate] = ordered.splice(penultimateIndex, 1);
16
+ if (penultimate) {
17
+ ordered.splice(ordered.length - 1, 0, penultimate);
18
+ }
19
+ }
20
+ return ordered.map((item) => ({
21
+ disabled: item.disabled,
22
+ icon: item.icon,
23
+ id: item.id,
24
+ label: item.label,
25
+ onClick: () => {
26
+ void item.onClick();
27
+ },
28
+ }));
29
+ }
30
+ export function createRegisteredTitleBarAction(item, onClick) {
31
+ return {
32
+ disabled: item.disabled ?? false,
33
+ icon: item.icon,
34
+ id: item.id,
35
+ label: item.label,
36
+ onClick,
37
+ placement: item.placement ?? null,
38
+ priority: item.priority ?? 0,
39
+ };
40
+ }
41
+ export function createRegisteredBackAction(item, onClick) {
42
+ return {
43
+ disabled: item.disabled ?? false,
44
+ label: item.label,
45
+ onClick,
46
+ priority: item.priority ?? 0,
47
+ };
48
+ }
49
+ export function createBackAction(item) {
50
+ if (!item) {
51
+ return null;
52
+ }
53
+ return {
54
+ disabled: item.disabled,
55
+ label: item.label,
56
+ onClick: () => {
57
+ void item.onClick();
58
+ },
59
+ };
60
+ }
61
+ //# sourceMappingURL=WindowChromeActions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WindowChromeActions.js","sourceRoot":"","sources":["../../../../../../../Users/a2f0/github/tearleads5/packages/windowing/src/window/WindowChromeActions.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from \"react\";\nimport type { WindowTitleBarAction } from \"./WindowTitleBar\";\n\nexport interface WindowTitleBarActionInput {\n disabled?: boolean;\n icon: ReactNode;\n id: string;\n label: string;\n onClick: () => unknown;\n /**\n * Reserves the slot immediately left of the lowest-priority toolbar action.\n * If multiple actions request it, the highest-priority action wins, with the\n * action id providing a deterministic tie-break. A sole action stays sole.\n */\n placement?: \"penultimate\";\n priority?: number;\n}\n\nexport interface WindowBackActionInput {\n disabled?: boolean;\n label: string;\n onClick: () => unknown;\n priority?: number;\n}\n\nexport interface RegisteredWindowTitleBarAction {\n disabled: boolean;\n icon: ReactNode;\n id: string;\n label: string;\n onClick: () => unknown;\n placement: \"penultimate\" | null;\n priority: number;\n}\n\nexport interface RegisteredWindowBackAction {\n disabled: boolean;\n label: string;\n onClick: () => unknown;\n priority: number;\n}\n\nexport interface WindowBackAction {\n disabled?: boolean;\n label: string;\n onClick: () => void;\n}\n\n/**\n * The registered title-bar actions rendered as icon buttons — the shared markup\n * behind the windowed toolbar row and the routed app bar, which differ only in\n * their button class.\n */\nexport function WindowTitleBarActionButtons({\n actions,\n className,\n}: {\n actions: ReadonlyArray<WindowTitleBarAction>;\n className: string;\n}) {\n return (\n <>\n {actions.map((action) => (\n <button\n aria-label={action.label}\n className={className}\n disabled={action.disabled}\n key={action.id}\n title={action.label}\n type=\"button\"\n onClick={action.onClick}\n >\n {action.icon}\n </button>\n ))}\n </>\n );\n}\n\nexport function createTitleBarActions(\n items: ReadonlyMap<object, RegisteredWindowTitleBarAction>,\n): WindowTitleBarAction[] {\n const ordered = Array.from(items.values()).sort(\n (left, right) =>\n right.priority - left.priority ||\n (left.id < right.id ? -1 : left.id > right.id ? 1 : 0),\n );\n const penultimateIndex = ordered.findIndex(\n (item) => item.placement === \"penultimate\",\n );\n if (penultimateIndex >= 0 && ordered.length > 1) {\n const [penultimate] = ordered.splice(penultimateIndex, 1);\n if (penultimate) {\n ordered.splice(ordered.length - 1, 0, penultimate);\n }\n }\n\n return ordered.map((item) => ({\n disabled: item.disabled,\n icon: item.icon,\n id: item.id,\n label: item.label,\n onClick: () => {\n void item.onClick();\n },\n }));\n}\n\nexport function createRegisteredTitleBarAction(\n item: WindowTitleBarActionInput,\n onClick: () => unknown,\n): RegisteredWindowTitleBarAction {\n return {\n disabled: item.disabled ?? false,\n icon: item.icon,\n id: item.id,\n label: item.label,\n onClick,\n placement: item.placement ?? null,\n priority: item.priority ?? 0,\n };\n}\n\nexport function createRegisteredBackAction(\n item: WindowBackActionInput,\n onClick: () => unknown,\n): RegisteredWindowBackAction {\n return {\n disabled: item.disabled ?? false,\n label: item.label,\n onClick,\n priority: item.priority ?? 0,\n };\n}\n\nexport function createBackAction(\n item: RegisteredWindowBackAction | null,\n): WindowBackAction | null {\n if (!item) {\n return null;\n }\n\n return {\n disabled: item.disabled,\n label: item.label,\n onClick: () => {\n void item.onClick();\n },\n };\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { RefObject } from "react";
2
+ export declare function WindowCloseButton({ buttonRef, label, onClick, }: {
3
+ buttonRef?: RefObject<HTMLButtonElement | null>;
4
+ label?: string;
5
+ onClick: () => void;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // The floating-window close control (the `×` in the title bar). Shared beyond
3
+ // WindowTitleBar so any surface dressing as a window — e.g. the note attachment
4
+ // preview — closes with the identical glyph and chrome instead of a look-alike.
5
+ // `label` names it for assistive tech; `buttonRef` lets a caller move focus to it.
6
+ export function WindowCloseButton({ buttonRef, label, onClick, }) {
7
+ return (_jsx("button", { type: "button", className: "window-close", onClick: onClick, ref: buttonRef, "aria-label": label, title: label, children: "\u00D7" }));
8
+ }
9
+ //# sourceMappingURL=WindowCloseButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WindowCloseButton.js","sourceRoot":"","sources":["../../../../../../../Users/a2f0/github/tearleads5/packages/windowing/src/window/WindowCloseButton.tsx"],"names":[],"mappings":";AAEA,8EAA8E;AAC9E,gFAAgF;AAChF,gFAAgF;AAChF,mFAAmF;AACnF,MAAM,UAAU,iBAAiB,CAAC,EAChC,SAAS,EACT,KAAK,EACL,OAAO,GAKR;IACC,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,cAAc,EACxB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,SAAS,gBACF,KAAK,EACjB,KAAK,EAAE,KAAK,uBAGL,CACV,CAAC;AACJ,CAAC","sourcesContent":["import type { RefObject } from \"react\";\n\n// The floating-window close control (the `×` in the title bar). Shared beyond\n// WindowTitleBar so any surface dressing as a window — e.g. the note attachment\n// preview — closes with the identical glyph and chrome instead of a look-alike.\n// `label` names it for assistive tech; `buttonRef` lets a caller move focus to it.\nexport function WindowCloseButton({\n buttonRef,\n label,\n onClick,\n}: {\n buttonRef?: RefObject<HTMLButtonElement | null>;\n label?: string;\n onClick: () => void;\n}) {\n return (\n <button\n type=\"button\"\n className=\"window-close\"\n onClick={onClick}\n ref={buttonRef}\n aria-label={label}\n title={label}\n >\n &times;\n </button>\n );\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function WindowMaximizeButton({ onClick }: {
2
+ onClick: () => void;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export function WindowMaximizeButton({ onClick }) {
3
+ return (_jsx("button", { type: "button", className: "window-maximize", "aria-label": "Toggle maximize window", title: "Toggle maximize window", onClick: onClick, children: "\u25A1" }));
4
+ }
5
+ //# sourceMappingURL=WindowMaximizeButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WindowMaximizeButton.js","sourceRoot":"","sources":["../../../../../../../Users/a2f0/github/tearleads5/packages/windowing/src/window/WindowMaximizeButton.tsx"],"names":[],"mappings":";AAAA,MAAM,UAAU,oBAAoB,CAAC,EAAE,OAAO,EAA2B;IACvE,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,iBAAiB,gBAChB,wBAAwB,EACnC,KAAK,EAAC,wBAAwB,EAC9B,OAAO,EAAE,OAAO,uBAGT,CACV,CAAC;AACJ,CAAC","sourcesContent":["export function WindowMaximizeButton({ onClick }: { onClick: () => void }) {\n return (\n <button\n type=\"button\"\n className=\"window-maximize\"\n aria-label=\"Toggle maximize window\"\n title=\"Toggle maximize window\"\n onClick={onClick}\n >\n &#9633;\n </button>\n );\n}\n"]}
@@ -0,0 +1,63 @@
1
+ .window-menubar {
2
+ display: flex;
3
+ background: var(--color-muted);
4
+ user-select: none;
5
+ }
6
+
7
+ .window-menubar-item {
8
+ position: relative;
9
+ }
10
+
11
+ .window-menubar-trigger {
12
+ padding: var(--window-bar-padding);
13
+ border: none;
14
+ background: none;
15
+ /* Take the theme's text color rather than the UA ButtonText (pure white or
16
+ black), which only happened to pass for the greyscale themes. */
17
+ color: inherit;
18
+ cursor: pointer;
19
+ }
20
+
21
+ .window-menubar-trigger:hover,
22
+ .window-menubar-trigger:focus-visible,
23
+ .window-menubar-trigger--active {
24
+ background: var(--emphasis-surface);
25
+ color: var(--emphasis-text);
26
+ }
27
+
28
+ .window-menubar-dropdown {
29
+ position: absolute;
30
+ top: 100%;
31
+ left: 0;
32
+ z-index: 1000;
33
+ display: flex;
34
+ flex-direction: column;
35
+ min-width: 7.5rem;
36
+ background: var(--color-light);
37
+ border: var(--border-strong);
38
+ padding: var(--space-xs);
39
+ border-radius: var(--app-radius-surface);
40
+ box-shadow: var(--app-shadow-surface);
41
+ }
42
+
43
+ .window-menubar-dropdown button {
44
+ min-height: var(--control-height);
45
+ padding: var(--menu-item-padding);
46
+ border: none;
47
+ background: none;
48
+ color: inherit;
49
+ text-align: left;
50
+ white-space: nowrap;
51
+ cursor: pointer;
52
+ border-radius: var(--app-radius-control);
53
+ }
54
+
55
+ .window-menubar-dropdown button:hover:not(:disabled),
56
+ .window-menubar-dropdown button:focus-visible:not(:disabled) {
57
+ background: var(--color-muted);
58
+ }
59
+
60
+ .window-menubar-dropdown button:disabled {
61
+ opacity: var(--opacity-disabled);
62
+ cursor: default;
63
+ }
@@ -0,0 +1,14 @@
1
+ export interface WindowMenuItem {
2
+ disabled?: boolean;
3
+ id: string;
4
+ label: string;
5
+ onClick: () => void;
6
+ }
7
+ interface WindowMenu {
8
+ label: string;
9
+ items: WindowMenuItem[];
10
+ }
11
+ export declare function WindowMenuBar({ menus }: {
12
+ menus: WindowMenu[];
13
+ }): import("react").JSX.Element;
14
+ export {};
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import "./WindowMenuBar.css";
4
+ export function WindowMenuBar({ menus }) {
5
+ const [openIndex, setOpenIndex] = useState(null);
6
+ const barRef = useRef(null);
7
+ const close = useCallback(() => setOpenIndex(null), []);
8
+ useEffect(() => {
9
+ if (openIndex === null)
10
+ return;
11
+ function handleMouseDown(e) {
12
+ const target = e.target;
13
+ if (!(target instanceof Node)) {
14
+ close();
15
+ return;
16
+ }
17
+ if (barRef.current && !barRef.current.contains(target)) {
18
+ close();
19
+ }
20
+ }
21
+ document.addEventListener("mousedown", handleMouseDown);
22
+ return () => document.removeEventListener("mousedown", handleMouseDown);
23
+ }, [openIndex, close]);
24
+ return (_jsx("div", { ref: barRef, className: "window-menubar", role: "menubar", children: menus.map((menu, i) => (_jsxs("div", { className: "window-menubar-item", children: [_jsx("button", { type: "button", role: "menuitem", "aria-haspopup": "true", "aria-expanded": openIndex === i, className: openIndex === i
25
+ ? "window-menubar-trigger window-menubar-trigger--active"
26
+ : "window-menubar-trigger", onClick: () => setOpenIndex(openIndex === i ? null : i), onMouseEnter: () => {
27
+ if (openIndex !== null)
28
+ setOpenIndex(i);
29
+ }, children: menu.label }), openIndex === i && (_jsx("div", { className: "window-menubar-dropdown", role: "menu", children: menu.items.map((item) => (_jsx("button", { type: "button", role: "menuitem", disabled: item.disabled, onClick: () => {
30
+ if (item.disabled) {
31
+ return;
32
+ }
33
+ item.onClick();
34
+ close();
35
+ }, children: item.label }, item.id))) }))] }, menu.label))) }));
36
+ }
37
+ //# sourceMappingURL=WindowMenuBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WindowMenuBar.js","sourceRoot":"","sources":["../../../../../../../Users/a2f0/github/tearleads5/packages/windowing/src/window/WindowMenuBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,qBAAqB,CAAC;AAc7B,MAAM,UAAU,aAAa,CAAC,EAAE,KAAK,EAA2B;IAC9D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAE5C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAExD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO;QAC/B,SAAS,eAAe,CAAC,CAAa;YACpC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;YACxB,IAAI,CAAC,CAAC,MAAM,YAAY,IAAI,CAAC,EAAE,CAAC;gBAC9B,KAAK,EAAE,CAAC;gBACR,OAAO;YACT,CAAC;YAED,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvD,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QACxD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAC1E,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IAEvB,OAAO,CACL,cAAK,GAAG,EAAE,MAAM,EAAE,SAAS,EAAC,gBAAgB,EAAC,IAAI,EAAC,SAAS,YACxD,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACtB,eAAsB,SAAS,EAAC,qBAAqB,aACnD,iBACE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,mBACD,MAAM,mBACL,SAAS,KAAK,CAAC,EAC9B,SAAS,EACP,SAAS,KAAK,CAAC;wBACb,CAAC,CAAC,uDAAuD;wBACzD,CAAC,CAAC,wBAAwB,EAE9B,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EACvD,YAAY,EAAE,GAAG,EAAE;wBACjB,IAAI,SAAS,KAAK,IAAI;4BAAE,YAAY,CAAC,CAAC,CAAC,CAAC;oBAC1C,CAAC,YAEA,IAAI,CAAC,KAAK,GACJ,EACR,SAAS,KAAK,CAAC,IAAI,CAClB,cAAK,SAAS,EAAC,yBAAyB,EAAC,IAAI,EAAC,MAAM,YACjD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACxB,iBAEE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,UAAU,EACf,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,OAAO,EAAE,GAAG,EAAE;4BACZ,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gCAClB,OAAO;4BACT,CAAC;4BACD,IAAI,CAAC,OAAO,EAAE,CAAC;4BACf,KAAK,EAAE,CAAC;wBACV,CAAC,YAEA,IAAI,CAAC,KAAK,IAZN,IAAI,CAAC,EAAE,CAaL,CACV,CAAC,GACE,CACP,KAtCO,IAAI,CAAC,KAAK,CAuCd,CACP,CAAC,GACE,CACP,CAAC;AACJ,CAAC","sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport \"./WindowMenuBar.css\";\n\nexport interface WindowMenuItem {\n disabled?: boolean;\n id: string;\n label: string;\n onClick: () => void;\n}\n\ninterface WindowMenu {\n label: string;\n items: WindowMenuItem[];\n}\n\nexport function WindowMenuBar({ menus }: { menus: WindowMenu[] }) {\n const [openIndex, setOpenIndex] = useState<number | null>(null);\n const barRef = useRef<HTMLDivElement>(null);\n\n const close = useCallback(() => setOpenIndex(null), []);\n\n useEffect(() => {\n if (openIndex === null) return;\n function handleMouseDown(e: MouseEvent) {\n const target = e.target;\n if (!(target instanceof Node)) {\n close();\n return;\n }\n\n if (barRef.current && !barRef.current.contains(target)) {\n close();\n }\n }\n document.addEventListener(\"mousedown\", handleMouseDown);\n return () => document.removeEventListener(\"mousedown\", handleMouseDown);\n }, [openIndex, close]);\n\n return (\n <div ref={barRef} className=\"window-menubar\" role=\"menubar\">\n {menus.map((menu, i) => (\n <div key={menu.label} className=\"window-menubar-item\">\n <button\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"true\"\n aria-expanded={openIndex === i}\n className={\n openIndex === i\n ? \"window-menubar-trigger window-menubar-trigger--active\"\n : \"window-menubar-trigger\"\n }\n onClick={() => setOpenIndex(openIndex === i ? null : i)}\n onMouseEnter={() => {\n if (openIndex !== null) setOpenIndex(i);\n }}\n >\n {menu.label}\n </button>\n {openIndex === i && (\n <div className=\"window-menubar-dropdown\" role=\"menu\">\n {menu.items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n onClick={() => {\n if (item.disabled) {\n return;\n }\n item.onClick();\n close();\n }}\n >\n {item.label}\n </button>\n ))}\n </div>\n )}\n </div>\n ))}\n </div>\n );\n}\n"]}