@byline/host-tanstack-start 6.8.0 → 6.9.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.
Files changed (32) hide show
  1. package/dist/admin-shell/chrome/admin-app-bar.d.ts +0 -7
  2. package/dist/admin-shell/collections/api.d.ts +6 -2
  3. package/dist/admin-shell/collections/api.js +109 -3
  4. package/dist/admin-shell/collections/api.test.d.ts +8 -0
  5. package/dist/admin-shell/collections/edit.js +2 -0
  6. package/dist/admin-shell/collections/preview-link.js +17 -13
  7. package/dist/admin-shell/collections/view-menu.d.ts +8 -1
  8. package/dist/admin-shell/collections/view-menu.js +124 -107
  9. package/dist/admin-shell/collections/view-menu.module.js +1 -0
  10. package/dist/admin-shell/collections/view-menu_module.css +15 -1
  11. package/dist/admin-shell/scheduled-publications/list.js +2 -2
  12. package/dist/admin-shell/singletons/preview-link.js +17 -13
  13. package/dist/admin-shell/singletons/view-menu.js +78 -63
  14. package/dist/admin-shell/singletons/view.js +2 -0
  15. package/dist/server-fns/collections/get.d.ts +1 -0
  16. package/dist/server-fns/collections/get.js +4 -0
  17. package/package.json +10 -10
  18. package/src/admin-shell/chrome/admin-app-bar.tsx +2 -2
  19. package/src/admin-shell/chrome/dashboard.test.tsx +2 -2
  20. package/src/admin-shell/collections/api.test.tsx +223 -0
  21. package/src/admin-shell/collections/api.tsx +98 -3
  22. package/src/admin-shell/collections/edit.tsx +9 -1
  23. package/src/admin-shell/collections/preview-link.tsx +13 -12
  24. package/src/admin-shell/collections/view-menu.module.css +29 -3
  25. package/src/admin-shell/collections/view-menu.tsx +114 -96
  26. package/src/admin-shell/scheduled-publications/list.tsx +3 -4
  27. package/src/admin-shell/singletons/history.test.tsx +1 -1
  28. package/src/admin-shell/singletons/preview-link.tsx +13 -12
  29. package/src/admin-shell/singletons/view-menu.tsx +65 -59
  30. package/src/admin-shell/singletons/view.test.tsx +4 -4
  31. package/src/admin-shell/singletons/view.tsx +3 -1
  32. package/src/server-fns/collections/get.ts +3 -0
@@ -1,10 +1,3 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
1
  import { type CurrentAdminUser } from '../../server-fns/auth/index.js';
9
2
  interface AdminAppBarProps {
10
3
  user: CurrentAdminUser;
@@ -12,13 +12,17 @@ import 'react-json-view-lite/dist/index.css';
12
12
  /**
13
13
  * JSON inspector view for a collection document.
14
14
  *
15
+ * Copy JSON and Download JSON render in the view menu's data group, beside the
16
+ * locale and depth controls whose selection they export.
17
+ *
15
18
  * Stable override handles: `.byline-api-section`, `.byline-api-container`,
16
- * `.byline-api-head`, `.byline-api-title`, `.byline-api-viewer`.
19
+ * `.byline-api-head`, `.byline-api-title`, `.byline-api-action`,
20
+ * `.byline-api-viewer`.
17
21
  */
18
22
  export declare const ApiView: ({ collectionDefinition, initialData, locale, depth, contentLocales, defaultContentLocale, }: {
19
23
  collectionDefinition: MultiCollectionDefinition;
20
24
  initialData: AnyCollectionSchemaTypes['UpdateType'];
21
- /** Currently active locale from the route search param (undefined → all). */
25
+ /** Locale from the route search param (undefined → the default content locale). */
22
26
  locale?: string;
23
27
  /** Populate depth from the route search param (undefined → 0, no populate). */
24
28
  depth?: number;
@@ -1,6 +1,7 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
2
3
  import { useTranslation } from "@byline/i18n/react";
3
- import { Container, Section } from "@byline/ui/react";
4
+ import { Container, CopyIcon, DownloadIcon, IconButton, Section, Tooltip, useToastManager } from "@byline/ui/react";
4
5
  import clsx from "clsx";
5
6
  import { JsonView, darkStyles } from "react-json-view-lite";
6
7
  import api_module from "./api.module.js";
@@ -10,6 +11,72 @@ const expandToFirstLevel = (level)=>level < 2;
10
11
  const ApiView = ({ collectionDefinition, initialData, locale, depth, contentLocales, defaultContentLocale })=>{
11
12
  const { labels, path } = collectionDefinition;
12
13
  const { t } = useTranslation('byline-admin');
14
+ const toastManager = useToastManager();
15
+ const [copyPending, setCopyPending] = useState(false);
16
+ const copyJson = async ()=>{
17
+ setCopyPending(true);
18
+ try {
19
+ await navigator.clipboard.writeText(JSON.stringify(initialData, null, 2));
20
+ toastManager.add({
21
+ title: t('collections.api.copied'),
22
+ data: {
23
+ intent: 'success',
24
+ iconType: 'success',
25
+ icon: true,
26
+ close: true
27
+ }
28
+ });
29
+ } catch {
30
+ toastManager.add({
31
+ title: t('collections.api.copyFailed'),
32
+ data: {
33
+ intent: 'danger',
34
+ iconType: 'danger',
35
+ icon: true,
36
+ close: true
37
+ }
38
+ });
39
+ } finally{
40
+ setCopyPending(false);
41
+ }
42
+ };
43
+ const downloadJson = ()=>{
44
+ try {
45
+ const blob = new Blob([
46
+ JSON.stringify(initialData, null, 2)
47
+ ], {
48
+ type: 'application/json;charset=utf-8'
49
+ });
50
+ const url = URL.createObjectURL(blob);
51
+ const link = document.createElement('a');
52
+ try {
53
+ link.href = url;
54
+ const selection = [
55
+ path,
56
+ String(initialData.id),
57
+ locale ?? defaultContentLocale
58
+ ];
59
+ if (null != depth && depth > 0) selection.push(`depth${depth}`);
60
+ const filename = selection.join('-').replace(/[^a-zA-Z0-9_-]/g, '-');
61
+ link.download = `${filename}.json`;
62
+ document.body.appendChild(link);
63
+ link.click();
64
+ } finally{
65
+ link.remove();
66
+ setTimeout(()=>URL.revokeObjectURL(url), 1000);
67
+ }
68
+ } catch {
69
+ toastManager.add({
70
+ title: t('collections.api.downloadFailed'),
71
+ data: {
72
+ intent: 'danger',
73
+ iconType: 'danger',
74
+ icon: true,
75
+ close: true
76
+ }
77
+ });
78
+ }
79
+ };
13
80
  return /*#__PURE__*/ jsx(Section, {
14
81
  className: clsx('byline-api-section', api_module.section),
15
82
  children: /*#__PURE__*/ jsxs(Container, {
@@ -31,7 +98,46 @@ const ApiView = ({ collectionDefinition, initialData, locale, depth, contentLoca
31
98
  locale: locale,
32
99
  depth: depth,
33
100
  contentLocales: contentLocales,
34
- defaultContentLocale: defaultContentLocale
101
+ defaultContentLocale: defaultContentLocale,
102
+ dataActions: /*#__PURE__*/ jsxs(Fragment, {
103
+ children: [
104
+ /*#__PURE__*/ jsx(Tooltip, {
105
+ text: t('collections.api.copyJson'),
106
+ side: "bottom",
107
+ delay: 0,
108
+ children: /*#__PURE__*/ jsx(IconButton, {
109
+ type: "button",
110
+ size: "xs",
111
+ variant: "text",
112
+ className: "byline-view-menu-icon-button byline-api-action",
113
+ "aria-label": t('collections.api.copyJson'),
114
+ disabled: copyPending,
115
+ onClick: copyJson,
116
+ children: /*#__PURE__*/ jsx(CopyIcon, {
117
+ width: "18px",
118
+ height: "18px"
119
+ })
120
+ })
121
+ }),
122
+ /*#__PURE__*/ jsx(Tooltip, {
123
+ text: t('collections.api.downloadJson'),
124
+ side: "bottom",
125
+ delay: 0,
126
+ children: /*#__PURE__*/ jsx(IconButton, {
127
+ type: "button",
128
+ size: "xs",
129
+ variant: "text",
130
+ className: "byline-view-menu-icon-button byline-api-action",
131
+ "aria-label": t('collections.api.downloadJson'),
132
+ onClick: downloadJson,
133
+ children: /*#__PURE__*/ jsx(DownloadIcon, {
134
+ width: "20px",
135
+ height: "20px"
136
+ })
137
+ })
138
+ })
139
+ ]
140
+ })
35
141
  })
36
142
  ]
37
143
  }),
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};
@@ -297,6 +297,7 @@ const EditView = ({ collectionDefinition, adminConfig, initialData: loadedData,
297
297
  }
298
298
  };
299
299
  const publishedVersion = initialData?._publishedVersion ?? null;
300
+ const unavailableBlocks = initialData?._unavailableBlocks;
300
301
  const restoreWarnings = initialData?._restoreWarnings;
301
302
  const handleUnpublish = async ()=>{
302
303
  try {
@@ -863,6 +864,7 @@ const EditView = ({ collectionDefinition, adminConfig, initialData: loadedData,
863
864
  contentLocales: contentLocales,
864
865
  publishedVersion: publishedVersion,
865
866
  restoreWarnings: restoreWarnings,
867
+ unavailableBlocks: unavailableBlocks,
866
868
  nextStatus: nextStatus,
867
869
  workflowStatuses: workflowStatuses,
868
870
  onCancel: ()=>{
@@ -1,7 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { useTranslation } from "@byline/i18n/react";
4
- import { ExternalLinkIcon, IconButton, useToastManager } from "@byline/ui/react";
4
+ import { ExternalLinkIcon, IconButton, Tooltip, useToastManager } from "@byline/ui/react";
5
5
  import clsx from "clsx";
6
6
  import { enablePreviewModeFn } from "../../server-fns/preview/index.js";
7
7
  import { resolvePreviewUrl } from "./resolve-preview-url.js";
@@ -34,18 +34,22 @@ const PreviewLink = ({ collectionPath, doc, adminConfig, locale, className })=>{
34
34
  setBusy(false);
35
35
  }
36
36
  };
37
- return /*#__PURE__*/ jsx(IconButton, {
38
- className: clsx('byline-preview-link', className),
39
- size: "xs",
40
- variant: "text",
41
- disabled: busy,
42
- onClick: handleClick,
43
- "aria-label": t('collections.preview.openAriaLabel'),
44
- title: t('collections.preview.title'),
45
- children: /*#__PURE__*/ jsx(ExternalLinkIcon, {
46
- width: "20px",
47
- height: "20px",
48
- className: "byline-preview-link-icon"
37
+ return /*#__PURE__*/ jsx(Tooltip, {
38
+ text: t('collections.preview.title'),
39
+ side: "bottom",
40
+ delay: 0,
41
+ children: /*#__PURE__*/ jsx(IconButton, {
42
+ className: clsx('byline-preview-link', className),
43
+ size: "xs",
44
+ variant: "text",
45
+ disabled: busy,
46
+ onClick: handleClick,
47
+ "aria-label": t('collections.preview.openAriaLabel'),
48
+ children: /*#__PURE__*/ jsx(ExternalLinkIcon, {
49
+ width: "20px",
50
+ height: "20px",
51
+ className: "byline-preview-link-icon"
52
+ })
49
53
  })
50
54
  });
51
55
  };
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * Copyright (c) Infonomic Company Limited
7
7
  */
8
+ import type { ReactNode } from 'react';
8
9
  import type { CollectionAdminConfig, PreviewDocument } from '@byline/core';
9
10
  export type ViewMenuPaths = 'edit' | 'history' | 'api';
10
11
  export interface ContentLocaleOption {
@@ -27,7 +28,7 @@ export interface ContentLocaleOption {
27
28
  * route shell (which reads them from the host's i18n config) so this
28
29
  * component stays free of host-specific imports.
29
30
  */
30
- export declare const ViewMenu: ({ collection, documentId, activeView, locale, depth, contentLocales, defaultContentLocale, adminConfig, doc, }: {
31
+ export declare const ViewMenu: ({ collection, documentId, activeView, locale, depth, contentLocales, defaultContentLocale, adminConfig, doc, dataActions, }: {
31
32
  /** Collection path (e.g. "docs", "news"). */
32
33
  collection: string;
33
34
  /** The document ID to navigate to. */
@@ -54,4 +55,10 @@ export declare const ViewMenu: ({ collection, documentId, activeView, locale, de
54
55
  * entirely (e.g. on the create route, where no doc exists yet).
55
56
  */
56
57
  doc?: PreviewDocument;
58
+ /**
59
+ * Controls that act on the data selected by the locale and depth controls
60
+ * (the API view's Copy / Download JSON). Rendered first, in the same group
61
+ * as those controls, so they wrap together ahead of the view navigation.
62
+ */
63
+ dataActions?: ReactNode;
57
64
  }) => import("react").JSX.Element;
@@ -1,13 +1,13 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from "react";
3
3
  import { useTranslation } from "@byline/i18n/react";
4
- import { Button, HistoryIcon, IconButton, Label, Select } from "@byline/ui/react";
4
+ import { Button, HistoryIcon, IconButton, Label, Select, Tooltip } from "@byline/ui/react";
5
5
  import clsx from "clsx";
6
6
  import { getAdminRoutePath } from "../../routes/admin-path.js";
7
7
  import { useNavigate } from "../chrome/loose-router.js";
8
8
  import { PreviewLink } from "./preview-link.js";
9
9
  import view_menu_module from "./view-menu.module.js";
10
- const ViewMenu = ({ collection, documentId, activeView, locale, depth, contentLocales, defaultContentLocale, adminConfig, doc })=>{
10
+ const ViewMenu = ({ collection, documentId, activeView, locale, depth, contentLocales, defaultContentLocale, adminConfig, doc, dataActions })=>{
11
11
  const navigate = useNavigate();
12
12
  const { t } = useTranslation('byline-admin');
13
13
  useEffect(()=>{
@@ -79,126 +79,143 @@ const ViewMenu = ({ collection, documentId, activeView, locale, depth, contentLo
79
79
  return /*#__PURE__*/ jsxs("div", {
80
80
  className: clsx('byline-view-menu', view_menu_module.root),
81
81
  children: [
82
- /*#__PURE__*/ jsx(Label, {
83
- className: clsx('muted byline-view-menu-label', view_menu_module.label),
84
- id: "contentLocaleLabel",
85
- htmlFor: "contentLocale",
86
- label: t('collections.viewMenu.contentLocaleLabel')
87
- }),
88
- /*#__PURE__*/ jsx(Select, {
89
- name: "contentLocale",
90
- id: "contentLocale",
91
- className: clsx('byline-view-menu-locale-select', view_menu_module.localeSelect),
92
- size: "xs",
93
- variant: "outlined",
94
- value: locale ?? defaultContentLocale,
95
- items: [
96
- ...'edit' !== activeView ? [
97
- {
98
- value: 'all',
99
- label: t('collections.viewMenu.localeAll')
100
- }
101
- ] : [],
102
- ...contentLocales.map((loc)=>({
103
- value: loc.code,
104
- label: loc.label
105
- }))
106
- ],
107
- onValueChange: handleLocaleChange
108
- }),
109
- 'api' === activeView && /*#__PURE__*/ jsxs(Fragment, {
82
+ /*#__PURE__*/ jsxs("div", {
83
+ className: clsx('byline-view-menu-data', view_menu_module.group),
110
84
  children: [
85
+ dataActions,
111
86
  /*#__PURE__*/ jsx(Label, {
112
87
  className: clsx('muted byline-view-menu-label', view_menu_module.label),
113
- id: "populateDepthLabel",
114
- htmlFor: "populateDepth",
115
- label: t('collections.viewMenu.depthLabel')
88
+ id: "contentLocaleLabel",
89
+ htmlFor: "contentLocale",
90
+ label: t('collections.viewMenu.contentLocaleLabel')
116
91
  }),
117
92
  /*#__PURE__*/ jsx(Select, {
118
- name: "populateDepth",
119
- id: "populateDepth",
120
- className: clsx('byline-view-menu-depth-select', view_menu_module.depthSelect),
93
+ name: "contentLocale",
94
+ id: "contentLocale",
95
+ className: clsx('byline-view-menu-locale-select', view_menu_module.localeSelect),
121
96
  size: "xs",
122
97
  variant: "outlined",
123
- value: String(depth ?? 0),
98
+ value: locale ?? defaultContentLocale,
124
99
  items: [
125
- {
126
- value: '0',
127
- label: '0'
128
- },
129
- {
130
- value: '1',
131
- label: '1'
132
- },
133
- {
134
- value: '2',
135
- label: '2'
136
- },
137
- {
138
- value: '3',
139
- label: '3'
140
- }
100
+ ...'edit' !== activeView ? [
101
+ {
102
+ value: 'all',
103
+ label: t('collections.viewMenu.localeAll')
104
+ }
105
+ ] : [],
106
+ ...contentLocales.map((loc)=>({
107
+ value: loc.code,
108
+ label: loc.label
109
+ }))
141
110
  ],
142
- onValueChange: handleDepthChange
111
+ onValueChange: handleLocaleChange
112
+ }),
113
+ 'api' === activeView && /*#__PURE__*/ jsxs(Fragment, {
114
+ children: [
115
+ /*#__PURE__*/ jsx(Label, {
116
+ className: clsx('muted byline-view-menu-label', view_menu_module.label),
117
+ id: "populateDepthLabel",
118
+ htmlFor: "populateDepth",
119
+ label: t('collections.viewMenu.depthLabel')
120
+ }),
121
+ /*#__PURE__*/ jsx(Select, {
122
+ name: "populateDepth",
123
+ id: "populateDepth",
124
+ className: clsx('byline-view-menu-depth-select', view_menu_module.depthSelect),
125
+ size: "xs",
126
+ variant: "outlined",
127
+ value: String(depth ?? 0),
128
+ items: [
129
+ {
130
+ value: '0',
131
+ label: '0'
132
+ },
133
+ {
134
+ value: '1',
135
+ label: '1'
136
+ },
137
+ {
138
+ value: '2',
139
+ label: '2'
140
+ },
141
+ {
142
+ value: '3',
143
+ label: '3'
144
+ }
145
+ ],
146
+ onValueChange: handleDepthChange
147
+ })
148
+ ]
143
149
  })
144
150
  ]
145
151
  }),
146
- doc && /*#__PURE__*/ jsx(PreviewLink, {
147
- collectionPath: collection,
148
- doc: doc,
149
- adminConfig: adminConfig,
150
- locale: locale,
151
- className: clsx('byline-view-menu-icon-button', view_menu_module.iconButton)
152
- }),
153
- /*#__PURE__*/ jsx(IconButton, {
154
- className: clsx('byline-view-menu-icon-button', view_menu_module.iconButton),
155
- size: "xs",
156
- variant: 'history' === activeView ? 'filled' : 'text',
157
- onClick: ()=>navigate({
158
- to: getAdminRoutePath('collections', '$collection', '$id', 'history'),
159
- params: {
160
- collection,
161
- id: documentId
162
- },
163
- search: locale ? {
164
- locale
165
- } : {}
152
+ /*#__PURE__*/ jsxs("div", {
153
+ className: clsx('byline-view-menu-nav', view_menu_module.group),
154
+ children: [
155
+ doc && /*#__PURE__*/ jsx(PreviewLink, {
156
+ collectionPath: collection,
157
+ doc: doc,
158
+ adminConfig: adminConfig,
159
+ locale: locale,
160
+ className: clsx('byline-view-menu-icon-button', view_menu_module.iconButton)
166
161
  }),
167
- children: /*#__PURE__*/ jsx(HistoryIcon, {
168
- className: clsx('byline-view-menu-icon-button-icon', view_menu_module.iconButtonIcon)
169
- })
170
- }),
171
- /*#__PURE__*/ jsx(Button, {
172
- size: "xs",
173
- variant: 'edit' === activeView ? 'filled' : 'outlined',
174
- className: clsx('byline-view-menu-button', view_menu_module.button),
175
- onClick: ()=>navigate({
176
- to: getAdminRoutePath('collections', '$collection', '$id'),
177
- params: {
178
- collection,
179
- id: documentId
180
- },
181
- search: locale ? {
182
- locale
183
- } : {}
162
+ /*#__PURE__*/ jsx(Tooltip, {
163
+ text: t('collections.breadcrumbs.history'),
164
+ side: "bottom",
165
+ delay: 0,
166
+ children: /*#__PURE__*/ jsx(IconButton, {
167
+ className: clsx('byline-view-menu-icon-button', view_menu_module.iconButton),
168
+ size: "xs",
169
+ variant: 'history' === activeView ? 'filled' : 'text',
170
+ "aria-label": t('collections.breadcrumbs.history'),
171
+ onClick: ()=>navigate({
172
+ to: getAdminRoutePath('collections', '$collection', '$id', 'history'),
173
+ params: {
174
+ collection,
175
+ id: documentId
176
+ },
177
+ search: locale ? {
178
+ locale
179
+ } : {}
180
+ }),
181
+ children: /*#__PURE__*/ jsx(HistoryIcon, {
182
+ className: clsx('byline-view-menu-icon-button-icon', view_menu_module.iconButtonIcon)
183
+ })
184
+ })
184
185
  }),
185
- children: t('common.actions.edit')
186
- }),
187
- /*#__PURE__*/ jsx(Button, {
188
- size: "xs",
189
- variant: 'api' === activeView ? 'filled' : 'outlined',
190
- className: clsx('byline-view-menu-button', view_menu_module.button),
191
- onClick: ()=>navigate({
192
- to: getAdminRoutePath('collections', '$collection', '$id', 'api'),
193
- params: {
194
- collection,
195
- id: documentId
196
- },
197
- search: locale ? {
198
- locale
199
- } : {}
186
+ /*#__PURE__*/ jsx(Button, {
187
+ size: "xs",
188
+ variant: 'edit' === activeView ? 'filled' : 'outlined',
189
+ className: clsx('byline-view-menu-button', view_menu_module.button),
190
+ onClick: ()=>navigate({
191
+ to: getAdminRoutePath('collections', '$collection', '$id'),
192
+ params: {
193
+ collection,
194
+ id: documentId
195
+ },
196
+ search: locale ? {
197
+ locale
198
+ } : {}
199
+ }),
200
+ children: t('common.actions.edit')
200
201
  }),
201
- children: t('collections.viewMenu.apiButton')
202
+ /*#__PURE__*/ jsx(Button, {
203
+ size: "xs",
204
+ variant: 'api' === activeView ? 'filled' : 'outlined',
205
+ className: clsx('byline-view-menu-button', view_menu_module.button),
206
+ onClick: ()=>navigate({
207
+ to: getAdminRoutePath('collections', '$collection', '$id', 'api'),
208
+ params: {
209
+ collection,
210
+ id: documentId
211
+ },
212
+ search: locale ? {
213
+ locale
214
+ } : {}
215
+ }),
216
+ children: t('collections.viewMenu.apiButton')
217
+ })
218
+ ]
202
219
  })
203
220
  ]
204
221
  });
@@ -1,6 +1,7 @@
1
1
  import "./view-menu_module.css";
2
2
  const view_menu_module = {
3
3
  root: "root-vI0s6C",
4
+ group: "group-cJt4nm",
4
5
  label: "label-GcmVPn",
5
6
  localeSelect: "localeSelect-JCYsTa",
6
7
  depthSelect: "depthSelect-_wY_6k",
@@ -1,11 +1,25 @@
1
1
  :is(.root-vI0s6C, .byline-view-menu) {
2
+ flex-wrap: wrap;
2
3
  align-items: center;
3
- gap: .5rem;
4
+ gap: .5rem 1.25rem;
4
5
  margin-top: .5rem;
5
6
  margin-bottom: .5rem;
6
7
  display: flex;
7
8
  }
8
9
 
10
+ @media (min-width: 40rem) {
11
+ :is(.root-vI0s6C, .byline-view-menu) {
12
+ justify-content: flex-end;
13
+ }
14
+ }
15
+
16
+ :is(.group-cJt4nm, .byline-view-menu-data, .byline-view-menu-nav) {
17
+ flex-wrap: wrap;
18
+ align-items: center;
19
+ gap: .5rem;
20
+ display: flex;
21
+ }
22
+
9
23
  :is(.label-GcmVPn, .byline-view-menu-label) {
10
24
  color: var(--gray-400);
11
25
  padding-top: 2px;
@@ -1,7 +1,5 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { DocumentMutationNotice } from "../document-mutation-notice.js";
4
- import { useDocumentMutationState } from "../document-mutation-state.js";
5
3
  import { useMemo, useState } from "react";
6
4
  import { useRouterState } from "@tanstack/react-router";
7
5
  import { deriveScheduledPublicationState } from "@byline/admin/react";
@@ -12,6 +10,8 @@ import { getAdminRoutePath } from "../../routes/admin-path.js";
12
10
  import { cancelCollectionDocumentScheduledPublish } from "../../server-fns/collections/index.js";
13
11
  import { Link, useNavigate } from "../chrome/loose-router.js";
14
12
  import { RouterPager } from "../chrome/router-pager.js";
13
+ import { DocumentMutationNotice } from "../document-mutation-notice.js";
14
+ import { useDocumentMutationState } from "../document-mutation-state.js";
15
15
  import list_module from "./list.module.js";
16
16
  const STATE_INTENT = {
17
17
  none: 'noeffect',
@@ -2,7 +2,7 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { useTranslation } from "@byline/i18n/react";
5
- import { ExternalLinkIcon, IconButton, useToastManager } from "@byline/ui/react";
5
+ import { ExternalLinkIcon, IconButton, Tooltip, useToastManager } from "@byline/ui/react";
6
6
  import clsx from "clsx";
7
7
  import { enablePreviewModeFn } from "../../server-fns/preview/index.js";
8
8
  function SingletonPreviewLink({ doc, adminConfig, locale, className }) {
@@ -36,18 +36,22 @@ function SingletonPreviewLink({ doc, adminConfig, locale, className }) {
36
36
  setBusy(false);
37
37
  }
38
38
  };
39
- return /*#__PURE__*/ jsx(IconButton, {
40
- className: clsx('byline-preview-link', className),
41
- size: "xs",
42
- variant: "text",
43
- disabled: busy,
44
- onClick: handleClick,
45
- "aria-label": t('collections.preview.openAriaLabel'),
46
- title: t('collections.preview.title'),
47
- children: /*#__PURE__*/ jsx(ExternalLinkIcon, {
48
- width: "20px",
49
- height: "20px",
50
- className: "byline-preview-link-icon"
39
+ return /*#__PURE__*/ jsx(Tooltip, {
40
+ text: t('collections.preview.title'),
41
+ side: "bottom",
42
+ delay: 0,
43
+ children: /*#__PURE__*/ jsx(IconButton, {
44
+ className: clsx('byline-preview-link', className),
45
+ size: "xs",
46
+ variant: "text",
47
+ disabled: busy,
48
+ onClick: handleClick,
49
+ "aria-label": t('collections.preview.openAriaLabel'),
50
+ children: /*#__PURE__*/ jsx(ExternalLinkIcon, {
51
+ width: "20px",
52
+ height: "20px",
53
+ className: "byline-preview-link-icon"
54
+ })
51
55
  })
52
56
  });
53
57
  }