@duro-app/ui 5.12.0 → 5.13.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/dist/Table-Bms2ftt3.js +1694 -0
- package/dist/Table-Bms2ftt3.js.map +1 -0
- package/dist/components/AppShell/native.css.d.ts +53 -0
- package/dist/components/AppShell/native.css.d.ts.map +1 -0
- package/dist/components/Drawer/Drawer.d.ts +7 -35
- package/dist/components/Drawer/Drawer.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.meta.d.ts.map +1 -1
- package/dist/components/Drawer/DrawerScroll.stories.d.ts.map +1 -1
- package/dist/components/Drawer/DrawerSnap.stories.d.ts +20 -0
- package/dist/components/Drawer/DrawerSnap.stories.d.ts.map +1 -0
- package/dist/components/Drawer/native.css.d.ts +55 -0
- package/dist/components/Drawer/native.css.d.ts.map +1 -0
- package/dist/components/Drawer/parts.css.d.ts +96 -0
- package/dist/components/Drawer/parts.css.d.ts.map +1 -0
- package/dist/components/Drawer/parts.d.ts +56 -0
- package/dist/components/Drawer/parts.d.ts.map +1 -0
- package/dist/components/Drawer/sheetPhysics.d.ts +56 -0
- package/dist/components/Drawer/sheetPhysics.d.ts.map +1 -0
- package/dist/components/Drawer/styles.css.d.ts +13 -58
- package/dist/components/Drawer/styles.css.d.ts.map +1 -1
- package/dist/components/Drawer/touchInput.d.ts +35 -0
- package/dist/components/Drawer/touchInput.d.ts.map +1 -0
- package/dist/components/Drawer/useDrawerRoot.d.ts +33 -0
- package/dist/components/Drawer/useDrawerRoot.d.ts.map +1 -0
- package/dist/components/Drawer/useSheetHeights.d.ts +7 -0
- package/dist/components/Drawer/useSheetHeights.d.ts.map +1 -0
- package/dist/components/Drawer/useSwipeDismiss.d.ts +13 -2
- package/dist/components/Drawer/useSwipeDismiss.d.ts.map +1 -1
- package/dist/components/Menu/Menu.d.ts +7 -1
- package/dist/components/Menu/Menu.d.ts.map +1 -1
- package/dist/components/Menu/native.css.d.ts +14 -0
- package/dist/components/Menu/native.css.d.ts.map +1 -0
- package/dist/components/Menu/useMenuOpen.d.ts +16 -0
- package/dist/components/Menu/useMenuOpen.d.ts.map +1 -0
- package/dist/components/Menu/useMenuRoot.d.ts +3 -6
- package/dist/components/Menu/useMenuRoot.d.ts.map +1 -1
- package/dist/components/Popover/native.css.d.ts +11 -0
- package/dist/components/Popover/native.css.d.ts.map +1 -0
- package/dist/components/SideNav/Chevron.d.ts +4 -0
- package/dist/components/SideNav/Chevron.d.ts.map +1 -0
- package/dist/components/SideNav/SideNav.d.ts.map +1 -1
- package/dist/components/SideNav/styles.css.d.ts +6 -0
- package/dist/components/SideNav/styles.css.d.ts.map +1 -1
- package/dist/components/SideNav/types.d.ts +6 -0
- package/dist/components/SideNav/types.d.ts.map +1 -0
- package/dist/components/SplitPane/SplitPane.d.ts +38 -0
- package/dist/components/SplitPane/SplitPane.d.ts.map +1 -0
- package/dist/components/SplitPane/SplitPane.meta.d.ts +3 -0
- package/dist/components/SplitPane/SplitPane.meta.d.ts.map +1 -0
- package/dist/components/SplitPane/SplitPane.stories.d.ts +17 -0
- package/dist/components/SplitPane/SplitPane.stories.d.ts.map +1 -0
- package/dist/components/SplitPane/styles.css.d.ts +78 -0
- package/dist/components/SplitPane/styles.css.d.ts.map +1 -0
- package/dist/components/Tabs/TabLayout.d.ts +7 -0
- package/dist/components/Tabs/TabLayout.d.ts.map +1 -0
- package/dist/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/components/Tabs/indicator.d.ts +28 -0
- package/dist/components/Tabs/indicator.d.ts.map +1 -0
- package/dist/components/Tabs/styles.css.d.ts +4 -0
- package/dist/components/Tabs/styles.css.d.ts.map +1 -1
- package/dist/components/Tabs/useTabIndicator.d.ts +4 -0
- package/dist/components/Tabs/useTabIndicator.d.ts.map +1 -0
- package/dist/components/Tabs/useTabKeys.d.ts +13 -0
- package/dist/components/Tabs/useTabKeys.d.ts.map +1 -0
- package/dist/components/Tag/RemoveGlyph.d.ts +4 -0
- package/dist/components/Tag/RemoveGlyph.d.ts.map +1 -0
- package/dist/components/Tag/Tag.d.ts +2 -24
- package/dist/components/Tag/Tag.d.ts.map +1 -1
- package/dist/components/Tag/styles.css.d.ts +8 -0
- package/dist/components/Tag/styles.css.d.ts.map +1 -1
- package/dist/components/Tag/types.d.ts +39 -0
- package/dist/components/Tag/types.d.ts.map +1 -0
- package/dist/components/Toast/ToastProvider.d.ts +2 -21
- package/dist/components/Toast/ToastProvider.d.ts.map +1 -1
- package/dist/components/Toast/native.css.d.ts +81 -0
- package/dist/components/Toast/native.css.d.ts.map +1 -0
- package/dist/components/Toast/store.d.ts +47 -0
- package/dist/components/Toast/store.d.ts.map +1 -0
- package/dist/components/Tooltip/TooltipHint.d.ts +9 -0
- package/dist/components/Tooltip/TooltipHint.d.ts.map +1 -0
- package/dist/components/Tree/Tree.d.ts.map +1 -1
- package/dist/components/Tree/styles.css.d.ts +6 -0
- package/dist/components/Tree/styles.css.d.ts.map +1 -1
- package/dist/components/Tree/useTreeKeyboard.d.ts +19 -0
- package/dist/components/Tree/useTreeKeyboard.d.ts.map +1 -0
- package/dist/components/VirtualList/VirtualList.d.ts +33 -0
- package/dist/components/VirtualList/VirtualList.d.ts.map +1 -0
- package/dist/components/VirtualList/VirtualList.meta.d.ts +3 -0
- package/dist/components/VirtualList/VirtualList.meta.d.ts.map +1 -0
- package/dist/components/VirtualList/VirtualList.stories.d.ts +15 -0
- package/dist/components/VirtualList/VirtualList.stories.d.ts.map +1 -0
- package/dist/components/VirtualList/styles.css.d.ts +28 -0
- package/dist/components/VirtualList/styles.css.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4935 -4260
- package/dist/index.js.map +1 -1
- package/dist/shared/ButtonHost.d.ts +11 -0
- package/dist/shared/ButtonHost.d.ts.map +1 -0
- package/dist/shared/pressableChild.d.ts +14 -0
- package/dist/shared/pressableChild.d.ts.map +1 -0
- package/dist/table.js +1 -1
- package/package.json +2 -2
- package/src/components/AppShell/AppShell.meta.ts +1 -1
- package/src/components/AppShell/AppShell.native.tsx +174 -0
- package/src/components/AppShell/native.css.ts +56 -0
- package/src/components/Button/Button.tsx +3 -3
- package/src/components/Drawer/Drawer.meta.ts +2 -1
- package/src/components/Drawer/Drawer.native.tsx +469 -0
- package/src/components/Drawer/Drawer.tsx +58 -145
- package/src/components/Drawer/DrawerScroll.stories.tsx +4 -54
- package/src/components/Drawer/DrawerSnap.stories.tsx +205 -0
- package/src/components/Drawer/native.css.ts +58 -0
- package/src/components/Drawer/parts.css.ts +125 -0
- package/src/components/Drawer/parts.tsx +110 -0
- package/src/components/Drawer/sheetPhysics.ts +103 -0
- package/src/components/Drawer/styles.css.ts +15 -74
- package/src/components/Drawer/touchInput.ts +81 -0
- package/src/components/Drawer/useDrawerRoot.ts +90 -0
- package/src/components/Drawer/useSheetHeights.ts +42 -0
- package/src/components/Drawer/useSwipeDismiss.ts +146 -23
- package/src/components/Menu/Menu.meta.ts +1 -1
- package/src/components/Menu/Menu.native.tsx +268 -0
- package/src/components/Menu/Menu.tsx +11 -2
- package/src/components/Menu/native.css.ts +18 -0
- package/src/components/Menu/useMenuOpen.ts +33 -0
- package/src/components/Menu/useMenuRoot.ts +7 -26
- package/src/components/Popover/Popover.meta.ts +1 -1
- package/src/components/Popover/Popover.native.tsx +205 -0
- package/src/components/Popover/native.css.ts +13 -0
- package/src/components/SideNav/Chevron.native.tsx +20 -0
- package/src/components/SideNav/Chevron.tsx +20 -0
- package/src/components/SideNav/SideNav.meta.ts +1 -1
- package/src/components/SideNav/SideNav.tsx +57 -41
- package/src/components/SideNav/styles.css.ts +8 -0
- package/src/components/SideNav/types.ts +6 -0
- package/src/components/SplitPane/SplitPane.meta.ts +40 -0
- package/src/components/SplitPane/SplitPane.native.tsx +47 -0
- package/src/components/SplitPane/SplitPane.stories.tsx +148 -0
- package/src/components/SplitPane/SplitPane.tsx +218 -0
- package/src/components/SplitPane/styles.css.ts +81 -0
- package/src/components/Tabs/TabLayout.native.tsx +9 -0
- package/src/components/Tabs/TabLayout.tsx +6 -0
- package/src/components/Tabs/Tabs.meta.ts +1 -1
- package/src/components/Tabs/Tabs.tsx +38 -136
- package/src/components/Tabs/indicator.ts +36 -0
- package/src/components/Tabs/styles.css.ts +7 -0
- package/src/components/Tabs/useTabIndicator.native.ts +35 -0
- package/src/components/Tabs/useTabIndicator.ts +49 -0
- package/src/components/Tabs/useTabKeys.native.ts +2 -0
- package/src/components/Tabs/useTabKeys.ts +99 -0
- package/src/components/Tag/RemoveGlyph.native.tsx +17 -0
- package/src/components/Tag/RemoveGlyph.tsx +10 -0
- package/src/components/Tag/Tag.meta.ts +1 -1
- package/src/components/Tag/Tag.native.tsx +70 -0
- package/src/components/Tag/Tag.tsx +9 -37
- package/src/components/Tag/styles.css.ts +9 -0
- package/src/components/Tag/types.ts +43 -0
- package/src/components/Toast/Toast.meta.ts +1 -1
- package/src/components/Toast/ToastProvider.native.tsx +226 -0
- package/src/components/Toast/ToastProvider.tsx +22 -84
- package/src/components/Toast/native.css.ts +78 -0
- package/src/components/Toast/store.ts +96 -0
- package/src/components/Toggle/Toggle.tsx +3 -3
- package/src/components/Tooltip/Tooltip.meta.ts +1 -1
- package/src/components/Tooltip/Tooltip.native.tsx +36 -0
- package/src/components/Tooltip/TooltipHint.ts +13 -0
- package/src/components/Tree/Tree.meta.ts +1 -1
- package/src/components/Tree/Tree.tsx +23 -135
- package/src/components/Tree/styles.css.ts +6 -0
- package/src/components/Tree/useTreeKeyboard.native.ts +5 -0
- package/src/components/Tree/useTreeKeyboard.ts +152 -0
- package/src/components/VirtualList/VirtualList.meta.ts +39 -0
- package/src/components/VirtualList/VirtualList.stories.tsx +95 -0
- package/src/components/VirtualList/VirtualList.tsx +103 -0
- package/src/components/VirtualList/styles.css.ts +31 -0
- package/src/hooks/useContainerBelow.native.ts +30 -0
- package/src/index.native.ts +33 -0
- package/src/index.ts +8 -1
- package/src/shared/ButtonHost.native.tsx +70 -0
- package/src/shared/ButtonHost.tsx +12 -0
- package/src/shared/portal/AnchoredOverlay.native.tsx +117 -0
- package/src/shared/pressableChild.ts +33 -0
- package/src/shared/useKeyboardHeight.native.ts +18 -0
- package/src/shared/useScreenReaderEnabled.native.ts +19 -0
- package/dist/Table-ColfpgKV.js +0 -1552
- package/dist/Table-ColfpgKV.js.map +0 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ComponentProps } from 'react';
|
|
2
|
+
import { html } from 'react-strict-dom';
|
|
3
|
+
export type ButtonHostProps = ComponentProps<typeof html.button>;
|
|
4
|
+
/**
|
|
5
|
+
* The host element of Button and Toggle. On the web it is the <button>
|
|
6
|
+
* itself, with no component in between (a wrapper changed what a Form's
|
|
7
|
+
* submit focuses); the native variant (ButtonHost.native.tsx) can swap in an
|
|
8
|
+
* RN Pressable to carry a Tooltip's accessibility hint.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ButtonHost: typeof html.button;
|
|
11
|
+
//# sourceMappingURL=ButtonHost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonHost.d.ts","sourceRoot":"","sources":["../../src/shared/ButtonHost.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,OAAO,CAAA;AACzC,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAErC,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAA;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,OAAO,IAAI,CAAC,MAAoB,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactElement, ReactNode, Ref } from 'react';
|
|
2
|
+
interface PressableChildProps {
|
|
3
|
+
onClick?: () => void;
|
|
4
|
+
ref?: Ref<unknown>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Native overlay triggers and closes: native presses do not bubble to a
|
|
8
|
+
* wrapper as web clicks do, so the one child (a Button) gets the handler
|
|
9
|
+
* itself, after its own. `ref` also receives the child's host, where focus
|
|
10
|
+
* returns when the overlay closes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function withPress(children: ReactNode, onPress: () => void, ref?: Ref<unknown>): ReactElement<PressableChildProps, string | import('react').JSXElementConstructor<any>> | null;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=pressableChild.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pressableChild.d.ts","sourceRoot":"","sources":["../../src/shared/pressableChild.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,GAAG,EACT,MAAM,OAAO,CAAA;AAGd,UAAU,mBAAmB;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,iGAWrF"}
|
package/dist/table.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as F,
|
|
1
|
+
import { M as F, p as gt, R as it, H as kt, q as pt, t as bt, v as ft, w as mt, b as ht, f as yt, r as St, x as Tt } from "./Table-Bms2ftt3.js";
|
|
2
2
|
import { jsx as u, jsxs as q } from "react/jsx-runtime";
|
|
3
3
|
import { c as H, m as p, d as b } from "./compiler-runtime-BuugS4D0.js";
|
|
4
4
|
import { flexRender as E, useReactTable as rt, getPaginationRowModel as Ct, getFilteredRowModel as vt, getSortedRowModel as at, getCoreRowModel as st } from "@tanstack/react-table";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duro-app/ui",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.13.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
92
|
"@tanstack/react-virtual": "^3.14.6",
|
|
93
|
-
"@duro-app/tokens": "^5.
|
|
93
|
+
"@duro-app/tokens": "^5.13.0"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
96
|
"@babel/preset-typescript": "^7.28.0",
|
|
@@ -2,7 +2,7 @@ import type {ComponentMeta} from '../component-meta'
|
|
|
2
2
|
|
|
3
3
|
export const meta: ComponentMeta = {
|
|
4
4
|
description:
|
|
5
|
-
"An application's frame: a navigation rail (a labelled nav, the brand at its top, an optional footer at its foot) beside the main content (main), with an optional Header. Below collapseBelow (sm default, md or lg), measured on the shell's own width, the rail is hidden and one sticky bar (a banner, appBarH tall) holds a Menu button that opens the same navigation in a left Drawer, the brand, and the Header's content when there is one — a shell without a Header still has its navigation on a phone. Above it the Header is a row that scrolls with the page and the rail stays sticky. CSS makes the switch: the server renders one HTML for every width with the brand in both places, and display: none hides the inactive copy, so nothing moves after hydration. A skip link (the first tab stop) focuses Main; an Aside inside Main sticks below the bar. Labels come from props. Compound component — Root is required.",
|
|
5
|
+
"An application's frame: a navigation rail (a labelled nav, the brand at its top, an optional footer at its foot) beside the main content (main), with an optional Header. Below collapseBelow (sm default, md or lg), measured on the shell's own width, the rail is hidden and one sticky bar (a banner, appBarH tall) holds a Menu button that opens the same navigation in a left Drawer, the brand, and the Header's content when there is one — a shell without a Header still has its navigation on a phone. Above it the Header is a row that scrolls with the page and the rail stays sticky. CSS makes the switch: the server renders one HTML for every width with the brand in both places, and display: none hides the inactive copy, so nothing moves after hydration. A skip link (the first tab stop) focuses Main; an Aside inside Main sticks below the bar. Labels come from props. Compound component — Root is required. On React Native it is always the narrow shape: the bar below the status bar, Main under it, and the Rail in a left Drawer; there is no skip link.",
|
|
6
6
|
whenToUse: [
|
|
7
7
|
'The frame of an app with a persistent navigation rail: an admin, a docs site, a dashboard',
|
|
8
8
|
'A layout whose navigation must collapse into a drawer on a phone',
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Children,
|
|
3
|
+
createContext,
|
|
4
|
+
isValidElement,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
type Ref,
|
|
7
|
+
useCallback,
|
|
8
|
+
useContext,
|
|
9
|
+
useMemo,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react'
|
|
12
|
+
import {html} from 'react-strict-dom'
|
|
13
|
+
import {Button} from '../Button/Button'
|
|
14
|
+
import {Drawer} from '../Drawer/Drawer'
|
|
15
|
+
import {Icon} from '../Icon'
|
|
16
|
+
import {useSafeAreaInsets} from '../../shared/safeArea'
|
|
17
|
+
import {nativeStyles} from './native.css'
|
|
18
|
+
|
|
19
|
+
export type AppShellCollapse = 'sm' | 'md' | 'lg'
|
|
20
|
+
|
|
21
|
+
interface AppShellContextValue {
|
|
22
|
+
open: boolean
|
|
23
|
+
setOpen: (open: boolean) => void
|
|
24
|
+
closeLabel: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const AppShellContext = createContext<AppShellContextValue | null>(null)
|
|
28
|
+
|
|
29
|
+
// True only where Root placed a Header itself, as on the web.
|
|
30
|
+
const HeaderSlotContext = createContext(false)
|
|
31
|
+
|
|
32
|
+
function useAppShell(): AppShellContextValue {
|
|
33
|
+
const ctx = useContext(AppShellContext)
|
|
34
|
+
if (!ctx) throw new Error('AppShell compound components must be used within AppShell.Root')
|
|
35
|
+
return ctx
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface RootProps {
|
|
39
|
+
/** The button that opens the navigation ("Menu"), from your text catalog. */
|
|
40
|
+
menuLabel: string
|
|
41
|
+
/** The accessible name of the drawer's close button ("Close navigation"). */
|
|
42
|
+
closeLabel: string
|
|
43
|
+
/** Web only: native has no skip link (no keyboard tab order to skip). */
|
|
44
|
+
skipLabel: string
|
|
45
|
+
/** The app's name or logo, shown in the bar beside the Menu button. */
|
|
46
|
+
brand?: ReactNode
|
|
47
|
+
/** Web only: a phone is below every breakpoint, so native always shows the bar and the drawer. */
|
|
48
|
+
collapseBelow?: AppShellCollapse
|
|
49
|
+
/** `AppShell.Rail`, `AppShell.Main` and an optional `AppShell.Header`, as direct children. */
|
|
50
|
+
children: ReactNode
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Native AppShell (plan N0, Slice B): the narrow shape the web shows below
|
|
55
|
+
* `collapseBelow`, always — the bar on top (Menu button, brand, the Header's
|
|
56
|
+
* content) below the top safe-area inset, Main under it, and the Rail's
|
|
57
|
+
* navigation in a left Drawer. The same parts and props as the web.
|
|
58
|
+
*/
|
|
59
|
+
function Root({menuLabel, closeLabel, brand, children}: RootProps) {
|
|
60
|
+
const [open, setOpen] = useState(false)
|
|
61
|
+
const insets = useSafeAreaInsets()
|
|
62
|
+
|
|
63
|
+
let header: ReactNode = null
|
|
64
|
+
const rest: ReactNode[] = []
|
|
65
|
+
Children.forEach(children, (child) => {
|
|
66
|
+
if (isValidElement<HeaderProps>(child) && child.type === Header) {
|
|
67
|
+
header = child
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
rest.push(child)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const value = useMemo(() => ({open, setOpen, closeLabel}), [open, closeLabel])
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<AppShellContext.Provider value={value}>
|
|
77
|
+
<html.div style={nativeStyles.shell}>
|
|
78
|
+
<html.header style={[nativeStyles.bar, nativeStyles.barTop(insets.top)]}>
|
|
79
|
+
<Button
|
|
80
|
+
variant="secondary"
|
|
81
|
+
size="small"
|
|
82
|
+
aria-expanded={open}
|
|
83
|
+
onClick={() => setOpen(true)}
|
|
84
|
+
>
|
|
85
|
+
<Icon name="menu" size="sm" />
|
|
86
|
+
{menuLabel}
|
|
87
|
+
</Button>
|
|
88
|
+
{brand}
|
|
89
|
+
<HeaderSlotContext.Provider value={true}>{header}</HeaderSlotContext.Provider>
|
|
90
|
+
</html.header>
|
|
91
|
+
{rest}
|
|
92
|
+
</html.div>
|
|
93
|
+
</AppShellContext.Provider>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface AppShellRailState {
|
|
98
|
+
/** Closes the drawer. Call it after any selection in the navigation. */
|
|
99
|
+
close: () => void
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
type RailContent = ReactNode | ((state: AppShellRailState) => ReactNode)
|
|
103
|
+
|
|
104
|
+
interface RailProps {
|
|
105
|
+
/** Names the navigation and the drawer ("Navigation"); required. */
|
|
106
|
+
'aria-label': string
|
|
107
|
+
/** The navigation (a SideNav, a Tree), shown in the drawer; a function receives `close`. */
|
|
108
|
+
children: RailContent
|
|
109
|
+
/** What sits at the end of the drawer; a function receives `close`. */
|
|
110
|
+
footer?: RailContent
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function Rail({'aria-label': ariaLabel, children, footer}: RailProps) {
|
|
114
|
+
const {open, setOpen, closeLabel} = useAppShell()
|
|
115
|
+
const close = useCallback(() => setOpen(false), [setOpen])
|
|
116
|
+
const content = typeof children === 'function' ? children({close}) : children
|
|
117
|
+
const foot = typeof footer === 'function' ? footer({close}) : footer
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<Drawer.Root anchor="left" open={open} onOpenChange={setOpen}>
|
|
121
|
+
<Drawer.Portal size="sm">
|
|
122
|
+
<Drawer.Header>
|
|
123
|
+
<Drawer.Title>{ariaLabel}</Drawer.Title>
|
|
124
|
+
<Drawer.Close aria-label={closeLabel} />
|
|
125
|
+
</Drawer.Header>
|
|
126
|
+
<Drawer.Body>
|
|
127
|
+
<html.nav aria-label={ariaLabel}>
|
|
128
|
+
{content}
|
|
129
|
+
{foot != null && <html.div style={nativeStyles.drawerFooter}>{foot}</html.div>}
|
|
130
|
+
</html.nav>
|
|
131
|
+
</Drawer.Body>
|
|
132
|
+
</Drawer.Portal>
|
|
133
|
+
</Drawer.Root>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface HeaderProps {
|
|
138
|
+
/** The header's content: a search field, the account menu. It sits in the bar. */
|
|
139
|
+
children?: ReactNode
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function Header({children}: HeaderProps) {
|
|
143
|
+
useAppShell()
|
|
144
|
+
if (!useContext(HeaderSlotContext)) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
'AppShell.Header must be a direct child of AppShell.Root (not inside a fragment or another component)',
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
return <html.div style={nativeStyles.header}>{children}</html.div>
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
interface MainProps {
|
|
153
|
+
children: ReactNode
|
|
154
|
+
/** Web only: the skip link's target. */
|
|
155
|
+
id?: string
|
|
156
|
+
/** The main region's host, e.g. to hand `useContainerBelow`. */
|
|
157
|
+
ref?: Ref<HTMLElement>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function Main({children, ref}: MainProps) {
|
|
161
|
+
useAppShell()
|
|
162
|
+
return (
|
|
163
|
+
<html.main ref={ref} style={nativeStyles.main}>
|
|
164
|
+
{children}
|
|
165
|
+
</html.main>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const AppShell = {
|
|
170
|
+
Root,
|
|
171
|
+
Rail,
|
|
172
|
+
Header,
|
|
173
|
+
Main,
|
|
174
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {css} from 'react-strict-dom'
|
|
2
|
+
import {colors} from '@duro-app/tokens/tokens/colors.css'
|
|
3
|
+
import {spacing} from '@duro-app/tokens/tokens/spacing.css'
|
|
4
|
+
import {sizes} from '@duro-app/tokens/tokens/sizes.css'
|
|
5
|
+
import {borders} from '@duro-app/tokens/tokens/borders.css'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The native AppShell: a phone is below every `collapseBelow`, so it is
|
|
9
|
+
* always the narrow shape — the bar on top, Main below, the rail in a Drawer.
|
|
10
|
+
*/
|
|
11
|
+
export const nativeStyles = css.create({
|
|
12
|
+
shell: {
|
|
13
|
+
flexGrow: 1,
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
color: colors.text,
|
|
17
|
+
backgroundColor: colors.bg,
|
|
18
|
+
},
|
|
19
|
+
bar: {
|
|
20
|
+
display: 'flex',
|
|
21
|
+
flexDirection: 'row',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
gap: spacing.sm,
|
|
24
|
+
minHeight: sizes.appBarH,
|
|
25
|
+
paddingLeft: spacing.md,
|
|
26
|
+
paddingRight: spacing.md,
|
|
27
|
+
backgroundColor: colors.bgCard,
|
|
28
|
+
borderBottomWidth: borders.hairline,
|
|
29
|
+
borderBottomStyle: 'solid',
|
|
30
|
+
borderBottomColor: colors.border,
|
|
31
|
+
},
|
|
32
|
+
// The bar sits below the status bar: the top safe-area inset is its padding.
|
|
33
|
+
barTop: (top: number) => ({paddingTop: top}),
|
|
34
|
+
header: {
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'row',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
gap: spacing.sm,
|
|
39
|
+
flexGrow: 1,
|
|
40
|
+
flexShrink: 1,
|
|
41
|
+
},
|
|
42
|
+
main: {
|
|
43
|
+
flexGrow: 1,
|
|
44
|
+
flexShrink: 1,
|
|
45
|
+
paddingTop: spacing.lg,
|
|
46
|
+
paddingLeft: spacing.md,
|
|
47
|
+
paddingRight: spacing.md,
|
|
48
|
+
},
|
|
49
|
+
drawerFooter: {
|
|
50
|
+
marginTop: spacing.md,
|
|
51
|
+
paddingTop: spacing.md,
|
|
52
|
+
borderTopWidth: borders.hairline,
|
|
53
|
+
borderTopStyle: 'solid',
|
|
54
|
+
borderTopColor: colors.border,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {ReactNode, Ref} from 'react'
|
|
2
|
-
import {html} from 'react-strict-dom'
|
|
3
2
|
import {isNative} from '../../platform'
|
|
4
3
|
import {mergeRefs} from '../../shared/mergeRefs'
|
|
4
|
+
import {ButtonHost} from '../../shared/ButtonHost'
|
|
5
5
|
import type {ControlSize} from '../../shared/types'
|
|
6
6
|
import {useGroupedControl} from '../ButtonGroup/useGroupedControl'
|
|
7
7
|
import {styles} from './styles.css'
|
|
@@ -51,7 +51,7 @@ export function Button({
|
|
|
51
51
|
}: ButtonProps) {
|
|
52
52
|
const grouped = useGroupedControl<HTMLButtonElement>({disabled})
|
|
53
53
|
return (
|
|
54
|
-
<
|
|
54
|
+
<ButtonHost
|
|
55
55
|
ref={isNative ? ref : mergeRefs(ref, grouped.ref)}
|
|
56
56
|
tabIndex={grouped.tabIndex}
|
|
57
57
|
onFocus={grouped.onFocus}
|
|
@@ -72,6 +72,6 @@ export function Button({
|
|
|
72
72
|
]}
|
|
73
73
|
>
|
|
74
74
|
{children}
|
|
75
|
-
</
|
|
75
|
+
</ButtonHost>
|
|
76
76
|
)
|
|
77
77
|
}
|
|
@@ -2,10 +2,11 @@ import type {ComponentMeta} from '../component-meta'
|
|
|
2
2
|
|
|
3
3
|
export const meta: ComponentMeta = {
|
|
4
4
|
description:
|
|
5
|
-
'Modal sliding panel from a screen edge (right, left, or bottom). Slides in with backdrop overlay. Supports swipe-to-dismiss gestures on mobile, backdrop click dismiss, and Escape key. Swipe yields to an inner Duro ScrollArea: a drag there scrolls it, and dismisses only toward the edge from the scroll start (a native overflow:auto child is not covered — use ScrollArea). Use for secondary workflows, settings panels, or mobile-friendly detail views.',
|
|
5
|
+
'Modal sliding panel from a screen edge (right, left, or bottom). Slides in with backdrop overlay. Supports swipe-to-dismiss gestures on mobile, backdrop click dismiss, and Escape key. Swipe yields to an inner Duro ScrollArea: a drag there scrolls it, and dismisses only toward the edge from the scroll start (a native overflow:auto child is not covered — use ScrollArea). A bottom drawer with snapPoints (medium and large) is a phone sheet: it rests at its detents, a grabber steps through them on a tap, a drag moves between them and dismisses below the lowest; its Body is the scroll the drag defers to. Header titleAlign="center" centres the title with a text close at the end. Use for secondary workflows, settings panels, or mobile-friendly detail views.',
|
|
6
6
|
whenToUse: [
|
|
7
7
|
'Settings or configuration panels that slide in from the side',
|
|
8
8
|
'Mobile-friendly detail views (bottom sheet pattern)',
|
|
9
|
+
'Phone pickers and lists that need more room on demand (snapPoints)',
|
|
9
10
|
'Secondary workflows that need more space than a dialog',
|
|
10
11
|
'Filters, navigation drawers, or form panels',
|
|
11
12
|
],
|