@cosmoops/connect-sdk 0.9.0 → 0.15.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/CHANGELOG.md +497 -0
- package/README.md +111 -41
- package/dist/cjs/cli/doctor.js +3 -3
- package/dist/cjs/cli/init.js +3 -3
- package/dist/cjs/client/components/access-gate.js +20 -1
- package/dist/cjs/client/components/app-shell.js +75 -41
- package/dist/cjs/client/components/apps-grid.js +3 -2
- package/dist/cjs/client/components/ask-ai.js +20 -1
- package/dist/cjs/client/components/confirm-dialog.js +40 -0
- package/dist/cjs/client/components/cursor-pagination.js +22 -0
- package/dist/cjs/client/components/filter-tabs.js +24 -0
- package/dist/cjs/client/components/grievance-notice.js +30 -0
- package/dist/cjs/client/components/link-button.js +17 -10
- package/dist/cjs/client/components/logo.js +4 -3
- package/dist/cjs/client/components/nav.js +41 -0
- package/dist/cjs/client/components/plan-pricing.js +27 -0
- package/dist/cjs/client/components/plans.js +1 -1
- package/dist/cjs/client/components/platform-gate.js +48 -0
- package/dist/cjs/client/components/shell-context.js +149 -0
- package/dist/cjs/client/components/side-nav.js +49 -23
- package/dist/cjs/client/components/sidebar.js +127 -0
- package/dist/cjs/client/components/subscribe-dialog.js +2 -2
- package/dist/cjs/client/components/subscription.js +1 -1
- package/dist/cjs/client/components/user-menu.js +96 -37
- package/dist/cjs/client/components/workspace-switcher.js +100 -0
- package/dist/cjs/client/index.js +69 -2
- package/dist/cjs/client/provider.js +57 -2
- package/dist/cjs/client/use-checkout.js +9 -5
- package/dist/cjs/errors.js +1 -1
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/next/index.js +2 -2
- package/dist/cjs/server/client.js +25 -2
- package/dist/cjs/server/handler.js +196 -18
- package/dist/cjs/server/http.js +6 -2
- package/dist/cjs/server/resources/acting-as.js +16 -0
- package/dist/cjs/server/resources/ai.js +15 -2
- package/dist/cjs/server/resources/data.js +374 -0
- package/dist/cjs/server/resources/identity.js +7 -3
- package/dist/cjs/server/resources/kv.js +84 -0
- package/dist/cjs/server/resources/platform.js +43 -0
- package/dist/cjs/server/resources/sessions.js +1 -1
- package/dist/cjs/server/resources/workspaces.js +74 -0
- package/dist/cjs/store.js +152 -0
- package/dist/cjs/ui/empty-state.js +28 -0
- package/dist/cjs/ui/index.js +44 -0
- package/dist/cjs/ui/page-header.js +27 -0
- package/dist/cjs/ui/page-shell.js +44 -0
- package/dist/cjs/ui/section.js +20 -0
- package/dist/cjs/ui/setting-row.js +14 -0
- package/dist/cjs/ui/status-chip.js +17 -0
- package/dist/esm/cli/doctor.js +3 -3
- package/dist/esm/cli/init.js +3 -3
- package/dist/esm/client/components/access-gate.js +20 -1
- package/dist/esm/client/components/app-shell.js +81 -47
- package/dist/esm/client/components/apps-grid.js +3 -2
- package/dist/esm/client/components/ask-ai.js +20 -1
- package/dist/esm/client/components/confirm-dialog.js +37 -0
- package/dist/esm/client/components/cursor-pagination.js +19 -0
- package/dist/esm/client/components/filter-tabs.js +21 -0
- package/dist/esm/client/components/grievance-notice.js +27 -0
- package/dist/esm/client/components/link-button.js +17 -10
- package/dist/esm/client/components/logo.js +4 -3
- package/dist/esm/client/components/nav.js +40 -0
- package/dist/esm/client/components/plan-pricing.js +25 -0
- package/dist/esm/client/components/plans.js +2 -2
- package/dist/esm/client/components/platform-gate.js +45 -0
- package/dist/esm/client/components/shell-context.js +141 -0
- package/dist/esm/client/components/side-nav.js +48 -21
- package/dist/esm/client/components/sidebar.js +120 -0
- package/dist/esm/client/components/subscribe-dialog.js +3 -3
- package/dist/esm/client/components/subscription.js +2 -2
- package/dist/esm/client/components/user-menu.js +95 -38
- package/dist/esm/client/components/workspace-switcher.js +97 -0
- package/dist/esm/client/index.js +46 -4
- package/dist/esm/client/provider.js +53 -2
- package/dist/esm/client/use-checkout.js +9 -5
- package/dist/esm/errors.js +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/next/index.js +2 -2
- package/dist/esm/server/client.js +25 -2
- package/dist/esm/server/handler.js +196 -18
- package/dist/esm/server/http.js +6 -2
- package/dist/esm/server/resources/acting-as.js +13 -0
- package/dist/esm/server/resources/ai.js +15 -2
- package/dist/esm/server/resources/data.js +369 -0
- package/dist/esm/server/resources/identity.js +7 -3
- package/dist/esm/server/resources/kv.js +80 -0
- package/dist/esm/server/resources/platform.js +39 -0
- package/dist/esm/server/resources/sessions.js +1 -1
- package/dist/esm/server/resources/workspaces.js +70 -0
- package/dist/esm/store.js +141 -0
- package/dist/esm/ui/empty-state.js +25 -0
- package/dist/esm/ui/index.js +34 -0
- package/dist/esm/ui/page-header.js +24 -0
- package/dist/esm/ui/page-shell.js +41 -0
- package/dist/esm/ui/section.js +17 -0
- package/dist/esm/ui/setting-row.js +11 -0
- package/dist/esm/ui/status-chip.js +14 -0
- package/dist/types/client/components/app-shell.d.ts +65 -27
- package/dist/types/client/components/apps-grid.d.ts +11 -2
- package/dist/types/client/components/confirm-dialog.d.ts +34 -0
- package/dist/types/client/components/cursor-pagination.d.ts +27 -0
- package/dist/types/client/components/filter-tabs.d.ts +26 -0
- package/dist/types/client/components/grievance-notice.d.ts +31 -0
- package/dist/types/client/components/link-button.d.ts +32 -9
- package/dist/types/client/components/logo.d.ts +18 -2
- package/dist/types/client/components/nav.d.ts +24 -0
- package/dist/types/client/components/plan-pricing.d.ts +10 -0
- package/dist/types/client/components/platform-gate.d.ts +53 -0
- package/dist/types/client/components/shell-context.d.ts +116 -0
- package/dist/types/client/components/side-nav.d.ts +27 -13
- package/dist/types/client/components/sidebar.d.ts +95 -0
- package/dist/types/client/components/user-menu.d.ts +68 -22
- package/dist/types/client/components/workspace-switcher.d.ts +49 -0
- package/dist/types/client/index.d.ts +27 -5
- package/dist/types/client/provider.d.ts +109 -6
- package/dist/types/client/use-checkout.d.ts +8 -4
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/next/index.d.ts +1 -1
- package/dist/types/server/client.d.ts +25 -2
- package/dist/types/server/http.d.ts +8 -2
- package/dist/types/server/index.d.ts +4 -0
- package/dist/types/server/resources/acting-as.d.ts +11 -0
- package/dist/types/server/resources/ai.d.ts +9 -1
- package/dist/types/server/resources/data.d.ts +258 -0
- package/dist/types/server/resources/kv.d.ts +63 -0
- package/dist/types/server/resources/platform.d.ts +32 -0
- package/dist/types/server/resources/workspaces.d.ts +55 -0
- package/dist/types/store.d.ts +96 -0
- package/dist/types/types.d.ts +319 -15
- package/dist/types/ui/empty-state.d.ts +34 -0
- package/dist/types/ui/index.d.ts +40 -0
- package/dist/types/ui/page-header.d.ts +34 -0
- package/dist/types/ui/page-shell.d.ts +39 -0
- package/dist/types/ui/section.d.ts +26 -0
- package/dist/types/ui/setting-row.d.ts +18 -0
- package/dist/types/ui/status-chip.d.ts +36 -0
- package/package.json +6 -2
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
import { type ButtonProps } from "@heroui/react";
|
|
2
|
+
import type { ReactElement, ReactNode } from "react";
|
|
2
3
|
export type ConnectLinkButtonProps = Omit<ButtonProps, "render"> & {
|
|
3
4
|
href: string;
|
|
5
|
+
/**
|
|
6
|
+
* The host's router link, if it has one.
|
|
7
|
+
*
|
|
8
|
+
* Without it this renders a plain `<a href>`, which navigates under every
|
|
9
|
+
* router but costs a **full document load** - in an App Router app that
|
|
10
|
+
* throws away the client cache and re-runs the whole tree, which is very
|
|
11
|
+
* visible on a button sitting inside a page the user is working in.
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <ConnectLinkButton href="/deals" renderLink={(props) => <Link {...props} />}>
|
|
15
|
+
* Open board
|
|
16
|
+
* </ConnectLinkButton>
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Same shape as `AppShell`'s `renderLink`, so a host declares its router
|
|
20
|
+
* once and passes the same function to both. This package imports nothing
|
|
21
|
+
* from `next` - see the `./next` entry's doc comment for why that is a hard
|
|
22
|
+
* rule - so handing the link component in is the only way it can be a client
|
|
23
|
+
* transition.
|
|
24
|
+
*/
|
|
25
|
+
renderLink?: (props: {
|
|
26
|
+
href: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
}) => ReactElement;
|
|
4
30
|
};
|
|
5
31
|
/**
|
|
6
|
-
* A HeroUI `Button` that renders a
|
|
32
|
+
* A HeroUI `Button` that renders a link.
|
|
7
33
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* rule), so it cannot know which router - if any - the host app uses. An
|
|
11
|
-
* `<a href>` navigates under all of them, and a host that wants client-side
|
|
12
|
-
* routing can pass its own element via `render` on the underlying `Button`.
|
|
34
|
+
* HeroUI v3's `Button` has no `as` prop, so `<Button as={Link}>` does not type
|
|
35
|
+
* check and does not work - this is the supported route, via `render`.
|
|
13
36
|
*
|
|
14
37
|
* The cast is the same one HeroUI's own "Render Function" example needs:
|
|
15
38
|
* `render` types its argument as the props of the element `Button` would have
|
|
16
|
-
* emitted, which is a `<button
|
|
39
|
+
* emitted, which is a `<button>`, including a `ref` typed to
|
|
17
40
|
* `HTMLButtonElement`. Spreading that onto an anchor is sound at runtime and
|
|
18
|
-
* unrepresentable in the type, which is
|
|
41
|
+
* unrepresentable in the type, which is what the cast is for.
|
|
19
42
|
*/
|
|
20
|
-
export declare function ConnectLinkButton({ href, ...props }: Readonly<ConnectLinkButtonProps>): import("react").JSX.Element;
|
|
43
|
+
export declare function ConnectLinkButton({ href, renderLink, ...props }: Readonly<ConnectLinkButtonProps>): import("react").JSX.Element;
|
|
@@ -14,7 +14,23 @@ export interface ConnectLogoProps {
|
|
|
14
14
|
* Falls back to the Connect rings when omitted.
|
|
15
15
|
*/
|
|
16
16
|
icon?: ReactNode;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Which halves of the lockup to draw.
|
|
19
|
+
*
|
|
20
|
+
* - `"full"` - badge and wordmark, the default.
|
|
21
|
+
* - `"mark"` - the badge alone, for a collapsed rail or a favicon-sized slot.
|
|
22
|
+
* - `"wordmark"` - the words alone, for a layout that already draws the
|
|
23
|
+
* badge somewhere fixed. The sidebar header uses this: the badge stays put
|
|
24
|
+
* while the words fade, and rendering a second badge only to hide it with
|
|
25
|
+
* a CSS child selector is how that kind of layout quietly rots.
|
|
26
|
+
*/
|
|
27
|
+
variant?: "full" | "mark" | "wordmark";
|
|
28
|
+
/**
|
|
29
|
+
* Mark only, no wordmark.
|
|
30
|
+
*
|
|
31
|
+
* @deprecated Pass `variant="mark"`. Kept because it is the published 0.11
|
|
32
|
+
* API; `variant` wins when both are given.
|
|
33
|
+
*/
|
|
18
34
|
collapsed?: boolean;
|
|
19
35
|
className?: string;
|
|
20
36
|
}
|
|
@@ -32,4 +48,4 @@ export interface ConnectLogoProps {
|
|
|
32
48
|
* as `text-sm`, with the badge, gap and tracking all in proportion. That is
|
|
33
49
|
* why there is no `size` prop.
|
|
34
50
|
*/
|
|
35
|
-
export declare function ConnectLogo({ name, icon, collapsed, className }: ConnectLogoProps): import("react").JSX.Element;
|
|
51
|
+
export declare function ConnectLogo({ name, icon, variant, collapsed, className }: ConnectLogoProps): import("react").JSX.Element;
|
|
@@ -33,6 +33,21 @@ export interface ConnectNavItem {
|
|
|
33
33
|
* step with this one.
|
|
34
34
|
*/
|
|
35
35
|
gateExempt?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Who this route is for. Omit - the default - and it renders for everyone,
|
|
38
|
+
* which is what every route on an ordinary application is.
|
|
39
|
+
*
|
|
40
|
+
* - `"staff"` shows it only to a Connect platform staff session; your
|
|
41
|
+
* operator console goes here.
|
|
42
|
+
* - `"member"` hides it *from* staff. Reach for it on anything that reads
|
|
43
|
+
* the workspace - billing, plans, usage, team - since a staff session has
|
|
44
|
+
* no workspace and those routes can only fail for it.
|
|
45
|
+
*
|
|
46
|
+
* Filtering here is presentation, not access: it decides which links a
|
|
47
|
+
* person sees, and a link is not a permission. The route itself must still
|
|
48
|
+
* check `staff` on your server - see `<ConnectPlatformGate>`.
|
|
49
|
+
*/
|
|
50
|
+
audience?: "staff" | "member";
|
|
36
51
|
}
|
|
37
52
|
/** A titled group of items. Untitled groups render as a plain divider-free block. */
|
|
38
53
|
export interface ConnectNavSection {
|
|
@@ -106,5 +121,14 @@ export declare function resolveActivePath(pathname: string, nav: ConnectNav): st
|
|
|
106
121
|
* lights up on every app's home route. External items are never active.
|
|
107
122
|
*/
|
|
108
123
|
export declare function isNavItemActive(item: ConnectNavItem, activeHref: string | null): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* `nav` with every item the current session should not see removed - see
|
|
126
|
+
* `ConnectNavItem.audience`.
|
|
127
|
+
*
|
|
128
|
+
* Applied to nested `items` too, and a parent whose children all disappear is
|
|
129
|
+
* dropped along with them: a section header pointing at nothing reads as a
|
|
130
|
+
* broken link rather than as a hidden one.
|
|
131
|
+
*/
|
|
132
|
+
export declare function filterNavForAudience(nav: ConnectNav, isStaff: boolean): ConnectNav;
|
|
109
133
|
/** Hrefs declared `gateExempt` - the routes `AppShell` renders outside its `ConnectAccessGate`. */
|
|
110
134
|
export declare function gateExemptPaths(nav: ConnectNav): string[];
|
|
@@ -24,6 +24,16 @@ export declare function formatPrice(paise: number): string;
|
|
|
24
24
|
* plan reads as an upgrade over every monthly one.
|
|
25
25
|
*/
|
|
26
26
|
export declare function monthlyOf(price: number, billingInterval: Plan["billingInterval"]): number;
|
|
27
|
+
export declare function intervalLabel(billingInterval: Plan["billingInterval"]): {
|
|
28
|
+
per: string;
|
|
29
|
+
short: string;
|
|
30
|
+
adverb: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* `/month`, `/year` - and nothing at all for a one-time plan, where a price
|
|
34
|
+
* with no slash is the honest rendering.
|
|
35
|
+
*/
|
|
36
|
+
export declare function pricePerLabel(billingInterval: Plan["billingInterval"]): string;
|
|
27
37
|
export declare function discountLabel(discount: AppliedDiscount): string;
|
|
28
38
|
/**
|
|
29
39
|
* The minimum a plan-like object has to expose to be grouped or compared -
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface ConnectPlatformGateProps {
|
|
3
|
+
/**
|
|
4
|
+
* Restrict to these platform roles. Omit to accept any staff session, which
|
|
5
|
+
* is the common case - your application already chose which roles it accepts
|
|
6
|
+
* when a platform admin turned staff access on for it, and repeating that
|
|
7
|
+
* list here is a second copy to keep in sync.
|
|
8
|
+
*
|
|
9
|
+
* Pass one only when your own console has tiers: a `developer` seeing the
|
|
10
|
+
* read-only half of what a `platform_admin` can edit.
|
|
11
|
+
*/
|
|
12
|
+
roles?: string[];
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Rendered for an ordinary member session. Defaults to `null` - hiding the
|
|
16
|
+
* subtree, which is what this gate is normally for. An operator console is
|
|
17
|
+
* usually better hidden than explained.
|
|
18
|
+
*/
|
|
19
|
+
fallback?: ReactNode;
|
|
20
|
+
/** Shown until the session read settles. Defaults to nothing. */
|
|
21
|
+
loading?: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Renders `children` only for a Connect platform staff session.
|
|
25
|
+
*
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <ConnectPlatformGate>
|
|
28
|
+
* <LinkMasterConsole />
|
|
29
|
+
* </ConnectPlatformGate>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Staff are Connect's own operators - `platform_admin`, `platform_manager`,
|
|
33
|
+
* `developer` - signing into *your* application. They belong to no workspace,
|
|
34
|
+
* so a staff session carries no `workspaceId`, no subscription and no member
|
|
35
|
+
* role; see `SessionAccess.staff`. Staff access is off by default and turned on
|
|
36
|
+
* per application by a Connect platform admin, so this gate renders nothing
|
|
37
|
+
* until that happens.
|
|
38
|
+
*
|
|
39
|
+
* **This is a UX gate, not a security boundary** - the same caveat every gate
|
|
40
|
+
* in this package carries, and it matters more here than anywhere else,
|
|
41
|
+
* because what is behind this one is usually an operator console. The browser
|
|
42
|
+
* can ignore this component entirely. Every route it reveals must re-check
|
|
43
|
+
* `staff` on your own server:
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* const { staff, role } = await client.identity.getUser(sessionToken);
|
|
47
|
+
* if (!staff) return forbidden();
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* Renders `loading` - not `fallback` - while the session is unknown, so an
|
|
51
|
+
* operator never flickers through the customer layout on the way in.
|
|
52
|
+
*/
|
|
53
|
+
export declare function ConnectPlatformGate({ roles, children, fallback, loading, }: ConnectPlatformGateProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { ConnectLinkRenderer } from "./nav.js";
|
|
3
|
+
/** A plain anchor - correct anywhere, and a full page load. See `ConnectLinkRenderer`. */
|
|
4
|
+
export declare const defaultRenderLink: ConnectLinkRenderer;
|
|
5
|
+
/**
|
|
6
|
+
* The rail's two widths, in pixels, and the one place they are written down.
|
|
7
|
+
*
|
|
8
|
+
* They are numbers rather than Tailwind classes because the rail animates
|
|
9
|
+
* between them - `motion` interpolates a number and cannot interpolate
|
|
10
|
+
* `w-16` into `w-64`.
|
|
11
|
+
*
|
|
12
|
+
* **The pairing is load-bearing, not arbitrary.** Every row in the sidebar is
|
|
13
|
+
* laid out so its glyph sits exactly `RAIL_GUTTER + ROW_GUTTER + 10px` from
|
|
14
|
+
* the rail's left edge - 32px, which is also `RAIL_COLLAPSED / 2`. So the
|
|
15
|
+
* collapsed rail centres every icon *without a single centring rule*, and
|
|
16
|
+
* nothing moves horizontally while the width animates. Change either width
|
|
17
|
+
* and that identity breaks: the icons drift sideways during the transition,
|
|
18
|
+
* which is the exact stutter this geometry exists to avoid.
|
|
19
|
+
*/
|
|
20
|
+
export declare const RAIL_COLLAPSED = 64;
|
|
21
|
+
export declare const RAIL_EXPANDED = 264;
|
|
22
|
+
/** Horizontal padding inside the rail, on every region. Tailwind `px-3`. */
|
|
23
|
+
export declare const RAIL_GUTTER = 12;
|
|
24
|
+
/**
|
|
25
|
+
* The row shape every sidebar control shares - nav items, the workspace tile,
|
|
26
|
+
* the launcher, the user button.
|
|
27
|
+
*
|
|
28
|
+
* `overflow-hidden` + `whitespace-nowrap` is what makes the collapse smooth:
|
|
29
|
+
* the label is *clipped* by the shrinking rail rather than re-wrapped or
|
|
30
|
+
* re-measured, so a width animation costs no reflow. Fading the label with
|
|
31
|
+
* `SidebarLabel` on top of that hides the clip itself.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SIDEBAR_ROW = "group/row relative flex h-10 w-full shrink-0 items-center justify-start gap-3 overflow-hidden whitespace-nowrap rounded-lg pe-2 text-sm font-normal transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus";
|
|
34
|
+
/**
|
|
35
|
+
* Leading inset for a row whose glyph is 20px wide (a nav icon, the launcher).
|
|
36
|
+
* `10 + 10 = 20`, and `RAIL_GUTTER + 20 = 32`. See `RAIL_COLLAPSED`.
|
|
37
|
+
*/
|
|
38
|
+
export declare const SIDEBAR_ROW_ICON = "ps-[10px]";
|
|
39
|
+
/**
|
|
40
|
+
* Leading inset for a row whose glyph is 32px wide (the avatar, the workspace
|
|
41
|
+
* tile). `4 + 16 = 20`, landing on the same 32px centre as `SIDEBAR_ROW_ICON`.
|
|
42
|
+
*/
|
|
43
|
+
export declare const SIDEBAR_ROW_TILE = "ps-[4px]";
|
|
44
|
+
/**
|
|
45
|
+
* The class list for one sidebar row, in one place.
|
|
46
|
+
*
|
|
47
|
+
* Three different elements need it - a plain `<button>` (`ConnectSidebarRow`),
|
|
48
|
+
* a rendered link (`ConnectSidebarNav`), and a HeroUI `<Button>` acting as a
|
|
49
|
+
* `Modal`/`Dropdown` trigger (the account row, the launcher, the workspace
|
|
50
|
+
* tile). They cannot share a component, because a React Aria overlay has to
|
|
51
|
+
* own its own trigger element - so they share this instead. A second copy of
|
|
52
|
+
* the string is a row that drifts off the 32px centre and takes the collapse
|
|
53
|
+
* animation's smoothness with it.
|
|
54
|
+
*
|
|
55
|
+
* On the HeroUI trigger: these are all Tailwind *utilities*, and HeroUI's
|
|
56
|
+
* `.button` is a component class, so the utility layer wins - which is what
|
|
57
|
+
* lets a `w-fit`, centred, `px-`-padded button take this shape at all.
|
|
58
|
+
*/
|
|
59
|
+
export declare function sidebarRowClass(options?: Readonly<{
|
|
60
|
+
/** The glyph is 32px (an avatar, a workspace tile) rather than 20px. */
|
|
61
|
+
tile?: boolean;
|
|
62
|
+
/** Paint the active treatment. */
|
|
63
|
+
active?: boolean;
|
|
64
|
+
className?: string;
|
|
65
|
+
}>): string;
|
|
66
|
+
export interface ConnectShellContextValue {
|
|
67
|
+
/** Icon-only rail. Always `false` inside the mobile drawer - see `AppShell`. */
|
|
68
|
+
collapsed: boolean;
|
|
69
|
+
/** Whether a collapse control should be offered at all. */
|
|
70
|
+
collapsible: boolean;
|
|
71
|
+
setCollapsed: (collapsed: boolean) => void;
|
|
72
|
+
renderLink: ConnectLinkRenderer;
|
|
73
|
+
/** The host router's current path, if it supplied one. */
|
|
74
|
+
activePath?: string;
|
|
75
|
+
/** Called after any sidebar link is pressed - the mobile drawer closes itself with this. */
|
|
76
|
+
onNavigate?: () => void;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Shared state for every part of the shell, so `ConnectSidebarNav`,
|
|
80
|
+
* `ConnectWorkspaceSwitcher` and `ConnectUserButton` all know whether the rail
|
|
81
|
+
* is collapsed without the host threading a prop through three layers.
|
|
82
|
+
*
|
|
83
|
+
* This is the piece that makes the shell *composable* rather than only
|
|
84
|
+
* configurable: the parts are exported and can be rearranged, and they still
|
|
85
|
+
* agree about collapse, routing and the mobile drawer because they read one
|
|
86
|
+
* context instead of one another's props.
|
|
87
|
+
*/
|
|
88
|
+
export declare function ConnectShellProvider({ value, children, }: Readonly<{
|
|
89
|
+
value: ConnectShellContextValue;
|
|
90
|
+
children: ReactNode;
|
|
91
|
+
}>): import("react").JSX.Element;
|
|
92
|
+
/**
|
|
93
|
+
* The shell state a sidebar part is rendering inside.
|
|
94
|
+
*
|
|
95
|
+
* Falls back to "expanded, not collapsible, plain anchors" rather than
|
|
96
|
+
* throwing, because every part is also exported for use *outside* a shell -
|
|
97
|
+
* a host dropping `ConnectSidebarNav` into a layout it already owns should
|
|
98
|
+
* not have to mount a provider to get a nav column.
|
|
99
|
+
*/
|
|
100
|
+
export declare function useConnectShell(): ConnectShellContextValue;
|
|
101
|
+
/**
|
|
102
|
+
* A sidebar row's text, faded out as the rail collapses.
|
|
103
|
+
*
|
|
104
|
+
* Opacity only - never width, height or margin. The rail's own width
|
|
105
|
+
* animation already moves the clip edge past this text; animating the text's
|
|
106
|
+
* box as well would make two things fight over the same pixels and is what
|
|
107
|
+
* makes a collapsing sidebar look like it stutters.
|
|
108
|
+
*
|
|
109
|
+
* Asymmetric timing on purpose. Collapsing, the label leaves *first* (no
|
|
110
|
+
* delay, quick) so the rail never appears to squeeze text; expanding, it
|
|
111
|
+
* arrives *after* the rail has opened enough to hold it.
|
|
112
|
+
*/
|
|
113
|
+
export declare function SidebarLabel({ children, className, }: Readonly<{
|
|
114
|
+
children: ReactNode;
|
|
115
|
+
className?: string;
|
|
116
|
+
}>): import("react").JSX.Element;
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import { defaultRenderLink } from "./shell-context.js";
|
|
1
2
|
import { type ConnectLinkRenderer, type ConnectNav } from "./nav.js";
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
export interface ConnectSideNavProps {
|
|
3
|
+
export { defaultRenderLink };
|
|
4
|
+
export interface ConnectSidebarNavProps {
|
|
5
5
|
/** Items, sections, or a mix - see `ConnectNav`. */
|
|
6
6
|
nav: ConnectNav;
|
|
7
7
|
/**
|
|
8
|
-
* The current path. Pass your router's own (`usePathname()` in Next)
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* never has to do.
|
|
8
|
+
* The current path. Pass your router's own (`usePathname()` in Next).
|
|
9
|
+
* Inherited from `AppShell` when omitted; without either, the nav
|
|
10
|
+
* reads `window.location` and patches `history` to hear about client-side
|
|
11
|
+
* navigations - which works, but is guesswork the router never has to do.
|
|
12
|
+
* See `useActivePath`.
|
|
12
13
|
*/
|
|
13
14
|
activePath?: string;
|
|
14
|
-
/** See `ConnectLinkRenderer`.
|
|
15
|
+
/** See `ConnectLinkRenderer`. Inherited from the shell; defaults to a plain `<a>`. */
|
|
15
16
|
renderLink?: ConnectLinkRenderer;
|
|
16
|
-
/** Icon-only rail.
|
|
17
|
+
/** Icon-only rail. Inherited from the shell when omitted. */
|
|
17
18
|
collapsed?: boolean;
|
|
18
19
|
/** Called after any nav link is pressed - the mobile drawer closes itself with this. */
|
|
19
20
|
onNavigate?: () => void;
|
|
@@ -24,8 +25,21 @@ export interface ConnectSideNavProps {
|
|
|
24
25
|
* children, and the active highlight.
|
|
25
26
|
*
|
|
26
27
|
* `AppShell` renders this inside its rail and its mobile drawer, and it
|
|
27
|
-
* is exported for a host that wants the shell's navigation without its
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* is exported for a host that wants the shell's navigation without its chrome:
|
|
29
|
+
* the same items, the same active resolution, dropped into a layout you
|
|
30
|
+
* already have.
|
|
31
|
+
*
|
|
32
|
+
* Every prop is optional except `nav`, because inside a shell they are all
|
|
33
|
+
* inherited from it. Passing one wins over what the shell says - which is how
|
|
34
|
+
* a second nav column (see `secondaryNav`) can share the rail's collapse state
|
|
35
|
+
* while keeping its own items.
|
|
36
|
+
*/
|
|
37
|
+
export declare function ConnectSidebarNav({ nav, activePath, renderLink, collapsed, onNavigate, className, }: ConnectSidebarNavProps): import("react").JSX.Element;
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Renamed to `ConnectSidebarNav` in 0.12 - the rail is a
|
|
40
|
+
* `ConnectSidebar` now, and its pieces are named after it. Identical
|
|
41
|
+
* behaviour; this alias will be removed in a later minor.
|
|
30
42
|
*/
|
|
31
|
-
export declare
|
|
43
|
+
export declare const ConnectSideNav: typeof ConnectSidebarNav;
|
|
44
|
+
/** @deprecated Renamed to `ConnectSidebarNavProps`. */
|
|
45
|
+
export type ConnectSideNavProps = ConnectSidebarNavProps;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
export interface ConnectSidebarProps {
|
|
3
|
+
/**
|
|
4
|
+
* The rail's contents, top to bottom. `AppShell` composes the default
|
|
5
|
+
* set; pass your own to rearrange them.
|
|
6
|
+
*/
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
/** Accessible name for the `<aside>`. Defaults to `"Sidebar"`. */
|
|
9
|
+
label?: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The application rail: a fixed-width column that animates between an
|
|
14
|
+
* icon-only 64px and a labelled 264px.
|
|
15
|
+
*
|
|
16
|
+
* **Why width, and nothing else, animates.** Every row inside puts its glyph
|
|
17
|
+
* on the same 32px centre in both states (see `RAIL_COLLAPSED`), and every
|
|
18
|
+
* label is clipped rather than re-measured (see `SIDEBAR_ROW`). So the only
|
|
19
|
+
* property in flight is this element's width: no child moves, no text
|
|
20
|
+
* re-wraps, and there is no per-frame layout pass on the rows. That is why the
|
|
21
|
+
* collapse looks smooth rather than merely fast.
|
|
22
|
+
*
|
|
23
|
+
* Hidden below `md`. The mobile drawer renders the same children with
|
|
24
|
+
* `collapsed: false`, because a 64px icon rail on a phone is a worse drawer,
|
|
25
|
+
* not a smaller one.
|
|
26
|
+
*/
|
|
27
|
+
export declare function ConnectSidebar({ children, label, className }: ConnectSidebarProps): import("react").JSX.Element;
|
|
28
|
+
export interface ConnectSidebarHeaderProps {
|
|
29
|
+
/** This application's name, in the `cosmo·ops VOICE` lockup. See `ConnectLogo`. */
|
|
30
|
+
name: string;
|
|
31
|
+
/** This application's glyph, drawn on the CosmoOps badge. */
|
|
32
|
+
icon?: ReactNode;
|
|
33
|
+
/** Where the lockup links when the rail is expanded. Defaults to `/`. */
|
|
34
|
+
homeHref?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The brand row, and the rail's own expand/collapse control.
|
|
38
|
+
*
|
|
39
|
+
* **Two states, one DOM.** The badge is mounted once and never re-created; the
|
|
40
|
+
* wordmark fades beside it and the collapse button fades at the trailing edge.
|
|
41
|
+
* Swapping which *element* renders the brand would remount it halfway through
|
|
42
|
+
* the width animation and flicker.
|
|
43
|
+
*
|
|
44
|
+
* Collapsed, hovering or focusing the badge swaps it for the expand glyph, so
|
|
45
|
+
* the rail needs no permanent control eating a row. Expanded, the control sits
|
|
46
|
+
* at the header's trailing edge, where it can be aimed at without covering the
|
|
47
|
+
* brand. The swap is pure CSS on a `group` - hovering re-renders nothing,
|
|
48
|
+
* which is what keeps it instant.
|
|
49
|
+
*
|
|
50
|
+
* The trade this makes, deliberately: **collapsed, the lockup is no longer a
|
|
51
|
+
* link home.** It has become the expand control, and one target that navigates
|
|
52
|
+
* on click but expands on click-after-hover would be worse than either. Put
|
|
53
|
+
* home in the nav; every application already does.
|
|
54
|
+
*/
|
|
55
|
+
export declare function ConnectSidebarHeader({ name, icon, homeHref }: ConnectSidebarHeaderProps): import("react").JSX.Element;
|
|
56
|
+
/**
|
|
57
|
+
* The rail's scrolling region, for the primary navigation. Everything else in
|
|
58
|
+
* the sidebar is `shrink-0`, so this is the only part that ever gives way.
|
|
59
|
+
*/
|
|
60
|
+
export declare function ConnectSidebarBody({ children }: Readonly<{
|
|
61
|
+
children: ReactNode;
|
|
62
|
+
}>): import("react").JSX.Element;
|
|
63
|
+
/**
|
|
64
|
+
* A pinned region at the foot of the rail - the secondary nav, the launcher,
|
|
65
|
+
* the account row.
|
|
66
|
+
*
|
|
67
|
+
* `separated` draws the hairline above it. The account row wants one; a
|
|
68
|
+
* secondary nav sitting directly on top of the account row does not, or the
|
|
69
|
+
* rail ends in two rules a few pixels apart.
|
|
70
|
+
*/
|
|
71
|
+
export declare function ConnectSidebarFooter({ children, separated, }: Readonly<{
|
|
72
|
+
children: ReactNode;
|
|
73
|
+
separated?: boolean;
|
|
74
|
+
}>): import("react").JSX.Element;
|
|
75
|
+
export type ConnectSidebarRowProps = Readonly<{
|
|
76
|
+
/** The glyph. 20px by default; 32px when `tile`. */
|
|
77
|
+
icon: ReactNode;
|
|
78
|
+
/** The row's text, and - when it is a string - its accessible name. */
|
|
79
|
+
label: ReactNode;
|
|
80
|
+
/** Overrides the accessible name. Required when `label` is not a string. */
|
|
81
|
+
"aria-label"?: string;
|
|
82
|
+
/** The glyph is 32px (an avatar, a workspace tile) rather than 20px. */
|
|
83
|
+
tile?: boolean;
|
|
84
|
+
/** Trailing content, e.g. a chevron. Clipped with the label while collapsed. */
|
|
85
|
+
trailing?: ReactNode;
|
|
86
|
+
}> & Omit<ComponentPropsWithoutRef<"button">, "aria-label">;
|
|
87
|
+
/**
|
|
88
|
+
* A pressable row in the rail that is not a nav item - the launcher, the
|
|
89
|
+
* account button, anything a host adds to the footer.
|
|
90
|
+
*
|
|
91
|
+
* Exported so a host's own row lines up with the SDK's. It applies the same
|
|
92
|
+
* 32px glyph centre and the same label fade, which is what stops a partner's
|
|
93
|
+
* "Help" row drifting sideways while every row above it holds still.
|
|
94
|
+
*/
|
|
95
|
+
export declare function ConnectSidebarRow({ icon, label, tile, trailing, className, "aria-label": ariaLabel, ...props }: ConnectSidebarRowProps): import("react").JSX.Element;
|
|
@@ -14,35 +14,81 @@ export interface ConnectUserMenuProps {
|
|
|
14
14
|
theme?: ThemeMode;
|
|
15
15
|
onThemeChange?: (theme: ThemeMode) => void;
|
|
16
16
|
}
|
|
17
|
+
export interface ConnectProfileModalProps {
|
|
18
|
+
/** Controlled open state. */
|
|
19
|
+
isOpen: boolean;
|
|
20
|
+
onOpenChange: (isOpen: boolean) => void;
|
|
21
|
+
/** Called after sign-out completes, e.g. to route home. */
|
|
22
|
+
onSignOut?: () => void;
|
|
23
|
+
/** See `ConnectUserButtonProps.theme`. */
|
|
24
|
+
theme?: ThemeMode;
|
|
25
|
+
onThemeChange?: (theme: ThemeMode) => void;
|
|
26
|
+
}
|
|
17
27
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
28
|
+
* The account modal with **no trigger of its own**, driven by the host.
|
|
29
|
+
*
|
|
30
|
+
* For an application that already has an account control it wants to keep -
|
|
31
|
+
* an avatar in its own chrome, a "Manage account" row in its own settings
|
|
32
|
+
* page - and wants Connect's modal behind it rather than rebuilding identity,
|
|
33
|
+
* sessions and sign-out. `ConnectUserButton` is the same modal with the
|
|
34
|
+
* trigger supplied; reach for this one only when that trigger is in the way.
|
|
21
35
|
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
* Renders nothing when signed out, open or not - there is no account to show.
|
|
37
|
+
*/
|
|
38
|
+
export declare function ConnectProfileModal({ isOpen, onOpenChange, onSignOut, theme, onThemeChange, }: ConnectProfileModalProps): import("react").JSX.Element | null;
|
|
39
|
+
export interface ConnectUserButtonProps {
|
|
40
|
+
/**
|
|
41
|
+
* How the trigger is drawn.
|
|
42
|
+
*
|
|
43
|
+
* - `"row"` - a full-width sidebar row: avatar, name over email, and a
|
|
44
|
+
* trailing glyph. The default, because the account control now lives in
|
|
45
|
+
* the rail's footer rather than in a top bar.
|
|
46
|
+
* - `"compact"` - the avatar alone, for a dense strip.
|
|
47
|
+
* - `"inline"` - avatar and name side by side, for a bar with room.
|
|
48
|
+
*/
|
|
49
|
+
variant?: "row" | "compact" | "inline";
|
|
50
|
+
/** Called after sign-out completes, e.g. to route home. */
|
|
51
|
+
onSignOut?: () => void;
|
|
52
|
+
className?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Current theme, and where to send a change - the SDK has no theme provider
|
|
55
|
+
* of its own (no `next-themes` dependency), so this is entirely
|
|
56
|
+
* host-controlled. The Appearance section only renders when both are
|
|
57
|
+
* supplied; omit either to leave theme control out of the modal.
|
|
58
|
+
*/
|
|
59
|
+
theme?: ThemeMode;
|
|
60
|
+
onThemeChange?: (theme: ThemeMode) => void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The signed-in account, as a control that opens the account modal.
|
|
28
64
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
65
|
+
* In `"row"` form this is the last thing in the sidebar, which is where an
|
|
66
|
+
* account control belongs: it is the one item in the rail that is about *you*
|
|
67
|
+
* rather than about the product, and the foot of a column is where every
|
|
68
|
+
* application a customer already uses puts that. It collapses with the rail -
|
|
69
|
+
* the avatar holds its position and the name fades - because the avatar alone
|
|
70
|
+
* is still a complete account control, which is exactly what an icon rail
|
|
71
|
+
* needs.
|
|
32
72
|
*
|
|
33
|
-
* Renders nothing when signed out
|
|
34
|
-
* renders nothing when signed *in*, and the two swap
|
|
35
|
-
*
|
|
73
|
+
* Renders nothing when signed out. Pair it with `<ConnectLoginButton>`, which
|
|
74
|
+
* renders nothing when signed *in*, and the two swap with no branch in your
|
|
75
|
+
* own layout.
|
|
36
76
|
*
|
|
37
77
|
* On the HeroUI shape: the trigger is a plain `<Button>` as a **direct child**
|
|
38
|
-
* of `<Modal>`, not wrapped in `<Modal.Trigger>`. `Dropdown.Trigger`
|
|
39
|
-
* the same construction
|
|
40
|
-
* wrapping one produces a button inside a button
|
|
41
|
-
* handling stops working.
|
|
42
|
-
|
|
43
|
-
|
|
78
|
+
* of `<Modal>`, not wrapped in `<Modal.Trigger>`. `Dropdown.Trigger` - and by
|
|
79
|
+
* the same construction `Modal.Trigger` - is itself a React Aria `Button`, so
|
|
80
|
+
* wrapping one produces a button inside a button: invalid markup, and press
|
|
81
|
+
* handling stops working.
|
|
82
|
+
*/
|
|
83
|
+
export declare function ConnectUserButton({ variant, onSignOut, className, theme, onThemeChange, }: ConnectUserButtonProps): import("react").JSX.Element | null;
|
|
84
|
+
/**
|
|
85
|
+
* @deprecated Renamed to `ConnectUserButton` in 0.12 - it is one button that
|
|
86
|
+
* opens a modal, and calling it a "menu" had people looking for a dropdown
|
|
87
|
+
* that never existed. `compact` maps to `variant="compact"`, and the old
|
|
88
|
+
* default (avatar plus name) to `variant="inline"`. This alias will be removed
|
|
89
|
+
* in a later minor.
|
|
44
90
|
*/
|
|
45
|
-
export declare function ConnectUserMenu({ compact,
|
|
91
|
+
export declare function ConnectUserMenu({ compact, ...rest }: ConnectUserMenuProps): import("react").JSX.Element;
|
|
46
92
|
/**
|
|
47
93
|
* The "Manage subscription" action on its own, for layouts that want it
|
|
48
94
|
* visible rather than buried in the account menu - a billing banner, an empty
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { UserWorkspace } from "../../types.js";
|
|
2
|
+
export interface ConnectWorkspaceSwitcherProps {
|
|
3
|
+
/**
|
|
4
|
+
* Called after a successful switch. Defaults to a full reload, which is
|
|
5
|
+
* almost always right: every server-rendered page in the host application
|
|
6
|
+
* was built for the *previous* workspace, and a client-side refresh would
|
|
7
|
+
* leave stale data on screen next to the new workspace's name.
|
|
8
|
+
*/
|
|
9
|
+
onSwitched?: (workspace: UserWorkspace) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Draw the trigger as a sidebar row rather than a plain button. On by
|
|
12
|
+
* default inside `AppShell`, where this sits directly under the
|
|
13
|
+
* brand; pass `false` for a standalone placement in your own chrome.
|
|
14
|
+
*/
|
|
15
|
+
row?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Switches which workspace this application is looking at.
|
|
20
|
+
*
|
|
21
|
+
* **Rendered in the `AppShell` sidebar header by default**, directly
|
|
22
|
+
* under the brand - which workspace you are in scopes everything below it, so
|
|
23
|
+
* it reads as a heading for the nav rather than as one more destination.
|
|
24
|
+
*
|
|
25
|
+
* It decides for itself whether to appear: an account with one workspace is
|
|
26
|
+
* shown nothing, because a switcher offering one option teaches the user
|
|
27
|
+
* nothing and costs a row of chrome on every screen. That is the opposite of
|
|
28
|
+
* Connect's own switcher, which always renders - Connect is where a workspace
|
|
29
|
+
* is *created*, so hiding it at one workspace would guarantee nobody ever
|
|
30
|
+
* reached two. A partner application creates nothing, so it has no such duty.
|
|
31
|
+
*
|
|
32
|
+
* **Authorization is the data, not a prop.** The list comes from the caller's
|
|
33
|
+
* own session (`GET /workspaces` takes no user id), so a workspace the user
|
|
34
|
+
* may not reach is simply not in it. There is deliberately no `roles` prop to
|
|
35
|
+
* pass or forget - the only way to render a row is for Connect to have
|
|
36
|
+
* returned it.
|
|
37
|
+
*
|
|
38
|
+
* Rows the user *belongs to* but cannot use here - a workspace outside the
|
|
39
|
+
* application's audience, or one with no subscription - are shown **disabled
|
|
40
|
+
* with the reason** rather than hidden. Hiding them reads as data loss to the
|
|
41
|
+
* person who owns the workspace; `switch` refuses the same two cases, so the
|
|
42
|
+
* label and the outcome always agree.
|
|
43
|
+
*
|
|
44
|
+
* A switch rotates the session token. The bridge persists the new one in the
|
|
45
|
+
* host application's own session store and the token never reaches this
|
|
46
|
+
* component - so the only thing to do afterwards is reload, which is what
|
|
47
|
+
* `onSwitched` defaults to.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ConnectWorkspaceSwitcher({ onSwitched, row, className, }: Readonly<ConnectWorkspaceSwitcherProps>): import("react").JSX.Element | null;
|