@imfusion/web-ui 0.10.1-dev.37.g9147f90f → 0.10.1-dev.41.g467e0e1a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -36,7 +36,6 @@ never changes the character width, so columns stay aligned. CoFo Sans Mono still
36
36
  - The aside drawer pads its content inside the scroll area; a `padding` on `AppShell.Aside` in drawer mode adds to it.
37
37
  - **Breaking:** The AppShell navbar drawer opens from the inline start instead of the inline end. — Nikolaus Brunner <brunner@imfusion.com>
38
38
  - Consumer CSS that places something against the navbar drawer's right edge moves to its left edge.
39
- - **Breaking:** The AppShell navbar drawer renders its own close on the logo row. Remove the close `AppShell.Burger` your navbar's first section renders, or the drawer shows two closes. — Nikolaus Brunner <brunner@imfusion.com>
40
39
  - **Breaking:** `AppShell.Navbar` renders its own logo row and account foot, and stacks its children as nav items with its own spacing. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
41
40
  - Pass the brand as `logo` and the account card as `account`, and drop the `AppShell.Section`s that held them. Omit `logo` to use the default ImFusion logo; `logo={null}` leaves the row empty.
42
41
  - **Breaking:** The current `AppShell.NavItem` has no background surface; its label is underlined and the other items are dimmed. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
@@ -52,9 +51,27 @@ never changes the character width, so columns stay aligned. CoFo Sans Mono still
52
51
  - An account switcher built on `AccountCard.Group` has no replacement yet; keep the old version until one ships.
53
52
  - **Breaking:** An `AccountCard` in the `AppShell.Navbar` foot lines up with the nav items and no longer takes the 8px inset. — Nikolaus Brunner <brunner@imfusion.com>
54
53
  - Pass the card as `account`; a card kept in a Section keeps its own padding.
54
+ - **Breaking:** The shell renders the mobile bar itself: the navbar's logo and the toggle for its drawer. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
55
+ - Remove `AppShell.Burger` and a logo of your own from your `AppShell.Header`; the Burger is no longer exported, and phones would show two logos.
56
+ - **Breaking:** The header takes the navbar's `variant`: beside a `branded` navbar it fills with the primary color and its text is on-color. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
57
+ - Remove a header color set to match the branded navbar. A custom surface still goes in `--imf-ui-appshell-header-bg`, which is unset by default and falls back to the navbar variant's surface.
58
+ - **Breaking:** `AppShell.Header` is a flex row, and it wraps its children in a container that aligns them to the inline end. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
59
+ - Give your own row `width: 100%` to span the header.
55
60
 
56
61
  ### Added
57
62
 
63
+ - `npx web-ui link <checkout>` copies a local checkout's build into the app's `node_modules/@imfusion/web-ui` and syncs it
64
+ after each rebuild. `npx web-ui unlink` restores the published files byte for byte. — Nikolaus Brunner <brunner@imfusion.com>
65
+ - `localWebUILink` serves a linked build without pre-bundling it and reloads the page on each sync. `imf-web-ui-setup` proposes registering it, and `imf-web-ui-audit` reports a `vite.config.ts` without it as Missing.
66
+ - Migrating from `"@imfusion/web-ui": "file:../web-ui"`: set the dependency back to the registry version, run `npm install`,
67
+ then run `npx web-ui link ../web-ui`.
68
+ - The shell's navigation toggle sets `aria-controls` from the navbar, and reads the `id` the navbar generates. A consumer `id` on the Navbar is what it points at. — Nikolaus Brunner <brunner@imfusion.com>
69
+ - [`useDisclosure`](src/hooks/use-disclosure.stories.tsx#UseDisclosure) keeps an open state that works controlled or uncontrolled, and returns `open`, `setOpen`, and `toggle`. — Nikolaus Brunner <brunner@imfusion.com>
70
+ - `AppShell.Navbar` and `AppShell.Aside` take `defaultOpen` and keep their own open state when `open` is omitted, and close an uncontrolled drawer when they dock. A controlled `open` is never changed by docking. — Nikolaus Brunner <brunner@imfusion.com>
71
+ - An `AppShell.Aside` opens from the consumer's state, such as a row selection, through `open` and `onOpenChange`. — Nikolaus Brunner <brunner@imfusion.com>
72
+ - `AppShell.Main` content is a flex column at least as tall as Main, so a child with `flex: 1` fills the rest. — Nikolaus Brunner <brunner@imfusion.com>
73
+ - `AppShell.Aside` content wraps long words such as emails and IDs inside a narrow panel, in the docked aside and the drawer. — Nikolaus Brunner <brunner@imfusion.com>
74
+ - An `AppShell.NavItem` followed inside the navbar drawer closes it, unless its `onClick` calls `event.preventDefault()`. — Nikolaus Brunner <brunner@imfusion.com>
58
75
  - [`ColorSchemeToggle`](src/components/color-scheme-toggle/color-scheme-toggle.mdx) lets the user pick Light, System, or
59
76
  Dark. It stores the choice through `useColorScheme` and needs no props; `label`, `lightLabel`, `systemLabel`, and
60
77
  `darkLabel` replace the English text — Nikolaus Brunner <brunner@imfusion.com>
@@ -85,13 +102,14 @@ never changes the character width, so columns stay aligned. CoFo Sans Mono still
85
102
  - `AppShell.Navbar` takes `logo` and `account`; the logo defaults to the ImFusion logo and lines up with the baseline of Main's first heading. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
86
103
  - `--imf-ui-appshell-heading-*`, `--imf-ui-appshell-navitem-dim`, `--imf-ui-appshell-navitem-underline-thickness`, and `--imf-ui-appshell-navitem-underline-offset` tune the logo row's strut and the NavItem underline. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
87
104
  - `AppShell.Aside` shows a close button in its drawer below `lg`, labelled by `closeLabel` (`Close panel` by default). [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
88
- - `AppShell.Burger` with no props opens and closes the navbar drawer, so the header needs no state wiring. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
89
105
  - `Select.Root` takes a `width` of `auto`, `wide`, or `full`: `auto` keeps the 10rem trigger minimum, `wide` raises it to
90
106
  15rem, and `full` fills the parent. `--imf-ui-select-trigger-min-width` on `Select.Root` sets the minimum from CSS.
91
107
  [Select](src/components/select/select.mdx) — Nikolaus Brunner <brunner@imfusion.com>
92
108
 
93
109
  ### Changed
94
110
 
111
+ - [`AppShell.Header`](src/components/app-shell/app-shell.mdx) is a page row that shows at every width, below the shell's bar on mobile. — Nikolaus Brunner <brunner@imfusion.com>
112
+ - The header pads its inline sides with `--imf-ui-appshell-padding`, so its content lines up with Main's.
95
113
  - AppShell — Nikolaus Brunner <brunner@imfusion.com>
96
114
  - The navbar rail, `AppShell.Main`, and the aside share `--imf-ui-appshell-padding` as their inline gutter, so their text lines up with no consumer CSS. [Docs](src/components/app-shell/app-shell.mdx)
97
115
  - `--imf-ui-appshell-navbar-padding` defaults to `--imf-ui-appshell-padding`, and `--imf-ui-appshell-aside-padding` defaults to `--imf-ui-appshell-padding`. Both stay overridable.
@@ -131,6 +149,7 @@ never changes the character width, so columns stay aligned. CoFo Sans Mono still
131
149
 
132
150
  ### Fixed
133
151
 
152
+ - `Drawer.Popup` draws no focus ring around the whole panel when it takes focus on open; the controls inside keep theirs. — Nikolaus Brunner <brunner@imfusion.com>
134
153
  - `Chip` stays on one line within its container's width and ends in an ellipsis when the label doesn't fit. [Chip](src/components/chip/chip.mdx) — Nikolaus Brunner <brunner@imfusion.com>
135
154
  - Only the navbar drawer's own close shows while it is open; the header burger steps back until the drawer closes — Nikolaus Brunner <brunner@imfusion.com>
136
155
  - `Skeleton.Root` fades only its own content in when loading ends; the rest of the page stays still, and no `AbortError` reaches the console — Nikolaus Brunner <brunner@imfusion.com>
package/README.md CHANGED
@@ -103,18 +103,27 @@ The main development skills are:
103
103
 
104
104
  ### Work on it from a consumer app
105
105
 
106
- To change the library while running an app that uses it, point the app's dependency at your checkout:
106
+ Keep this checkout's watch build running, then link it into the app:
107
107
 
108
- ```json
109
- "@imfusion/web-ui": "file:../web-ui"
108
+ ```sh
109
+ npm run dev:lib # here: rebuilds dist/ on every change
110
+ npx web-ui link ../web-ui # in the app: syncs dist/ into node_modules on every rebuild
110
111
  ```
111
112
 
112
- Run `npm install` in the app, then keep `npm run dev:lib` running here. The package resolves to `dist/`, so the app resolves
113
- nothing until the first watch build has finished, and sees each later change once the rebuild has written it.
113
+ `link` takes the checkout path, or reads `WEB_UI_CHECKOUT` when the path is omitted. It copies the build into the app's
114
+ `node_modules/@imfusion/web-ui`, keeps the published `dist/` and `package.json` in `node_modules/.web-ui-link/backup`, and
115
+ writes a marker file, `.local-link.json`, into the package folder. It stays in the foreground and syncs after each rebuild.
116
+ Ctrl+C stops syncing, and the app keeps the last synced build. Restart the app's dev server after `link` and after `unlink`,
117
+ because the Vite config reads the link when the server starts.
118
+
119
+ `npx web-ui unlink` restores the published files, removes the marker and the backup, and prints each step. Run it again and
120
+ it reports that nothing is linked.
121
+
122
+ The app's `package.json` and lockfile are never touched. `npm install` replaces the linked copy with the published package,
123
+ so run `link` again after it.
114
124
 
115
- npm links the checkout instead of copying it. Without help, the app then loads two copies of React, its own and the one in
116
- this checkout's `node_modules`, and every hook fails with "Invalid hook call". Register `localWebUILink` in the app's Vite
117
- config to prevent that:
125
+ The app registers `localWebUILink` in its Vite config and keeps it there. With no link it does nothing. With a link it serves
126
+ the copied build without pre-bundling it and reloads the page on each sync:
118
127
 
119
128
  ```ts
120
129
  import { localWebUILink } from "@imfusion/web-ui/vite";
@@ -124,11 +133,5 @@ export default defineConfig({
124
133
  });
125
134
  ```
126
135
 
127
- It dedupes every peer dependency of the library, including `@imfusion/sdk` and `@imfusion/sdk-react`. For an installed
128
- package it does nothing, so it can stay in the config.
129
-
130
- It covers the browser in development and in the build. Server rendering, including React Router prerendering, still loads
131
- dependencies through Node, which picks the checkout's React. Base UI's `use-sync-external-store` dependency is CommonJS, so
132
- Popover, Select, and other stateful parts can still fail on the server.
133
-
134
- Change the dependency back before you commit the app.
136
+ Server rendering, including React Router prerendering, is not covered, and Base UI's CommonJS `use-sync-external-store`
137
+ dependency can still fail there.
@@ -1,20 +1,46 @@
1
- import { Button as ButtonPrimitive } from '@base-ui/react/button';
2
1
  import { useRender } from '@base-ui/react/use-render';
3
2
  import { VariantProps } from 'class-variance-authority';
4
3
  declare const navbarSurface: (props?: ({
5
4
  variant?: "default" | "branded" | null | undefined;
6
5
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
- declare function Root({ ref, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
6
+ /**
7
+ * AppShell — application layout shell.
8
+ *
9
+ * The classic dashboard frame: collapsible navbar/aside sidebars, a page
10
+ * header, an optional footer, and a scrolling main content area. Also called
11
+ * an app layout, application frame, dashboard shell, or sidebar layout.
12
+ *
13
+ * Composed of eight sub-components used with dot-notation: `AppShell.Root` wraps
14
+ * `AppShell.Header`, `AppShell.Navbar`, `AppShell.Aside`, `AppShell.Footer`,
15
+ * `AppShell.Main`, and (inside a sidebar) `AppShell.Section` and `AppShell.NavItem`.
16
+ *
17
+ * Sizing is CSS-driven — override the `--imf-ui-appshell-*` custom properties
18
+ * rather than passing dimension props. Below the responsive breakpoints the
19
+ * navbar (`md`) and aside (`lg`) open as drawers, and the shell renders the
20
+ * navbar's toggle itself.
21
+ *
22
+ * Copied and adapted from Mantine's AppShell; no upstream runtime dependency.
23
+ * MIT licensed (see Mantine repo).
24
+ *
25
+ * @see https://mantine.dev/llms/core-app-shell.md
26
+ */
27
+ declare function Root({ ref, children, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
8
28
  declare namespace Root {
9
29
  var displayName: string;
10
30
  }
11
- declare function Header(props: React.ComponentProps<"header">): import("react/jsx-runtime").JSX.Element;
31
+ /**
32
+ * AppShell.Header — the page row: a title and actions at every width. Below `md` it sits under the shell's bar, and from
33
+ * `md` it sits beside the rail.
34
+ */
35
+ declare function Header({ className, children, ...props }: React.ComponentProps<"header">): import("react/jsx-runtime").JSX.Element;
12
36
  declare namespace Header {
13
37
  var displayName: string;
14
38
  }
15
39
  type DrawerProps = {
16
40
  /** Whether the drawer is open below the breakpoint. Ignored while the section is docked. */
17
41
  open?: boolean;
42
+ /** Whether the drawer starts open when `open` is omitted. */
43
+ defaultOpen?: boolean;
18
44
  /** Called when the drawer asks to open or close: scrim, swipe, or Escape. */
19
45
  onOpenChange?: (open: boolean) => void;
20
46
  };
@@ -30,7 +56,7 @@ type NavbarProps = React.ComponentProps<"nav"> & VariantProps<typeof navbarSurfa
30
56
  * color; children opt into on-color themselves. Below `md` the navbar opens as a drawer from the inline start, with its
31
57
  * close on the logo row.
32
58
  */
33
- declare function Navbar({ variant, open, onOpenChange, logo, account, children, ...props }: NavbarProps): import("react/jsx-runtime").JSX.Element;
59
+ declare function Navbar({ variant, open, defaultOpen, onOpenChange, logo, account, children, id, ...props }: NavbarProps): import("react/jsx-runtime").JSX.Element;
34
60
  declare namespace Navbar {
35
61
  var displayName: string;
36
62
  }
@@ -45,7 +71,7 @@ type AsideProps = React.ComponentProps<"aside"> & DrawerProps & {
45
71
  closeLabel?: string;
46
72
  };
47
73
  /** Below `lg` the aside opens as a drawer: from the bottom on phones, from the inline end on tablets. */
48
- declare function Aside({ open, onOpenChange, resizable, onWidthChange, resizeLabel, closeLabel, children, ...props }: AsideProps): import("react/jsx-runtime").JSX.Element;
74
+ declare function Aside({ open, defaultOpen, onOpenChange, resizable, onWidthChange, resizeLabel, closeLabel, children, ...props }: AsideProps): import("react/jsx-runtime").JSX.Element;
49
75
  declare namespace Aside {
50
76
  var displayName: string;
51
77
  }
@@ -65,21 +91,6 @@ declare function Section({ grow, ...props }: SectionProps): import("react/jsx-ru
65
91
  declare namespace Section {
66
92
  var displayName: string;
67
93
  }
68
- type BurgerProps = React.ComponentProps<typeof ButtonPrimitive> & {
69
- /** Draws the cross and sets `aria-expanded`. Defaults to whether the navbar drawer is open. */
70
- opened?: boolean;
71
- /** Accessible name. Defaults to "Open navigation" or "Close navigation" from `opened`. */
72
- label?: string;
73
- };
74
- /**
75
- * AppShell.Burger — the menu toggle: three sheared bands that cross while the navbar drawer is open. With no props it
76
- * opens and closes that drawer. Takes its color from the surface and hides from `md` up, where the navbar is docked, and
77
- * steps back while the drawer is open, so the drawer's own close is the only one shown.
78
- */
79
- declare function Burger({ opened, label, className, onClick, ...props }: BurgerProps): import("react/jsx-runtime").JSX.Element;
80
- declare namespace Burger {
81
- var displayName: string;
82
- }
83
94
  type NavItemProps = useRender.ComponentProps<"a"> & {
84
95
  /** Leading icon, decorative. */
85
96
  icon?: React.ReactNode;
@@ -88,10 +99,11 @@ type NavItemProps = useRender.ComponentProps<"a"> & {
88
99
  };
89
100
  /**
90
101
  * AppShell.NavItem — a navbar link: icon, mono label, and an optional description. Takes its color from the navbar;
91
- * `aria-current="page"` underlines the label. Pass a router link through `render`.
102
+ * `aria-current="page"` underlines the label. Pass a router link through `render`. Followed inside the navbar drawer,
103
+ * it closes the drawer.
92
104
  */
93
- declare function NavItem({ icon, description, children, render, ...props }: NavItemProps): import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>>;
105
+ declare function NavItem({ icon, description, children, render, onClick, ...props }: NavItemProps): import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>>;
94
106
  declare namespace NavItem {
95
107
  var displayName: string;
96
108
  }
97
- export { Root, Header, Navbar, Aside, Footer, Main, Section, Burger, NavItem };
109
+ export { Root, Header, Navbar, Aside, Footer, Main, Section, NavItem };
@@ -1,6 +1,8 @@
1
1
  export { useClipboard } from './use-clipboard';
2
2
  export type { UseClipboardOptions } from './use-clipboard';
3
3
  export { useColorScheme } from './use-color-scheme';
4
+ export { useDisclosure } from './use-disclosure';
5
+ export type { UseDisclosureOptions } from './use-disclosure';
4
6
  export { useMediaQuery } from './use-media-query';
5
7
  export type { UseMediaQueryOptions } from './use-media-query';
6
8
  export { useResizeObserver } from './use-resize-observer';
@@ -0,0 +1,17 @@
1
+ export interface UseDisclosureOptions {
2
+ /** The controlled open state. Leave it out to let the hook keep the state. */
3
+ open?: boolean;
4
+ /** The initial open state while uncontrolled. @default false */
5
+ defaultOpen?: boolean;
6
+ /** Called with the next state whenever it is requested, controlled or not. */
7
+ onOpenChange?: (open: boolean) => void;
8
+ }
9
+ /**
10
+ * Tracks an open/closed state that works controlled or uncontrolled, like a Base UI root. The hook updates its own
11
+ * state only while `open` is undefined, and always reports the request through `onOpenChange`.
12
+ */
13
+ export declare function useDisclosure({ open, defaultOpen, onOpenChange }?: UseDisclosureOptions): {
14
+ open: boolean;
15
+ setOpen: (next: boolean) => void;
16
+ toggle: () => void;
17
+ };
package/dist/index.d.ts CHANGED
@@ -44,8 +44,8 @@ export { Tooltip, tooltipMeta } from './components/tooltip';
44
44
  export { Typo, typoMeta } from './components/typo';
45
45
  export { ImFusionLogo } from './components/logo';
46
46
  export { WebUIProvider } from './provider';
47
- export { useClipboard, useColorScheme, useMediaQuery } from './hooks';
48
- export type { UseClipboardOptions, UseMediaQueryOptions } from './hooks';
47
+ export { useClipboard, useColorScheme, useDisclosure, useMediaQuery } from './hooks';
48
+ export type { UseClipboardOptions, UseDisclosureOptions, UseMediaQueryOptions } from './hooks';
49
49
  export { cva, cx } from 'class-variance-authority';
50
50
  export { initialsFromEmail, splitEmail } from './utils/email';
51
51
  export { withBreakHints } from './utils/break-hints';