@oxy.so/bloom 4.1.0 → 4.3.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/docs/adoption-matrix.mdx +7 -7
- package/docs/ai-chat.mdx +8 -0
- package/docs/app-shell.mdx +7 -0
- package/docs/bottom-bar.mdx +6 -0
- package/docs/settings-modal.mdx +14 -5
- package/docs/sidebar.mdx +11 -0
- package/lib/commonjs/ai-chat/AiChatContainer.js +8 -8
- package/lib/commonjs/ai-chat/AiChatContainer.js.map +1 -1
- package/lib/commonjs/app-shell/AppShellBars.js +8 -2
- package/lib/commonjs/app-shell/AppShellBars.js.map +1 -1
- package/lib/commonjs/app-shell/AppShellEngine.js +29 -6
- package/lib/commonjs/app-shell/AppShellEngine.js.map +1 -1
- package/lib/commonjs/bottom-bar/BottomBarBase.js +10 -2
- package/lib/commonjs/bottom-bar/BottomBarBase.js.map +1 -1
- package/lib/commonjs/layout/bottom-bar-slot.js +10 -0
- package/lib/commonjs/layout/bottom-bar-slot.js.map +1 -0
- package/lib/commonjs/settings-modal/SettingsModal.js +153 -230
- package/lib/commonjs/settings-modal/SettingsModal.js.map +1 -1
- package/lib/commonjs/sidebar/Sidebar.js +35 -5
- package/lib/commonjs/sidebar/Sidebar.js.map +1 -1
- package/lib/module/ai-chat/AiChatContainer.js +8 -8
- package/lib/module/ai-chat/AiChatContainer.js.map +1 -1
- package/lib/module/app-shell/AppShellBars.js +8 -2
- package/lib/module/app-shell/AppShellBars.js.map +1 -1
- package/lib/module/app-shell/AppShellEngine.js +30 -7
- package/lib/module/app-shell/AppShellEngine.js.map +1 -1
- package/lib/module/bottom-bar/BottomBarBase.js +11 -3
- package/lib/module/bottom-bar/BottomBarBase.js.map +1 -1
- package/lib/module/layout/bottom-bar-slot.js +7 -0
- package/lib/module/layout/bottom-bar-slot.js.map +1 -0
- package/lib/module/settings-modal/SettingsModal.js +150 -227
- package/lib/module/settings-modal/SettingsModal.js.map +1 -1
- package/lib/module/sidebar/Sidebar.js +35 -5
- package/lib/module/sidebar/Sidebar.js.map +1 -1
- package/lib/typescript/commonjs/ai-chat/types.d.ts +2 -2
- package/lib/typescript/commonjs/ai-chat/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/app-shell/AppShellBars.d.ts +1 -0
- package/lib/typescript/commonjs/app-shell/AppShellBars.d.ts.map +1 -1
- package/lib/typescript/commonjs/app-shell/AppShellEngine.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-bar/BottomBarBase.d.ts +1 -1
- package/lib/typescript/commonjs/bottom-bar/BottomBarBase.d.ts.map +1 -1
- package/lib/typescript/commonjs/bottom-bar/types.d.ts +4 -0
- package/lib/typescript/commonjs/bottom-bar/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/layout/bottom-bar-slot.d.ts +3 -0
- package/lib/typescript/commonjs/layout/bottom-bar-slot.d.ts.map +1 -0
- package/lib/typescript/commonjs/settings-modal/SettingsModal.d.ts +1 -1
- package/lib/typescript/commonjs/settings-modal/SettingsModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/settings-modal/types.d.ts +7 -2
- package/lib/typescript/commonjs/settings-modal/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/sidebar/Sidebar.d.ts.map +1 -1
- package/lib/typescript/commonjs/sidebar/types.d.ts +12 -1
- package/lib/typescript/commonjs/sidebar/types.d.ts.map +1 -1
- package/lib/typescript/module/ai-chat/types.d.ts +2 -2
- package/lib/typescript/module/ai-chat/types.d.ts.map +1 -1
- package/lib/typescript/module/app-shell/AppShellBars.d.ts +1 -0
- package/lib/typescript/module/app-shell/AppShellBars.d.ts.map +1 -1
- package/lib/typescript/module/app-shell/AppShellEngine.d.ts.map +1 -1
- package/lib/typescript/module/bottom-bar/BottomBarBase.d.ts +1 -1
- package/lib/typescript/module/bottom-bar/BottomBarBase.d.ts.map +1 -1
- package/lib/typescript/module/bottom-bar/types.d.ts +4 -0
- package/lib/typescript/module/bottom-bar/types.d.ts.map +1 -1
- package/lib/typescript/module/layout/bottom-bar-slot.d.ts +3 -0
- package/lib/typescript/module/layout/bottom-bar-slot.d.ts.map +1 -0
- package/lib/typescript/module/settings-modal/SettingsModal.d.ts +1 -1
- package/lib/typescript/module/settings-modal/SettingsModal.d.ts.map +1 -1
- package/lib/typescript/module/settings-modal/types.d.ts +7 -2
- package/lib/typescript/module/settings-modal/types.d.ts.map +1 -1
- package/lib/typescript/module/sidebar/Sidebar.d.ts.map +1 -1
- package/lib/typescript/module/sidebar/types.d.ts +12 -1
- package/lib/typescript/module/sidebar/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/support/adoption-matrix.ts +703 -0
- package/src/__tests__/support/collision-fixture-barrel.ts +20 -0
- package/src/__tests__/support/commerce-harness.tsx +97 -0
- package/src/__tests__/support/composite.ts +68 -0
- package/src/__tests__/support/constructed-style-sheets.ts +68 -0
- package/src/__tests__/support/press-host.ts +30 -0
- package/src/__tests__/support/rendered-style.ts +99 -0
- package/src/__tests__/support/unread-hook-fixture.ts +33 -0
- package/src/ai-chat/AiChatContainer.tsx +7 -7
- package/src/ai-chat/types.ts +2 -2
- package/src/app-shell/AppShellBars.tsx +5 -3
- package/src/app-shell/AppShellEngine.tsx +23 -8
- package/src/bottom-bar/BottomBarBase.tsx +7 -4
- package/src/bottom-bar/types.ts +4 -0
- package/src/layout/bottom-bar-slot.ts +4 -0
- package/src/settings-modal/SettingsModal.tsx +180 -250
- package/src/settings-modal/types.ts +7 -2
- package/src/sidebar/Sidebar.tsx +26 -5
- package/src/sidebar/types.ts +12 -1
- package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +9346 -0
- package/src/theme/__tests__/__fixtures__/tonal-glass-primary-failures.json +69 -0
- package/src/theme/__tests__/fixtures/color-engine-golden.json +1 -0
|
@@ -33,11 +33,11 @@ export interface SettingsNavGroup {
|
|
|
33
33
|
|
|
34
34
|
/** One page of the content pane. */
|
|
35
35
|
export interface SettingsModalPage {
|
|
36
|
-
/** Heading of the
|
|
36
|
+
/** Heading of the shared PageHeader. */
|
|
37
37
|
title: string;
|
|
38
38
|
content: ReactNode;
|
|
39
39
|
/**
|
|
40
|
-
* Used on the Storage page: a 6px
|
|
40
|
+
* Used on the Storage page: a 6px gap below PageHeader instead of 12px, because the page
|
|
41
41
|
* already carries 10px of headroom for the dropzone's progress badge.
|
|
42
42
|
*/
|
|
43
43
|
compactTitle?: boolean;
|
|
@@ -66,6 +66,9 @@ export interface SettingsModalProps {
|
|
|
66
66
|
* Imperative mode: the modal finished closing.
|
|
67
67
|
*/
|
|
68
68
|
onClose?: () => void;
|
|
69
|
+
/** Veto an internal leave intention before it changes or unmounts content.
|
|
70
|
+
* External controlled open/page changes remain the caller's authority. */
|
|
71
|
+
onBeforeLeave?: (reason: 'close' | 'navigation' | 'page') => boolean;
|
|
69
72
|
groups: SettingsNavGroup[];
|
|
70
73
|
pages: Record<string, SettingsModalPage>;
|
|
71
74
|
/** Controlled current page. */
|
|
@@ -73,6 +76,8 @@ export interface SettingsModalProps {
|
|
|
73
76
|
/** Page selected every time the modal opens. Defaults to the first navigable row. */
|
|
74
77
|
defaultPage?: string;
|
|
75
78
|
onPageChange?: (page: string) => void;
|
|
79
|
+
/** Compact layout: start at the section list (default) or open the selected page directly. Resets on each open. */
|
|
80
|
+
initialView?: 'navigation' | 'page';
|
|
76
81
|
labels?: SettingsModalLabels;
|
|
77
82
|
testID?: string;
|
|
78
83
|
}
|
package/src/sidebar/Sidebar.tsx
CHANGED
|
@@ -141,6 +141,8 @@ function SearchField({
|
|
|
141
141
|
const SidebarPanel: React.FC<SidebarProps> = ({
|
|
142
142
|
items = [],
|
|
143
143
|
primaryAction,
|
|
144
|
+
content,
|
|
145
|
+
onScroll,
|
|
144
146
|
footer,
|
|
145
147
|
secondaryItems = [],
|
|
146
148
|
modes,
|
|
@@ -210,7 +212,7 @@ const SidebarPanel: React.FC<SidebarProps> = ({
|
|
|
210
212
|
|
|
211
213
|
// `fluid` fills its container while expanded; the morph needs a number, so
|
|
212
214
|
// the last expanded width is measured. The collapsed end is the size's own.
|
|
213
|
-
const geometry = useMemo(() => resolveSidebarGeometry(metrics, surface, Boolean(primaryAction), Boolean(tree)), [metrics, surface, primaryAction, tree]);
|
|
215
|
+
const geometry = useMemo(() => resolveSidebarGeometry(metrics, surface, Boolean(primaryAction), Boolean(tree) || content != null), [metrics, surface, primaryAction, tree, content]);
|
|
214
216
|
const { collapsedWidth, collapsedLane, expandedInset } = geometry;
|
|
215
217
|
const expandedWidth = useSharedValue(metrics.expanded);
|
|
216
218
|
useEffect(() => {
|
|
@@ -232,6 +234,13 @@ const SidebarPanel: React.FC<SidebarProps> = ({
|
|
|
232
234
|
paddingLeft: padding, paddingRight: padding,
|
|
233
235
|
};
|
|
234
236
|
}, [progress, expandedWidth, fluid, collapsedWidth, expandedPadding, collapsedPadding]);
|
|
237
|
+
const [contentHeight, setContentHeight] = useState(0);
|
|
238
|
+
const contentMorph = useAnimatedStyle(() => ({
|
|
239
|
+
height: contentHeight > 0 ? contentHeight * (1 - progress.value) : collapsed ? 0 : undefined,
|
|
240
|
+
opacity: 1 - progress.value,
|
|
241
|
+
marginTop: tree ? 0 : 24 * (1 - progress.value),
|
|
242
|
+
}), [contentHeight, collapsed, progress, tree]);
|
|
243
|
+
const contentWidth = useAnimatedStyle(() => ({ width: expandedWidth.value - expandedPadding * 2 }), [expandedWidth, expandedPadding]);
|
|
235
244
|
const navInset = useAnimatedStyle(() => {
|
|
236
245
|
const inset = expandedInset * (1 - progress.value);
|
|
237
246
|
return { paddingLeft: inset, paddingRight: inset };
|
|
@@ -388,7 +397,7 @@ const SidebarPanel: React.FC<SidebarProps> = ({
|
|
|
388
397
|
setSuppressUserHover(expanding);
|
|
389
398
|
};
|
|
390
399
|
|
|
391
|
-
const headerControl = flatMobile ? (
|
|
400
|
+
const headerControl = flatMobile && showSearch ? (
|
|
392
401
|
<View
|
|
393
402
|
ref={searchFieldRef}
|
|
394
403
|
style={{
|
|
@@ -534,8 +543,8 @@ const SidebarPanel: React.FC<SidebarProps> = ({
|
|
|
534
543
|
[tree, normalized],
|
|
535
544
|
);
|
|
536
545
|
const nothingMatches =
|
|
537
|
-
shownItems.length === 0 && shownSecondary.length === 0 && shownFolders.length === 0 && !collapsed;
|
|
538
|
-
const hasTree = !!tree;
|
|
546
|
+
shownItems.length === 0 && shownSecondary.length === 0 && shownFolders.length === 0 && content == null && !collapsed;
|
|
547
|
+
const hasTree = !!tree || content != null;
|
|
539
548
|
|
|
540
549
|
// A fresh closure here would re-render the memoised switcher on every
|
|
541
550
|
// keystroke in the quick-search field.
|
|
@@ -642,8 +651,9 @@ const SidebarPanel: React.FC<SidebarProps> = ({
|
|
|
642
651
|
testID={`${testID ?? 'sidebar'}-scroll`}
|
|
643
652
|
{...(IS_WEB ? { dataSet: { bloomSidebarScroll: 'none' } } : {})}
|
|
644
653
|
style={{ marginTop: -headerOverlap, marginBottom: -8, marginLeft: -8, marginRight: -8, flexGrow: 0, flexShrink: 1, minHeight: 0 }}
|
|
645
|
-
contentContainerStyle={{ paddingTop: headerOverlap + 12, paddingBottom: 8, paddingLeft: 8, paddingRight: 8, gap:
|
|
654
|
+
contentContainerStyle={{ paddingTop: headerOverlap + 12, paddingBottom: 8, paddingLeft: 8, paddingRight: 8, gap: tree ? 24 : 0 }}
|
|
646
655
|
showsVerticalScrollIndicator={false}
|
|
656
|
+
onScroll={onScroll}
|
|
647
657
|
>
|
|
648
658
|
{shownItems.length > 0 || !hasTree ? (
|
|
649
659
|
<Animated.View role="navigation" style={[{ width: '100%', gap: 4 }, !hasTree && navInset]}>
|
|
@@ -665,6 +675,17 @@ const SidebarPanel: React.FC<SidebarProps> = ({
|
|
|
665
675
|
</View>
|
|
666
676
|
</View>
|
|
667
677
|
) : null}
|
|
678
|
+
{content != null ? (
|
|
679
|
+
<Animated.View testID={`${testID ?? 'sidebar'}-content`} pointerEvents={collapsed ? 'none' : 'auto'}
|
|
680
|
+
aria-hidden={collapsed} accessibilityElementsHidden={collapsed}
|
|
681
|
+
importantForAccessibility={collapsed ? 'no-hide-descendants' : 'auto'}
|
|
682
|
+
{...(IS_WEB && collapsed ? { inert: true } : {})}
|
|
683
|
+
style={[{ overflow: 'hidden', flexShrink: 0 }, contentMorph]}>
|
|
684
|
+
<Animated.View onLayout={(event) => setContentHeight(event.nativeEvent.layout.height)} style={contentWidth}>
|
|
685
|
+
{content}
|
|
686
|
+
</Animated.View>
|
|
687
|
+
</Animated.View>
|
|
688
|
+
) : null}
|
|
668
689
|
{nothingMatches && hasTree ? (
|
|
669
690
|
<Text variant="body-regular" style={{ paddingLeft: 8, paddingRight: 8, color: palette.textTertiary }}>{noResultsLabel}</Text>
|
|
670
691
|
) : null}
|
package/src/sidebar/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { BloomIconComponent } from '../icons/icon-component';
|
|
3
|
-
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { StyleProp, ViewStyle, ScrollViewProps } from 'react-native';
|
|
4
4
|
|
|
5
5
|
/** A Remix-style icon: `width` / `height` / `fill`. */
|
|
6
6
|
/** @deprecated Use `BloomIconComponent` from `@oxy.so/bloom/icons`; this is an alias of it. */
|
|
@@ -232,6 +232,17 @@ export interface SidebarUserMenuProps {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
export interface SidebarProps {
|
|
235
|
+
/** App-owned destination content after the built-in rows/tree, inside the same
|
|
236
|
+
* scroll viewport. Hidden and noninteractive while collapsed; remains mounted
|
|
237
|
+
* so pagination and disclosure state survive. Not rendered in variant="rail".
|
|
238
|
+
* Compose rows here, never another ScrollView. Quick search filters only the
|
|
239
|
+
* built-in items/tree; disable it when your content owns search.
|
|
240
|
+
*/
|
|
241
|
+
content?: ReactNode;
|
|
242
|
+
/** Observes the destination viewport after Bloom updates its edge fades.
|
|
243
|
+
* Useful for loading another page of history. No separate scroll owner.
|
|
244
|
+
*/
|
|
245
|
+
onScroll?: ScrollViewProps['onScroll'];
|
|
235
246
|
/** Additional persistent bottom content, outside destination scrolling.
|
|
236
247
|
* Receives the rendered state: true in a rail, false in a mobile panel.
|
|
237
248
|
* Existing account/theme/team controls remain; omit account when supplying your own account footer.
|