@object-ui/react 4.5.0 → 4.6.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @object-ui/react
2
2
 
3
+ ## 4.6.0
4
+
5
+ ### Patch Changes
6
+
7
+ - @object-ui/types@4.6.0
8
+ - @object-ui/core@4.6.0
9
+ - @object-ui/i18n@4.6.0
10
+ - @object-ui/data-objectstack@4.6.0
11
+
3
12
  ## 4.5.0
4
13
 
5
14
  ### Patch Changes
@@ -30,6 +30,14 @@ export interface RecordContextValue<TData = any, TObjectSchema = any> {
30
30
  error?: Error | null;
31
31
  /** Re-fetch the record from the source. */
32
32
  refresh?: () => void | Promise<void>;
33
+ /**
34
+ * When true, the record is rendered inside a host overlay (drawer,
35
+ * modal, split-pane preview) rather than as a standalone route. Header
36
+ * affordances such as the auto "back to list" button should suppress
37
+ * themselves in this mode because the user already has Close / Expand
38
+ * controls in the overlay chrome.
39
+ */
40
+ embedded?: boolean;
33
41
  }
34
42
  export interface RecordContextProviderProps extends RecordContextValue {
35
43
  children: React.ReactNode;
@@ -27,6 +27,7 @@ export const RecordContextProvider = ({ children, ...value }) => {
27
27
  value.loading,
28
28
  value.error,
29
29
  value.refresh,
30
+ value.embedded,
30
31
  ]);
31
32
  return _jsx(RecordContext.Provider, { value: memo, children: children });
32
33
  };
@@ -28,6 +28,19 @@ export interface UseNavigationOverlayOptions {
28
28
  /** External onRowClick callback — if set, takes full priority */
29
29
  onRowClick?: (record: Record<string, unknown>) => void;
30
30
  }
31
+ /**
32
+ * Optional event-like payload accepted by `handleClick`. We don't depend on
33
+ * React's synthetic event type to keep this hook framework-agnostic — only
34
+ * the few fields needed for modifier detection are read.
35
+ */
36
+ export interface HandleClickModifiers {
37
+ /** macOS Command key — open in new tab when held */
38
+ metaKey?: boolean;
39
+ /** Windows/Linux Control key — open in new tab when held */
40
+ ctrlKey?: boolean;
41
+ /** Mouse button — 1 = middle click (treated as new tab) */
42
+ button?: number;
43
+ }
31
44
  export interface NavigationOverlayState {
32
45
  /** Whether the overlay (drawer/modal/split/popover) is open */
33
46
  isOpen: boolean;
@@ -41,8 +54,15 @@ export interface NavigationOverlayState {
41
54
  open: (record: Record<string, unknown>) => void;
42
55
  /** Set the open state (for controlled Sheet/Dialog `onOpenChange`) */
43
56
  setIsOpen: (open: boolean) => void;
44
- /** The click handler to attach to rows/items */
45
- handleClick: (record: Record<string, unknown>) => void;
57
+ /**
58
+ * The click handler to attach to rows/items.
59
+ *
60
+ * Accepts an optional event (or any object with `metaKey`/`ctrlKey`/`button`)
61
+ * to detect modifier clicks. When `Cmd`/`Ctrl`/middle-click is detected, the
62
+ * record opens in a new browser tab as a full page regardless of the
63
+ * configured mode — matches Linear / Notion / Airtable convention.
64
+ */
65
+ handleClick: (record: Record<string, unknown>, event?: HandleClickModifiers) => void;
46
66
  /** The width from NavigationConfig (for drawer/modal/split sizing) */
47
67
  width: string | number | undefined;
48
68
  /** Whether navigation is an overlay mode (drawer/modal/split/popover) */
@@ -59,12 +59,26 @@ export function useNavigationOverlay(options) {
59
59
  setSelectedRecord(record);
60
60
  setIsOpen(true);
61
61
  }, []);
62
- const handleClick = useCallback((record) => {
63
- // External onRowClick takes full priority
62
+ const handleClick = useCallback((record, event) => {
63
+ // External onRowClick takes full priority. Forward the modifier event
64
+ // so parent handlers (e.g. ObjectView) can still implement Cmd/Ctrl/
65
+ // middle-click → open in new tab.
64
66
  if (onRowClick) {
65
- onRowClick(record);
67
+ onRowClick(record, event);
66
68
  return;
67
69
  }
70
+ // Modifier / middle-click → always open in a new browser tab as a full
71
+ // page. Mirrors browser link convention (Cmd/Ctrl+Click, middle-click)
72
+ // so users can fan out multiple records into tabs from any list/board/
73
+ // gallery, regardless of the configured navigation mode.
74
+ const isModifierClick = !!(event && (event.metaKey || event.ctrlKey || event.button === 1));
75
+ if (isModifierClick) {
76
+ const recordId = record.id || record._id;
77
+ if (onNavigate && recordId != null) {
78
+ onNavigate(recordId, 'new_window');
79
+ return;
80
+ }
81
+ }
68
82
  // No navigation config — default to page navigation
69
83
  if (!navigation) {
70
84
  const recordId = record.id || record._id;
@@ -104,7 +118,7 @@ export function useNavigationOverlay(options) {
104
118
  setIsOpen(true);
105
119
  return;
106
120
  }
107
- }, [onRowClick, navigation, mode, objectName, onNavigate, isOverlay]);
121
+ }, [onRowClick, navigation, mode, objectName, onNavigate, isOverlay, view]);
108
122
  return useMemo(() => ({
109
123
  isOpen,
110
124
  selectedRecord,
@@ -67,8 +67,13 @@ export function usePageAssignment(objectName, opts = {}) {
67
67
  const candidates = pages.filter(p => {
68
68
  if (!p)
69
69
  return false;
70
- const pageType = p.pageType ?? p.type === 'record' ? p.pageType : p.pageType;
71
- const isRecord = (p.pageType ?? (p.type === 'record' ? 'record' : undefined)) === 'record';
70
+ // Only `pageType: 'record'` (or bare `type: 'record'`) is a
71
+ // user-facing record-detail page. `pageType: 'record_detail'` is a
72
+ // distinct concept used by the metadata designer page schemas
73
+ // (`buildObjectDetailPageSchema`) and must NOT be picked up here —
74
+ // otherwise opening any record would render the designer widget.
75
+ const pt = p.pageType ?? (p.type === 'record' ? 'record' : undefined);
76
+ const isRecord = pt === 'record';
72
77
  if (!isRecord)
73
78
  return false;
74
79
  if (p.object !== objectName)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/react",
3
- "version": "4.5.0",
3
+ "version": "4.6.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "React bindings and SchemaRenderer component for Object UI",
@@ -26,10 +26,10 @@
26
26
  "dependencies": {
27
27
  "@objectstack/spec": "^4.0.5",
28
28
  "react-hook-form": "^7.76.0",
29
- "@object-ui/core": "4.5.0",
30
- "@object-ui/data-objectstack": "4.5.0",
31
- "@object-ui/i18n": "4.5.0",
32
- "@object-ui/types": "4.5.0"
29
+ "@object-ui/core": "4.6.0",
30
+ "@object-ui/data-objectstack": "4.6.0",
31
+ "@object-ui/i18n": "4.6.0",
32
+ "@object-ui/types": "4.6.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "react": "^18.0.0 || ^19.0.0",