@byline/admin 6.1.0 → 6.2.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 (119) hide show
  1. package/dist/forms/form-layout-styles.test.node.d.ts +8 -0
  2. package/dist/forms/form-layout.d.ts +39 -0
  3. package/dist/forms/form-layout.js +100 -0
  4. package/dist/forms/form-page-chrome.d.ts +110 -0
  5. package/dist/forms/form-page-chrome.js +253 -0
  6. package/dist/forms/form-renderer.d.ts +2 -18
  7. package/dist/forms/form-renderer.js +104 -418
  8. package/dist/forms/form-renderer.module.js +2 -0
  9. package/dist/forms/form-renderer_module.css +13 -0
  10. package/dist/forms/use-form-submission.d.ts +74 -0
  11. package/dist/forms/use-form-submission.js +166 -0
  12. package/dist/forms/use-form-tabs.d.ts +29 -0
  13. package/dist/forms/use-form-tabs.js +45 -0
  14. package/dist/presentation/tab-strip-drag.d.ts +75 -0
  15. package/dist/presentation/tab-strip-drag.js +25 -0
  16. package/dist/presentation/tab-strip-drag.test.node.d.ts +8 -0
  17. package/dist/presentation/tab-strip-geometry.d.ts +75 -0
  18. package/dist/presentation/tab-strip-geometry.js +36 -0
  19. package/dist/presentation/tab-strip-geometry.test.node.d.ts +8 -0
  20. package/dist/presentation/tabs.d.ts +30 -7
  21. package/dist/presentation/tabs.js +260 -29
  22. package/dist/presentation/tabs.module.js +11 -1
  23. package/dist/presentation/tabs_module.css +166 -5
  24. package/package.json +7 -7
  25. package/src/forms/form-layout-styles.test.node.ts +32 -0
  26. package/src/forms/form-layout.test.tsx +276 -0
  27. package/src/forms/form-layout.tsx +198 -0
  28. package/src/forms/form-page-chrome.tsx +460 -0
  29. package/src/forms/form-renderer-submit.test.tsx +40 -0
  30. package/src/forms/form-renderer-tab-persistence.test.tsx +145 -0
  31. package/src/forms/form-renderer.module.css +18 -0
  32. package/src/forms/form-renderer.tsx +115 -593
  33. package/src/forms/use-form-submission.test.tsx +564 -0
  34. package/src/forms/use-form-submission.ts +293 -0
  35. package/src/forms/use-form-tabs.ts +89 -0
  36. package/src/presentation/tab-strip-drag.test.node.ts +143 -0
  37. package/src/presentation/tab-strip-drag.ts +114 -0
  38. package/src/presentation/tab-strip-geometry.test.node.ts +254 -0
  39. package/src/presentation/tab-strip-geometry.ts +158 -0
  40. package/src/presentation/tabs.module.css +209 -11
  41. package/src/presentation/tabs.test.tsx +481 -0
  42. package/src/presentation/tabs.tsx +369 -34
  43. package/dist/exports-parity.test.node.js +0 -37
  44. package/dist/fields/array/array-field.jsx +0 -209
  45. package/dist/fields/blocks/blocks-field.jsx +0 -248
  46. package/dist/fields/checkbox/checkbox-field.jsx +0 -24
  47. package/dist/fields/code/code-editor.jsx +0 -211
  48. package/dist/fields/code/code-field.jsx +0 -109
  49. package/dist/fields/column-formatter.jsx +0 -25
  50. package/dist/fields/date-time-formatter.jsx +0 -18
  51. package/dist/fields/datetime/datetime-field.jsx +0 -22
  52. package/dist/fields/draggable-context-menu.jsx +0 -50
  53. package/dist/fields/field-admin.test.node.js +0 -40
  54. package/dist/fields/field-renderer.jsx +0 -136
  55. package/dist/fields/field-services-context.jsx +0 -17
  56. package/dist/fields/file/file-field.jsx +0 -176
  57. package/dist/fields/file/file-upload-field.jsx +0 -89
  58. package/dist/fields/group/group-field.jsx +0 -46
  59. package/dist/fields/image/image-field.jsx +0 -183
  60. package/dist/fields/image/image-upload-field.jsx +0 -157
  61. package/dist/fields/local-date-time.jsx +0 -69
  62. package/dist/fields/locale-badge.jsx +0 -19
  63. package/dist/fields/numerical/numerical-field.jsx +0 -93
  64. package/dist/fields/relation/relation-column-formatter.jsx +0 -74
  65. package/dist/fields/relation/relation-display.jsx +0 -118
  66. package/dist/fields/relation/relation-field.jsx +0 -108
  67. package/dist/fields/relation/relation-many-field.jsx +0 -142
  68. package/dist/fields/relation/relation-picker.jsx +0 -240
  69. package/dist/fields/relation/relation-summary.jsx +0 -45
  70. package/dist/fields/select/select-field.jsx +0 -27
  71. package/dist/fields/sortable-item.jsx +0 -54
  72. package/dist/fields/text/text-field.jsx +0 -79
  73. package/dist/fields/text-area/text-area-field.jsx +0 -79
  74. package/dist/forms/available-locales-reconcile.test.node.js +0 -43
  75. package/dist/forms/available-locales-widget.jsx +0 -63
  76. package/dist/forms/document-actions.jsx +0 -486
  77. package/dist/forms/form-context.jsx +0 -598
  78. package/dist/forms/form-modals.jsx +0 -128
  79. package/dist/forms/form-renderer.jsx +0 -532
  80. package/dist/forms/form-status-display.jsx +0 -69
  81. package/dist/forms/navigation-guard.jsx +0 -68
  82. package/dist/forms/nested-path.test.node.js +0 -115
  83. package/dist/forms/path-widget.jsx +0 -125
  84. package/dist/forms/pending-uploads.test.node.js +0 -19
  85. package/dist/forms/repeating-items.test.node.js +0 -29
  86. package/dist/forms/scheduled-publication-control.jsx +0 -389
  87. package/dist/forms/scheduled-publication-state.test.node.js +0 -121
  88. package/dist/forms/scheduled-publication-time.test.node.js +0 -70
  89. package/dist/forms/status-transitions.test.node.js +0 -76
  90. package/dist/forms/tree-placement-widget.jsx +0 -160
  91. package/dist/forms/upload-executor.test.node.js +0 -368
  92. package/dist/forms/use-form-layout.test.node.js +0 -69
  93. package/dist/modules/admin-account/components/change-password.jsx +0 -169
  94. package/dist/modules/admin-account/components/container.jsx +0 -164
  95. package/dist/modules/admin-account/components/preferences.jsx +0 -148
  96. package/dist/modules/admin-account/components/theme-switch.jsx +0 -52
  97. package/dist/modules/admin-account/components/update.jsx +0 -186
  98. package/dist/modules/admin-permissions/components/inspector.jsx +0 -200
  99. package/dist/modules/admin-preferences/schemas.test.node.js +0 -48
  100. package/dist/modules/admin-roles/components/create.jsx +0 -155
  101. package/dist/modules/admin-roles/components/permissions.jsx +0 -239
  102. package/dist/modules/admin-roles/components/update.jsx +0 -148
  103. package/dist/modules/admin-users/components/create.jsx +0 -206
  104. package/dist/modules/admin-users/components/roles.jsx +0 -126
  105. package/dist/modules/admin-users/components/set-password.jsx +0 -133
  106. package/dist/modules/admin-users/components/update.jsx +0 -215
  107. package/dist/modules/analytics/components/dashboard.jsx +0 -208
  108. package/dist/modules/analytics/components/dashboard.test.node.js +0 -162
  109. package/dist/modules/analytics/components/timeseries.jsx +0 -193
  110. package/dist/modules/auth/components/sign-in-form.jsx +0 -89
  111. package/dist/modules/auth/safe-redirect.test.node.js +0 -40
  112. package/dist/modules/auth/sign-in-form-props.test.node.js +0 -7
  113. package/dist/presentation/group.jsx +0 -26
  114. package/dist/presentation/row.jsx +0 -23
  115. package/dist/presentation/tabs.jsx +0 -39
  116. package/dist/services/admin-services-context.jsx +0 -17
  117. package/dist/widgets/diff-viewer/diff-modal.jsx +0 -110
  118. package/dist/widgets/source-locale-badge/source-locale-badge.jsx +0 -28
  119. package/dist/widgets/status-badge/status-badge.jsx +0 -46
@@ -1,39 +1,270 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
3
4
  import { useTranslation } from "@byline/i18n/react";
4
- import { Badge } from "@byline/ui/react";
5
+ import { Badge, Dropdown, EllipsisIcon } from "@byline/ui/react";
5
6
  import clsx from "clsx";
7
+ import { advanceDrag, shouldStartDrag, shouldSuppressClick } from "./tab-strip-drag.js";
8
+ import { computeRevealScroll, computeStripState } from "./tab-strip-geometry.js";
6
9
  import tabs_module from "./tabs.module.js";
7
- const AdminTabs = ({ tabs, activeTab, onChange, errorCounts, className })=>{
10
+ const useIsoLayoutEffect = "u" > typeof window ? useLayoutEffect : useEffect;
11
+ const TRIGGER_SIZE_PX = 32;
12
+ const TRIGGER_GAP_PX = 12;
13
+ const INITIAL_STRIP_STATE = {
14
+ overflowing: false,
15
+ viewportWidth: 0,
16
+ atStart: true,
17
+ atEnd: true
18
+ };
19
+ const tabTriggerId = (idBase, name)=>`${idBase}-tab-${name}`;
20
+ const tabPanelId = (idBase, name)=>`${idBase}-panel-${name}`;
21
+ function prefersReducedMotion() {
22
+ if ("u" < typeof window || 'function' != typeof window.matchMedia) return false;
23
+ return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
24
+ }
25
+ const AdminTabs = ({ tabs, activeTab, onChange, idBase, errorCounts, className })=>{
8
26
  const { t } = useTranslation('byline-admin');
9
- return /*#__PURE__*/ jsx("div", {
10
- role: "tablist",
11
- "aria-label": t('presentation.formTabsAriaLabel'),
27
+ const containerRef = useRef(null);
28
+ const viewportRef = useRef(null);
29
+ const rowRef = useRef(null);
30
+ const tabRefs = useRef(new Map());
31
+ const focusAfterChange = useRef(null);
32
+ const [strip, setStrip] = useState(INITIAL_STRIP_STATE);
33
+ const measure = useCallback(()=>{
34
+ const container = containerRef.current;
35
+ const row = rowRef.current;
36
+ if (null == container || null == row) return;
37
+ const next = computeStripState({
38
+ rowWidth: row.scrollWidth,
39
+ containerWidth: container.clientWidth,
40
+ triggerWidth: TRIGGER_SIZE_PX,
41
+ triggerGap: TRIGGER_GAP_PX,
42
+ scrollLeft: viewportRef.current?.scrollLeft ?? 0
43
+ });
44
+ setStrip((prev)=>prev.overflowing === next.overflowing && prev.viewportWidth === next.viewportWidth && prev.atStart === next.atStart && prev.atEnd === next.atEnd ? prev : next);
45
+ }, []);
46
+ useIsoLayoutEffect(()=>{
47
+ measure();
48
+ const container = containerRef.current;
49
+ const row = rowRef.current;
50
+ if (null == container || null == row || "u" < typeof ResizeObserver) return;
51
+ const observer = new ResizeObserver(()=>measure());
52
+ observer.observe(container);
53
+ observer.observe(row);
54
+ return ()=>observer.disconnect();
55
+ }, [
56
+ measure,
57
+ tabs
58
+ ]);
59
+ const reveal = useCallback((name)=>{
60
+ const viewport = viewportRef.current;
61
+ const row = rowRef.current;
62
+ const tab = tabRefs.current.get(name);
63
+ if (null == viewport || null == row || null == tab) return;
64
+ const next = computeRevealScroll({
65
+ viewportWidth: viewport.clientWidth,
66
+ scrollLeft: viewport.scrollLeft,
67
+ tabOffsetLeft: tab.offsetLeft,
68
+ tabWidth: tab.offsetWidth,
69
+ rowWidth: row.scrollWidth
70
+ });
71
+ if (null == next) return;
72
+ viewport.scrollTo?.({
73
+ left: next,
74
+ behavior: prefersReducedMotion() ? 'auto' : 'smooth'
75
+ });
76
+ }, []);
77
+ useEffect(()=>{
78
+ const pending = focusAfterChange.current;
79
+ if (pending === activeTab) {
80
+ focusAfterChange.current = null;
81
+ tabRefs.current.get(activeTab)?.focus({
82
+ preventScroll: true
83
+ });
84
+ }
85
+ reveal(activeTab);
86
+ }, [
87
+ activeTab,
88
+ reveal
89
+ ]);
90
+ const select = useCallback((name, moveFocus)=>{
91
+ if (moveFocus) focusAfterChange.current = name;
92
+ onChange(name);
93
+ }, [
94
+ onChange
95
+ ]);
96
+ const dragRef = useRef(null);
97
+ const dragEndedAtRef = useRef(null);
98
+ const [dragging, setDragging] = useState(false);
99
+ const handlePointerDown = (event)=>{
100
+ const viewport = viewportRef.current;
101
+ if (null == viewport) return;
102
+ if (false === shouldStartDrag({
103
+ pointerType: event.pointerType,
104
+ button: event.button,
105
+ overflowing: strip.overflowing
106
+ })) return;
107
+ dragRef.current = {
108
+ origin: {
109
+ pointerX: event.clientX,
110
+ scrollLeft: viewport.scrollLeft
111
+ },
112
+ dragging: false
113
+ };
114
+ };
115
+ const handlePointerMove = (event)=>{
116
+ const state = dragRef.current;
117
+ const viewport = viewportRef.current;
118
+ const row = rowRef.current;
119
+ if (null == state || null == viewport || null == row) return;
120
+ const maxScroll = Math.max(0, row.scrollWidth - viewport.clientWidth);
121
+ const next = advanceDrag(state, event.clientX, maxScroll);
122
+ if (null == next.scrollLeft) return;
123
+ if (false === state.dragging) {
124
+ state.dragging = true;
125
+ setDragging(true);
126
+ viewport.setPointerCapture?.(event.pointerId);
127
+ }
128
+ viewport.scrollLeft = next.scrollLeft;
129
+ };
130
+ const handlePointerEnd = (event)=>{
131
+ const state = dragRef.current;
132
+ if (null == state) return;
133
+ dragRef.current = null;
134
+ if (false === state.dragging) return;
135
+ setDragging(false);
136
+ viewportRef.current?.releasePointerCapture?.(event.pointerId);
137
+ dragEndedAtRef.current = performance.now();
138
+ };
139
+ const handleClickCapture = (event)=>{
140
+ if (false === shouldSuppressClick(dragEndedAtRef.current, performance.now())) return;
141
+ dragEndedAtRef.current = null;
142
+ event.stopPropagation();
143
+ event.preventDefault();
144
+ };
145
+ const handleKeyDown = (event)=>{
146
+ const current = tabs.findIndex((tab)=>tab.name === activeTab);
147
+ if (current < 0 || 0 === tabs.length) return;
148
+ let nextIndex;
149
+ switch(event.key){
150
+ case 'ArrowRight':
151
+ nextIndex = (current + 1) % tabs.length;
152
+ break;
153
+ case 'ArrowLeft':
154
+ nextIndex = (current - 1 + tabs.length) % tabs.length;
155
+ break;
156
+ case 'Home':
157
+ nextIndex = 0;
158
+ break;
159
+ case 'End':
160
+ nextIndex = tabs.length - 1;
161
+ break;
162
+ default:
163
+ return;
164
+ }
165
+ event.preventDefault();
166
+ const next = tabs[nextIndex];
167
+ if (null == next || next.name === activeTab) return;
168
+ select(next.name, true);
169
+ };
170
+ const sizing = {
171
+ '--byline-admin-tabs-trigger-size': `${TRIGGER_SIZE_PX}px`,
172
+ '--byline-admin-tabs-trigger-gap': `${TRIGGER_GAP_PX}px`
173
+ };
174
+ return /*#__PURE__*/ jsxs("div", {
175
+ ref: containerRef,
176
+ style: sizing,
12
177
  className: clsx('byline-admin-tabs', tabs_module.tabs, className),
13
- children: tabs.map((tab)=>{
14
- const isActive = tab.name === activeTab;
15
- return /*#__PURE__*/ jsx("button", {
16
- type: "button",
17
- role: "tab",
18
- "aria-selected": isActive,
19
- onClick: ()=>onChange(tab.name),
20
- className: clsx('byline-admin-tab', tabs_module.tab, isActive && [
21
- 'byline-admin-tab-active',
22
- tabs_module["tab-active"]
23
- ]),
24
- children: /*#__PURE__*/ jsxs("span", {
25
- className: clsx('byline-admin-tab-label', tabs_module.label),
26
- children: [
27
- tab.label,
28
- (errorCounts?.[tab.name] ?? 0) > 0 && /*#__PURE__*/ jsx(Badge, {
29
- intent: "danger",
30
- className: clsx('byline-admin-tab-badge', tabs_module.badge),
31
- children: errorCounts?.[tab.name]
32
- })
33
- ]
178
+ children: [
179
+ /*#__PURE__*/ jsx("div", {
180
+ ref: viewportRef,
181
+ onScroll: measure,
182
+ onPointerDown: handlePointerDown,
183
+ onPointerMove: handlePointerMove,
184
+ onPointerUp: handlePointerEnd,
185
+ onPointerCancel: handlePointerEnd,
186
+ onClickCapture: handleClickCapture,
187
+ "data-at-start": strip.atStart,
188
+ "data-at-end": strip.atEnd,
189
+ "data-draggable": strip.overflowing,
190
+ "data-dragging": dragging,
191
+ className: clsx('byline-admin-tabs-viewport', tabs_module.viewport),
192
+ children: /*#__PURE__*/ jsx("div", {
193
+ ref: rowRef,
194
+ role: "tablist",
195
+ "aria-label": t('presentation.formTabsAriaLabel'),
196
+ onKeyDown: handleKeyDown,
197
+ className: clsx('byline-admin-tablist', tabs_module.tablist),
198
+ children: tabs.map((tab)=>{
199
+ const isActive = tab.name === activeTab;
200
+ const errorCount = errorCounts?.[tab.name] ?? 0;
201
+ return /*#__PURE__*/ jsx("button", {
202
+ ref: (node)=>{
203
+ if (null == node) tabRefs.current.delete(tab.name);
204
+ else tabRefs.current.set(tab.name, node);
205
+ },
206
+ type: "button",
207
+ role: "tab",
208
+ id: tabTriggerId(idBase, tab.name),
209
+ "aria-controls": tabPanelId(idBase, tab.name),
210
+ "aria-selected": isActive,
211
+ tabIndex: isActive ? 0 : -1,
212
+ onClick: ()=>select(tab.name, false),
213
+ onFocus: ()=>reveal(tab.name),
214
+ className: clsx('byline-admin-tab', tabs_module.tab, isActive && [
215
+ 'byline-admin-tab-active',
216
+ tabs_module["tab-active"]
217
+ ]),
218
+ children: /*#__PURE__*/ jsxs("span", {
219
+ className: clsx('byline-admin-tab-label', tabs_module.label),
220
+ children: [
221
+ tab.label,
222
+ errorCount > 0 && /*#__PURE__*/ jsx(Badge, {
223
+ intent: "danger",
224
+ className: clsx('byline-admin-tab-badge', tabs_module.badge),
225
+ children: errorCount
226
+ })
227
+ ]
228
+ })
229
+ }, tab.name);
230
+ })
34
231
  })
35
- }, tab.name);
36
- })
232
+ }),
233
+ strip.overflowing && /*#__PURE__*/ jsxs(Dropdown.Root, {
234
+ children: [
235
+ /*#__PURE__*/ jsx(Dropdown.Trigger, {
236
+ "aria-label": t('presentation.formTabsAllTabsLabel'),
237
+ className: clsx('byline-admin-tabs-overflow-trigger', tabs_module["overflow-trigger"]),
238
+ children: /*#__PURE__*/ jsx(EllipsisIcon, {
239
+ className: clsx('byline-admin-tabs-overflow-icon', tabs_module["overflow-icon"])
240
+ })
241
+ }),
242
+ /*#__PURE__*/ jsx(Dropdown.Portal, {
243
+ children: /*#__PURE__*/ jsx(Dropdown.Content, {
244
+ sideOffset: 6,
245
+ align: "end",
246
+ className: clsx('byline-admin-tabs-overflow-menu', tabs_module["overflow-menu"]),
247
+ children: tabs.map((tab)=>{
248
+ const errorCount = errorCounts?.[tab.name] ?? 0;
249
+ return /*#__PURE__*/ jsxs(Dropdown.Item, {
250
+ onClick: ()=>select(tab.name, true),
251
+ "data-active": tab.name === activeTab,
252
+ className: clsx('byline-admin-tabs-overflow-item', tabs_module["overflow-item"]),
253
+ children: [
254
+ tab.label,
255
+ errorCount > 0 && /*#__PURE__*/ jsx(Badge, {
256
+ intent: "danger",
257
+ className: clsx('byline-admin-tab-badge', tabs_module.badge),
258
+ children: errorCount
259
+ })
260
+ ]
261
+ }, tab.name);
262
+ })
263
+ })
264
+ })
265
+ ]
266
+ })
267
+ ]
37
268
  });
38
269
  };
39
- export { AdminTabs };
270
+ export { AdminTabs, tabPanelId, tabTriggerId };
@@ -1,10 +1,20 @@
1
1
  import "./tabs_module.css";
2
2
  const tabs_module = {
3
3
  tabs: "tabs-AUVgW2",
4
+ viewport: "viewport-Q34w7N",
5
+ tablist: "tablist-a3JXdm",
4
6
  tab: "tab-Tn7xcQ",
5
7
  "tab-active": "tab-active-cGf5N5",
6
8
  tabActive: "tab-active-cGf5N5",
7
9
  label: "label-PvJgbb",
8
- badge: "badge-NJVOEv"
10
+ badge: "badge-NJVOEv",
11
+ "overflow-trigger": "overflow-trigger-sQPQE8",
12
+ overflowTrigger: "overflow-trigger-sQPQE8",
13
+ "overflow-icon": "overflow-icon-E4aR_Z",
14
+ overflowIcon: "overflow-icon-E4aR_Z",
15
+ "overflow-menu": "overflow-menu-BkkmwS",
16
+ overflowMenu: "overflow-menu-BkkmwS",
17
+ "overflow-item": "overflow-item-ahgTA4",
18
+ overflowItem: "overflow-item-ahgTA4"
9
19
  };
10
20
  export default tabs_module;
@@ -1,18 +1,109 @@
1
1
  :is(.tabs-AUVgW2, .byline-admin-tabs) {
2
+ align-items: stretch;
3
+ gap: var(--byline-admin-tabs-trigger-gap, 12px);
4
+ display: flex;
5
+ position: relative;
6
+ }
7
+
8
+ .tabs-AUVgW2:after {
9
+ content: "";
10
+ z-index: 0;
11
+ height: var(--border-width-thin);
12
+ background-color: var(--gray-200);
13
+ position: absolute;
14
+ bottom: 0;
15
+ left: 0;
16
+ right: 0;
17
+ }
18
+
19
+ .byline-admin-tabs:after {
20
+ content: "";
21
+ z-index: 0;
22
+ height: var(--border-width-thin);
23
+ background-color: var(--gray-200);
24
+ position: absolute;
25
+ bottom: 0;
26
+ left: 0;
27
+ right: 0;
28
+ }
29
+
30
+ :is(.viewport-Q34w7N, .byline-admin-tabs-viewport) {
31
+ z-index: 1;
32
+ overscroll-behavior-x: contain;
33
+ scrollbar-width: none;
34
+ flex: auto;
35
+ min-width: 0;
36
+ position: relative;
37
+ overflow: auto hidden;
38
+ }
39
+
40
+ .viewport-Q34w7N::-webkit-scrollbar {
41
+ display: none;
42
+ }
43
+
44
+ .byline-admin-tabs-viewport::-webkit-scrollbar {
45
+ display: none;
46
+ }
47
+
48
+ :is(.viewport-Q34w7N[data-draggable="true"], .byline-admin-tabs-viewport[data-draggable="true"]) {
49
+ cursor: grab;
50
+ }
51
+
52
+ :is(.viewport-Q34w7N[data-dragging="true"], .byline-admin-tabs-viewport[data-dragging="true"]) {
53
+ cursor: grabbing;
54
+ -webkit-user-select: none;
55
+ user-select: none;
56
+ }
57
+
58
+ :is(.viewport-Q34w7N[data-dragging="true"] .byline-admin-tab, .byline-admin-tabs-viewport[data-dragging="true"] .byline-admin-tab) {
59
+ cursor: grabbing;
60
+ }
61
+
62
+ :is(.viewport-Q34w7N, .byline-admin-tabs-viewport) {
63
+ --fade-start: 0px;
64
+ --fade-end: 0px;
65
+ -webkit-mask-image: linear-gradient(to right,
66
+ transparent 0,
67
+ black var(--fade-start),
68
+ black calc(100% - var(--fade-end)),
69
+ transparent 100%);
70
+ -webkit-mask-image: linear-gradient(to right,
71
+ transparent 0,
72
+ black var(--fade-start),
73
+ black calc(100% - var(--fade-end)),
74
+ transparent 100%);
75
+ mask-image: linear-gradient(to right,
76
+ transparent 0,
77
+ black var(--fade-start),
78
+ black calc(100% - var(--fade-end)),
79
+ transparent 100%);
80
+ }
81
+
82
+ :is(.viewport-Q34w7N[data-at-start="false"], .byline-admin-tabs-viewport[data-at-start="false"]) {
83
+ --fade-start: 1.5rem;
84
+ }
85
+
86
+ :is(.viewport-Q34w7N[data-at-end="false"], .byline-admin-tabs-viewport[data-at-end="false"]) {
87
+ --fade-end: 1.5rem;
88
+ }
89
+
90
+ :is(.tablist-a3JXdm, .byline-admin-tablist) {
2
91
  gap: var(--spacing-16);
3
- border-bottom: var(--border-width-thin) var(--border-style-solid) var(--gray-200);
92
+ width: max-content;
4
93
  display: flex;
94
+ position: relative;
5
95
  }
6
96
 
7
97
  :is(.tab-Tn7xcQ, .byline-admin-tab) {
8
98
  color: var(--gray-500);
9
99
  font-size: 1.1rem;
10
100
  font-weight: var(--font-weight-medium);
101
+ white-space: nowrap;
11
102
  cursor: pointer;
12
103
  background: none;
13
104
  border-bottom: 2px solid #0000;
14
105
  outline: none;
15
- margin-bottom: -1px;
106
+ flex: none;
16
107
  padding: .625rem 0;
17
108
  transition: color .15s, border-color .15s;
18
109
  position: relative;
@@ -24,7 +115,8 @@
24
115
  }
25
116
 
26
117
  :is(.tab-Tn7xcQ:focus-visible, .byline-admin-tab:focus-visible) {
27
- box-shadow: 0 0 0 2px var(--blue-500);
118
+ box-shadow: inset 0 0 0 2px var(--blue-500);
119
+ border-radius: var(--radius-sm, 3px);
28
120
  }
29
121
 
30
122
  :is(.tab-active-cGf5N5, .byline-admin-tab-active), :is(.tab-active-cGf5N5:hover, .byline-admin-tab-active:hover) {
@@ -48,8 +140,64 @@
48
140
  display: flex;
49
141
  }
50
142
 
51
- :is(:is([data-theme="dark"], .dark) .tabs-AUVgW2, :is([data-theme="dark"], .dark) .byline-admin-tabs) {
52
- border-bottom-color: var(--gray-700);
143
+ :is(.overflow-trigger-sQPQE8, .byline-admin-tabs-overflow-trigger) {
144
+ z-index: 1;
145
+ width: var(--byline-admin-tabs-trigger-size, 32px);
146
+ height: var(--byline-admin-tabs-trigger-size, 32px);
147
+ border: var(--border-width-thin) var(--border-style-solid) transparent;
148
+ border-radius: var(--radius-sm, 3px);
149
+ color: var(--gray-500);
150
+ cursor: pointer;
151
+ background: none;
152
+ flex: none;
153
+ justify-content: center;
154
+ align-self: center;
155
+ align-items: center;
156
+ padding: 0;
157
+ transition: color .15s, background-color .15s;
158
+ display: flex;
159
+ position: relative;
160
+ }
161
+
162
+ :is(.overflow-trigger-sQPQE8:hover, .byline-admin-tabs-overflow-trigger:hover) {
163
+ color: var(--gray-800);
164
+ background-color: var(--gray-100);
165
+ }
166
+
167
+ :is(.overflow-trigger-sQPQE8:focus-visible, .byline-admin-tabs-overflow-trigger:focus-visible) {
168
+ outline: 2px solid var(--blue-500);
169
+ outline-offset: 1px;
170
+ }
171
+
172
+ :is(.overflow-icon-E4aR_Z, .byline-admin-tabs-overflow-icon) {
173
+ width: 1.125rem;
174
+ height: 1.125rem;
175
+ }
176
+
177
+ :is(.overflow-menu-BkkmwS, .byline-admin-tabs-overflow-menu) {
178
+ min-width: 12rem;
179
+ max-height: 60vh;
180
+ overflow-y: auto;
181
+ }
182
+
183
+ :is(.overflow-item-ahgTA4, .byline-admin-tabs-overflow-item) {
184
+ justify-content: space-between;
185
+ align-items: center;
186
+ gap: var(--spacing-8);
187
+ display: flex;
188
+ }
189
+
190
+ :is(.overflow-item-ahgTA4[data-active="true"], .byline-admin-tabs-overflow-item[data-active="true"]) {
191
+ color: var(--primary-600);
192
+ font-weight: var(--font-weight-medium);
193
+ }
194
+
195
+ :is([data-theme="dark"], .dark) .tabs-AUVgW2:after {
196
+ background-color: var(--gray-700);
197
+ }
198
+
199
+ :is([data-theme="dark"], .dark) .byline-admin-tabs:after {
200
+ background-color: var(--gray-700);
53
201
  }
54
202
 
55
203
  :is(:is([data-theme="dark"], .dark) .tab-Tn7xcQ, :is([data-theme="dark"], .dark) .byline-admin-tab) {
@@ -66,3 +214,16 @@
66
214
  border-bottom-color: var(--primary-400);
67
215
  }
68
216
 
217
+ :is(:is([data-theme="dark"], .dark) .overflow-trigger-sQPQE8, :is([data-theme="dark"], .dark) .byline-admin-tabs-overflow-trigger) {
218
+ color: var(--gray-400);
219
+ }
220
+
221
+ :is(:is([data-theme="dark"], .dark) .overflow-trigger-sQPQE8:hover, :is([data-theme="dark"], .dark) .byline-admin-tabs-overflow-trigger:hover) {
222
+ color: var(--gray-200);
223
+ background-color: var(--gray-800);
224
+ }
225
+
226
+ :is(:is([data-theme="dark"], .dark) .overflow-item-ahgTA4[data-active="true"], :is([data-theme="dark"], .dark) .byline-admin-tabs-overflow-item[data-active="true"]) {
227
+ color: var(--primary-200);
228
+ }
229
+
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/admin",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "6.1.0",
5
+ "version": "6.2.1",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },
@@ -183,12 +183,12 @@
183
183
  "react-diff-viewer-continued": "^4.4.0",
184
184
  "uuid": "^14.0.2",
185
185
  "zod": "^4.6.2",
186
- "@byline/analytics": "6.1.0",
187
- "@byline/analytics-agent": "6.1.0",
188
- "@byline/core": "6.1.0",
189
- "@byline/auth": "6.1.0",
190
- "@byline/i18n": "6.1.0",
191
- "@byline/ui": "6.1.0"
186
+ "@byline/analytics": "6.2.1",
187
+ "@byline/auth": "6.2.1",
188
+ "@byline/ui": "6.2.1",
189
+ "@byline/core": "6.2.1",
190
+ "@byline/analytics-agent": "6.2.1",
191
+ "@byline/i18n": "6.2.1"
192
192
  },
193
193
  "peerDependencies": {
194
194
  "react": "^19.0.0",
@@ -0,0 +1,32 @@
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
+
9
+ import { readFileSync } from 'node:fs'
10
+ import { join } from 'node:path'
11
+
12
+ import { describe, expect, it } from 'vitest'
13
+
14
+ // jsdom applies no stylesheet, so the one-column guarantee of the stacked
15
+ // variant cannot be asserted by rendering. This reads the stylesheet directly:
16
+ // the class has to exist, and it must not be swept into the two-column grid
17
+ // rule that the split layout gets at >=60rem.
18
+ const css = readFileSync(join(__dirname, 'form-renderer.module.css'), 'utf8')
19
+
20
+ describe('stacked form layout styles', () => {
21
+ it('defines the stacked layout class', () => {
22
+ expect(css).toMatch(/\.layout-stacked\b/)
23
+ expect(css).toMatch(/:global\(\.byline-form-layout-stacked\)/)
24
+ })
25
+
26
+ it('does not put the stacked class in the two-column grid rule', () => {
27
+ const desktop = css.slice(css.indexOf('@media (min-width: 60rem)'))
28
+ const gridRule = desktop.slice(0, desktop.indexOf('}\n}') + 3)
29
+ expect(gridRule).toContain('grid-template-columns')
30
+ expect(gridRule).not.toContain('layout-stacked')
31
+ })
32
+ })