@sanity/sdk-react 0.0.0-alpha.2 → 0.0.0-alpha.3

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/README.md CHANGED
@@ -10,7 +10,7 @@ React components and hooks for creating Sanity applications.
10
10
  ## Installation
11
11
 
12
12
  ```bash
13
- npm i @sanity/sdk-react @sanity/sdk styled-components
13
+ npm i @sanity/sdk-react @sanity/sdk
14
14
  ```
15
15
 
16
16
  ## SDK Documentation
@@ -21,7 +21,18 @@ See the [SDK Documentation](https://sdk-docs.sanity.dev) for more information.
21
21
 
22
22
  Here's how to implement your Sanity application:
23
23
 
24
+ ```bash
25
+ # Create a new Vite React TypeScript project
26
+ npm create vite@latest my-content-os-app -- --template react-ts -y
27
+ cd my-content-os-app
28
+ # Install Sanity dependencies
29
+ npm i @sanity/sdk-react @sanity/sdk @sanity/ui
30
+ # Run the app
31
+ npm run dev
32
+ ```
33
+
24
34
  ```tsx
35
+ // src/App.tsx
25
36
  import {createSanityInstance} from '@sanity/sdk'
26
37
  import {AuthBoundary, SanityProvider} from '@sanity/sdk-react/components'
27
38
  import {useCurrentUser, useLogOut} from '@sanity/sdk-react/hooks'
@@ -65,6 +76,8 @@ function Authenticated() {
65
76
  </Flex>
66
77
  )
67
78
  }
79
+
80
+ export default App
68
81
  ```
69
82
 
70
83
  ## Customizing your application
@@ -1,5 +1,6 @@
1
1
  import {FallbackProps} from 'react-error-boundary'
2
2
  import {ForwardRefExoticComponent} from 'react'
3
+ import {JSX} from 'react'
3
4
  import type {PropsWithChildren} from 'react'
4
5
  import {ReactElement} from 'react'
5
6
  import {RefAttributes} from 'react'
@@ -124,6 +125,7 @@ export declare interface DocumentPreviewLayoutProps {
124
125
  export declare function Login({header, footer}: LoginLayoutProps): JSX.Element
125
126
 
126
127
  /**
128
+ /**
127
129
  * Component shown during auth callback processing that handles login completion.
128
130
  * Automatically processes the auth callback when mounted and updates the URL
129
131
  * to remove callback parameters without triggering a page reload.
@@ -4,8 +4,7 @@ import { Fragment, Suspense, useEffect, useCallback, useMemo, forwardRef } from
4
4
  import { ErrorBoundary } from "react-error-boundary";
5
5
  import { useLoginUrls, useHandleCallback, useLogOut, useAuthState } from "./_chunks-es/useLogOut.js";
6
6
  import { SanityProvider } from "./_chunks-es/useLogOut.js";
7
- import { Text, Flex, Card, Heading, Spinner, Button, Container as Container$1, Stack } from "@sanity/ui";
8
- import styled from "styled-components";
7
+ import { Flex, Text, Card, Heading, Spinner, Button, Container, Stack } from "@sanity/ui";
9
8
  import { SanityLogo } from "@sanity/logos";
10
9
  import { DocumentIcon } from "@sanity/icons";
11
10
  class AuthError extends Error {
@@ -34,53 +33,45 @@ const LINKS = [
34
33
  i18nKey: "workspaces.sanity-io-title",
35
34
  title: "sanity.io"
36
35
  }
37
- ], StyledText = styled(Text)`
38
- a {
39
- color: inherit;
40
- }
41
- `;
36
+ ];
42
37
  function LoginFooter() {
43
38
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, justify: "center", align: "center", paddingTop: 2, children: [
44
39
  /* @__PURE__ */ jsx(Text, { size: 3, children: /* @__PURE__ */ jsx(SanityLogo, {}) }),
45
40
  /* @__PURE__ */ jsx(Flex, { align: "center", gap: 2, children: LINKS.map((link, index) => /* @__PURE__ */ jsxs(Fragment, { children: [
46
- /* @__PURE__ */ jsx(StyledText, { muted: !0, size: 1, children: /* @__PURE__ */ jsx("a", { href: link.url, target: "_blank", rel: "noopener noreferrer", children: link.title }) }),
41
+ /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: /* @__PURE__ */ jsx(
42
+ "a",
43
+ {
44
+ href: link.url,
45
+ target: "_blank",
46
+ rel: "noopener noreferrer",
47
+ style: { color: "inherit" },
48
+ children: link.title
49
+ }
50
+ ) }),
47
51
  index < LINKS.length - 1 && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "\u2022" })
48
52
  ] }, link.title)) })
49
53
  ] });
50
54
  }
51
- const Root = styled.div`
52
- width: 100%;
53
- display: flex;
54
- `, Container = styled(Flex)`
55
- width: 320px;
56
- margin: auto;
57
- display: flex;
58
- `, StyledCard = styled(Card)``, ChildrenFlex = styled(Flex)`
59
- min-height: 154px;
60
- `;
61
55
  function LoginLayout({
62
56
  children,
63
57
  footer = /* @__PURE__ */ jsx(LoginFooter, {}),
64
58
  header
65
59
  }) {
66
- return /* @__PURE__ */ jsx(Root, { children: /* @__PURE__ */ jsxs(Container, { direction: "column", gap: 4, children: [
67
- /* @__PURE__ */ jsx(StyledCard, { border: !0, radius: 2, paddingY: 4, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
60
+ return /* @__PURE__ */ jsx("div", { style: { width: "100%", display: "flex" }, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, style: { width: "320px", margin: "auto", display: "flex" }, children: [
61
+ /* @__PURE__ */ jsx(Card, { border: !0, radius: 2, paddingY: 4, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
68
62
  header && /* @__PURE__ */ jsx(Card, { borderBottom: !0, paddingX: 4, paddingBottom: 3, children: header }),
69
- children && /* @__PURE__ */ jsx(ChildrenFlex, { paddingX: 4, direction: "column", children })
63
+ children && /* @__PURE__ */ jsx(Flex, { paddingX: 4, direction: "column", style: { minHeight: "154px" }, children })
70
64
  ] }) }),
71
65
  footer
72
66
  ] }) });
73
67
  }
74
- const FallbackRoot = styled(Flex)`
75
- height: 123px;
76
- `;
77
68
  function Login({ header, footer }) {
78
69
  return /* @__PURE__ */ jsx(LoginLayout, { header, footer, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
79
70
  /* @__PURE__ */ jsx(Heading, { as: "h1", size: 1, align: "center", children: "Choose login provider" }),
80
71
  /* @__PURE__ */ jsx(
81
72
  Suspense,
82
73
  {
83
- fallback: /* @__PURE__ */ jsx(FallbackRoot, { align: "center", justify: "center", children: /* @__PURE__ */ jsx(Spinner, {}) }),
74
+ fallback: /* @__PURE__ */ jsx(Flex, { align: "center", justify: "center", style: { height: "123px" }, children: /* @__PURE__ */ jsx(Spinner, {}) }),
84
75
  children: /* @__PURE__ */ jsx(Providers, {})
85
76
  }
86
77
  )
@@ -90,9 +81,6 @@ function Providers() {
90
81
  const loginUrls = useLoginUrls();
91
82
  return /* @__PURE__ */ jsx(Flex, { direction: "column", gap: 3, children: loginUrls.map(({ title, url }) => /* @__PURE__ */ jsx(Button, { text: title, as: "a", href: url, mode: "ghost" }, url)) });
92
83
  }
93
- const StyledFlex$1 = styled(Flex)`
94
- margin: auto;
95
- `;
96
84
  function LoginCallback({ header, footer }) {
97
85
  const handleCallback = useHandleCallback();
98
86
  return useEffect(() => {
@@ -100,14 +88,11 @@ function LoginCallback({ header, footer }) {
100
88
  handleCallback(url.toString()).then((replacementLocation) => {
101
89
  replacementLocation && history.replaceState(null, "", replacementLocation);
102
90
  });
103
- }, [handleCallback]), /* @__PURE__ */ jsx(LoginLayout, { header, footer, children: /* @__PURE__ */ jsxs(StyledFlex$1, { direction: "column", justify: "center", align: "center", gap: 4, children: [
91
+ }, [handleCallback]), /* @__PURE__ */ jsx(LoginLayout, { header, footer, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", justify: "center", align: "center", gap: 4, style: { margin: "auto" }, children: [
104
92
  /* @__PURE__ */ jsx(Text, { size: 1, children: "Logging you in\u2026" }),
105
93
  /* @__PURE__ */ jsx(Spinner, { size: 4 })
106
94
  ] }) });
107
95
  }
108
- const StyledFlex = styled(Flex)`
109
- margin: auto;
110
- `;
111
96
  function LoginError({
112
97
  error,
113
98
  resetErrorBoundary,
@@ -118,7 +103,7 @@ function LoginError({
118
103
  const logout = useLogOut(), handleRetry = useCallback(async () => {
119
104
  await logout(), resetErrorBoundary();
120
105
  }, [logout, resetErrorBoundary]);
121
- return /* @__PURE__ */ jsx(LoginLayout, { header, footer, children: /* @__PURE__ */ jsxs(StyledFlex, { direction: "column", gap: 4, children: [
106
+ return /* @__PURE__ */ jsx(LoginLayout, { header, footer, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, style: { margin: "auto" }, children: [
122
107
  /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
123
108
  /* @__PURE__ */ jsx(Text, { as: "h2", align: "center", weight: "bold", size: 3, children: "Authentication Error" }),
124
109
  /* @__PURE__ */ jsx(Text, { size: 1, align: "center", children: "Please try again or contact support if the problem persists." })
@@ -283,7 +268,7 @@ const DocumentPreviewLayout = forwardRef(
283
268
  DocumentPreviewLayout.displayName = "DocumentPreviewLayout";
284
269
  const LoginLinks = () => {
285
270
  const loginUrls = useLoginUrls(), authState = useAuthState();
286
- return useHandleCallback(), authState.type === "logging-in" ? /* @__PURE__ */ jsx("div", { children: "Logging in..." }) : authState.type === "logged-in" ? /* @__PURE__ */ jsx("div", { children: "You are logged in" }) : /* @__PURE__ */ jsx(Card, { height: "fill", overflow: "auto", paddingX: 4, children: /* @__PURE__ */ jsx(Flex, { height: "fill", direction: "column", align: "center", justify: "center", paddingTop: 4, children: /* @__PURE__ */ jsx(Container$1, { width: 0, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
271
+ return useHandleCallback(), authState.type === "logging-in" ? /* @__PURE__ */ jsx("div", { children: "Logging in..." }) : authState.type === "logged-in" ? /* @__PURE__ */ jsx("div", { children: "You are logged in" }) : /* @__PURE__ */ jsx(Card, { height: "fill", overflow: "auto", paddingX: 4, children: /* @__PURE__ */ jsx(Flex, { height: "fill", direction: "column", align: "center", justify: "center", paddingTop: 4, children: /* @__PURE__ */ jsx(Container, { width: 0, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
287
272
  /* @__PURE__ */ jsx(Heading, { align: "center", size: 1, children: "Choose login provider" }),
288
273
  /* @__PURE__ */ jsx(Stack, { space: 2, children: loginUrls.map((provider, index) => /* @__PURE__ */ jsx(
289
274
  Button,
@@ -1 +1 @@
1
- {"version":3,"file":"components.js","sources":["../src/components/auth/AuthError.ts","../src/components/auth/LoginFooter.tsx","../src/components/auth/LoginLayout.tsx","../src/components/auth/Login.tsx","../src/components/auth/LoginCallback.tsx","../src/components/auth/LoginError.tsx","../src/components/auth/AuthBoundary.tsx","../src/components/DocumentGridLayout/DocumentGridLayout.tsx","../src/components/DocumentListLayout/DocumentListLayout.tsx","../src/components/DocumentPreviewLayout/DocumentPreviewLayout.tsx","../src/components/Login/LoginLinks.tsx"],"sourcesContent":["/**\n * Error class for authentication-related errors. Wraps errors thrown during the\n * authentication flow.\n *\n * @remarks\n * This class provides a consistent error type for authentication failures while\n * preserving the original error as the cause. If the original error has a\n * message property, it will be used as the error message.\n *\n * @alpha\n */\nexport class AuthError extends Error {\n constructor(error: unknown) {\n if (\n typeof error === 'object' &&\n !!error &&\n 'message' in error &&\n typeof error.message === 'string'\n ) {\n super(error.message)\n } else {\n super()\n }\n\n this.cause = error\n }\n}\n","import {SanityLogo} from '@sanity/logos'\nimport {Flex, Text} from '@sanity/ui'\nimport {Fragment} from 'react'\nimport styled from 'styled-components'\n\nconst LINKS = [\n {\n url: 'https://slack.sanity.io/',\n i18nKey: 'workspaces.community-title',\n title: 'Community',\n },\n {\n url: 'https://www.sanity.io/docs',\n i18nKey: 'workspaces.docs-title',\n title: 'Docs',\n },\n {\n url: 'https://www.sanity.io/legal/privacy',\n i18nKey: 'workspaces.privacy-title',\n title: 'Privacy',\n },\n {\n url: 'https://www.sanity.io',\n i18nKey: 'workspaces.sanity-io-title',\n title: 'sanity.io',\n },\n]\n\nconst StyledText = styled(Text)`\n a {\n color: inherit;\n }\n`\n\n/**\n * Default footer component for login screens showing Sanity branding and legal\n * links.\n *\n * @alpha\n */\nexport function LoginFooter(): React.ReactNode {\n return (\n <Flex direction=\"column\" gap={4} justify=\"center\" align=\"center\" paddingTop={2}>\n <Text size={3}>\n <SanityLogo />\n </Text>\n\n <Flex align=\"center\" gap={2}>\n {LINKS.map((link, index) => (\n <Fragment key={link.title}>\n <StyledText muted size={1}>\n <a href={link.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {link.title}\n </a>\n </StyledText>\n\n {index < LINKS.length - 1 && (\n <Text size={1} muted>\n •\n </Text>\n )}\n </Fragment>\n ))}\n </Flex>\n </Flex>\n )\n}\n","import {Card, Flex} from '@sanity/ui'\nimport styled from 'styled-components'\n\nimport {LoginFooter} from './LoginFooter'\n\n/**\n * @alpha\n */\nexport interface LoginLayoutProps {\n /** Optional header content rendered at top of card */\n header?: React.ReactNode\n\n /** Optional footer content rendered below card. Defaults to an internal login footer */\n footer?: React.ReactNode\n\n /** Main content rendered in card body */\n children?: React.ReactNode\n}\n\nconst Root = styled.div`\n width: 100%;\n display: flex;\n`\n\nconst Container = styled(Flex)`\n width: 320px;\n margin: auto;\n display: flex;\n`\n\nconst StyledCard = styled(Card)``\n\nconst ChildrenFlex = styled(Flex)`\n min-height: 154px;\n`\n\n/**\n * Layout component for login-related screens providing consistent styling and structure.\n * Renders content in a centered card with optional header and footer sections.\n *\n * Can be used to build custom login screens for the AuthBoundary component, including:\n * - Login provider selection (LoginComponent)\n * - OAuth callback handling (CallbackComponent)\n * - Error states (LoginErrorComponent)\n *\n * @example\n * ```tsx\n * // Custom login screen using the layout\n * function CustomLogin({header, footer}: LoginLayoutProps) {\n * return (\n * <LoginLayout\n * header={header}\n * footer={footer}\n * >\n * <CustomLoginContent />\n * </LoginLayout>\n * )\n * }\n *\n * // Use with AuthBoundary\n * <AuthBoundary\n * LoginComponent={CustomLogin}\n * header={<Logo />}\n * >\n * <ProtectedContent />\n * </AuthBoundary>\n * ```\n *\n * @alpha\n */\nexport function LoginLayout({\n children,\n footer = <LoginFooter />,\n header,\n}: LoginLayoutProps): React.ReactNode {\n return (\n <Root>\n <Container direction=\"column\" gap={4}>\n <StyledCard border radius={2} paddingY={4}>\n <Flex direction=\"column\" gap={4}>\n {header && (\n <Card borderBottom paddingX={4} paddingBottom={3}>\n {header}\n </Card>\n )}\n\n {children && (\n <ChildrenFlex paddingX={4} direction=\"column\">\n {children}\n </ChildrenFlex>\n )}\n </Flex>\n </StyledCard>\n\n {footer}\n </Container>\n </Root>\n )\n}\n","import {Button, Flex, Heading, Spinner} from '@sanity/ui'\nimport {Suspense} from 'react'\nimport styled from 'styled-components'\n\nimport {useLoginUrls} from '../../hooks/auth/useLoginUrls'\nimport {LoginLayout, type LoginLayoutProps} from './LoginLayout'\n\n/**\n * @alpha\n */\nexport interface LoginProps {\n header?: React.ReactNode\n footer?: React.ReactNode\n}\n\nconst FallbackRoot = styled(Flex)`\n height: 123px;\n`\n\n/**\n * Login component that displays available authentication providers.\n * Renders a list of login options with a loading fallback while providers load.\n *\n * @alpha\n */\nexport function Login({header, footer}: LoginLayoutProps): JSX.Element {\n return (\n <LoginLayout header={header} footer={footer}>\n <Flex direction=\"column\" gap={4}>\n <Heading as=\"h1\" size={1} align=\"center\">\n Choose login provider\n </Heading>\n\n <Suspense\n fallback={\n <FallbackRoot align=\"center\" justify=\"center\">\n <Spinner />\n </FallbackRoot>\n }\n >\n <Providers />\n </Suspense>\n </Flex>\n </LoginLayout>\n )\n}\n\nfunction Providers() {\n const loginUrls = useLoginUrls()\n\n return (\n <Flex direction=\"column\" gap={3}>\n {loginUrls.map(({title, url}) => (\n <Button key={url} text={title} as=\"a\" href={url} mode=\"ghost\" />\n ))}\n </Flex>\n )\n}\n","import {Flex, Spinner, Text} from '@sanity/ui'\nimport {useEffect} from 'react'\nimport styled from 'styled-components'\n\nimport {useHandleCallback} from '../../hooks/auth/useHandleCallback'\nimport {LoginLayout, type LoginLayoutProps} from './LoginLayout'\n\nconst StyledFlex = styled(Flex)`\n margin: auto;\n`\n\n/**\n * Component shown during auth callback processing that handles login completion.\n * Automatically processes the auth callback when mounted and updates the URL\n * to remove callback parameters without triggering a page reload.\n *\n * @alpha\n */\nexport function LoginCallback({header, footer}: LoginLayoutProps): React.ReactNode {\n const handleCallback = useHandleCallback()\n\n useEffect(() => {\n const url = new URL(location.href)\n handleCallback(url.toString()).then((replacementLocation) => {\n if (replacementLocation) {\n // history API with `replaceState` is used to prevent a reload but still\n // remove the short-lived token from the URL\n history.replaceState(null, '', replacementLocation)\n }\n })\n }, [handleCallback])\n\n return (\n <LoginLayout header={header} footer={footer}>\n <StyledFlex direction=\"column\" justify=\"center\" align=\"center\" gap={4}>\n <Text size={1}>Logging you in…</Text>\n <Spinner size={4} />\n </StyledFlex>\n </LoginLayout>\n )\n}\n","import {Button, Flex, Text} from '@sanity/ui'\nimport {useCallback} from 'react'\nimport {type FallbackProps} from 'react-error-boundary'\nimport styled from 'styled-components'\n\nimport {useLogOut} from '../../hooks/auth/useLogOut'\nimport {AuthError} from './AuthError'\nimport {LoginLayout, type LoginLayoutProps} from './LoginLayout'\n\n/**\n * @alpha\n */\nexport type LoginErrorProps = FallbackProps & LoginLayoutProps\n\nconst StyledFlex = styled(Flex)`\n margin: auto;\n`\n\n/**\n * Displays authentication error details and provides retry functionality.\n * Only handles {@link AuthError} instances - rethrows other error types.\n *\n * @alpha\n */\nexport function LoginError({\n error,\n resetErrorBoundary,\n header,\n footer,\n}: LoginErrorProps): React.ReactNode {\n if (!(error instanceof AuthError)) throw error\n const logout = useLogOut()\n\n const handleRetry = useCallback(async () => {\n await logout()\n resetErrorBoundary()\n }, [logout, resetErrorBoundary])\n\n return (\n <LoginLayout header={header} footer={footer}>\n <StyledFlex direction=\"column\" gap={4}>\n <Flex direction=\"column\" gap={3}>\n <Text as=\"h2\" align=\"center\" weight=\"bold\" size={3}>\n Authentication Error\n </Text>\n <Text size={1} align=\"center\">\n Please try again or contact support if the problem persists.\n </Text>\n </Flex>\n <Button text=\"Retry\" tone=\"primary\" onClick={handleRetry} />\n </StyledFlex>\n </LoginLayout>\n )\n}\n","import {AuthStateType} from '@sanity/sdk'\nimport {useMemo} from 'react'\nimport {ErrorBoundary, type FallbackProps} from 'react-error-boundary'\n\nimport {useAuthState} from '../../hooks/auth/useAuthState'\nimport {AuthError} from './AuthError'\nimport {Login} from './Login'\nimport {LoginCallback} from './LoginCallback'\nimport {LoginError, type LoginErrorProps} from './LoginError'\nimport type {LoginLayoutProps} from './LoginLayout'\n\n/**\n * @alpha\n */\nexport interface AuthBoundaryProps extends LoginLayoutProps {\n /**\n * Custom component to render the login screen.\n * Receives all login layout props. Defaults to {@link Login}.\n */\n LoginComponent?: React.ComponentType<LoginLayoutProps>\n\n /**\n * Custom component to render during OAuth callback processing.\n * Receives all login layout props. Defaults to {@link LoginCallback}.\n */\n CallbackComponent?: React.ComponentType<LoginLayoutProps>\n\n /**\n * Custom component to render when authentication errors occur.\n * Receives login layout props and error boundary props. Defaults to\n * {@link LoginError}\n */\n LoginErrorComponent?: React.ComponentType<LoginErrorProps>\n}\n\n/**\n * A component that handles authentication flow and error boundaries for a\n * protected section of the application.\n *\n * @remarks\n * This component manages different authentication states and renders the\n * appropriate components based on that state.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AuthBoundary header={<MyLogo />}>\n * <ProtectedContent />\n * </AuthBoundary>\n * )\n * }\n * ```\n *\n * @alpha\n */\nexport function AuthBoundary({\n LoginErrorComponent = LoginError,\n ...props\n}: AuthBoundaryProps): React.ReactNode {\n const {header, footer} = props\n const FallbackComponent = useMemo(() => {\n return function LoginComponentWithLayoutProps(fallbackProps: FallbackProps) {\n return <LoginErrorComponent {...fallbackProps} header={header} footer={footer} />\n }\n }, [header, footer, LoginErrorComponent])\n\n return (\n <ErrorBoundary FallbackComponent={FallbackComponent}>\n <AuthSwitch {...props} />\n </ErrorBoundary>\n )\n}\n\ninterface AuthSwitchProps extends LoginLayoutProps {\n LoginComponent?: React.ComponentType<LoginLayoutProps>\n CallbackComponent?: React.ComponentType<LoginLayoutProps>\n}\n\nfunction AuthSwitch({\n LoginComponent = Login,\n CallbackComponent = LoginCallback,\n children,\n ...props\n}: AuthSwitchProps) {\n const authState = useAuthState()\n\n switch (authState.type) {\n case AuthStateType.ERROR: {\n throw new AuthError(authState.error)\n }\n case AuthStateType.LOGGING_IN: {\n return <CallbackComponent {...props} />\n }\n case AuthStateType.LOGGED_IN: {\n return children\n }\n default: {\n return <LoginComponent {...props} />\n }\n }\n}\n","import '../../css/styles.css'\n\nimport type {PropsWithChildren, ReactElement} from 'react'\n\n/**\n * @public\n */\nexport const DocumentGridLayout = (props: PropsWithChildren): ReactElement => {\n return (\n <>\n <style>{`\n .DocumentGridLayout {\n grid-template-columns: repeat(auto-fit, minmax(38ch, 1fr));\n }\n `}</style>\n <ol className=\"DocumentGridLayout list-none grid\">{props.children}</ol>\n </>\n )\n}\n\nDocumentGridLayout.displayName = 'DocumentGridLayout'\n","import '../../css/styles.css'\n\nimport type {PropsWithChildren, ReactElement} from 'react'\n\n/**\n * @public\n */\nexport const DocumentListLayout = (props: PropsWithChildren): ReactElement => {\n return <ol className=\"DocumentListLayout list-none\">{props.children}</ol>\n}\n\nDocumentListLayout.displayName = 'DocumentListLayout'\n","import 'inter-ui/inter.css'\nimport '../../css/styles.css'\n\nimport {DocumentIcon} from '@sanity/icons'\nimport {forwardRef} from 'react'\n\n/**\n * @public\n */\nexport interface DocumentPreviewLayoutProps {\n docType?: string\n media?: {type: string; url: string} | null | undefined\n onClick?: () => void\n selected?: boolean\n status?: string\n subtitle?: string\n title: string\n}\n\n/**\n * This is a component that renders a document preview.\n *\n * @public\n *\n * @param props - The props for the DocumentPreviewLayout component.\n * @returns - The DocumentPreviewLayout component.\n */\nexport const DocumentPreviewLayout = forwardRef(\n (\n {\n docType,\n media,\n onClick,\n selected = false,\n status = '',\n subtitle = '',\n title,\n }: DocumentPreviewLayoutProps,\n ref: React.Ref<HTMLElement>,\n ): JSX.Element => {\n // @TODO: empty state\n if (!title) {\n return <></>\n }\n\n let PreviewMedia\n\n if (media?.url) {\n const baseUrl = new URL(media.url)\n baseUrl.searchParams.set('h', '66')\n baseUrl.searchParams.set('w', '66')\n baseUrl.searchParams.set('fit', 'crop')\n const mediaUrl = baseUrl.toString()\n // media url string params for sanity img\n PreviewMedia = <img src={mediaUrl} alt=\"\" />\n } else {\n PreviewMedia = <DocumentIcon />\n }\n\n return (\n <>\n <style>{`\n .DocumentPreviewLayout {\n --_hoverFocusBg: light-dark(var(--shade-11), var(--tint-1));\n --_selectedBg: light-dark(var(--blue-10), var(--blue-2));\n --_selectedFg: light-dark(var(--gray-1), var(--gray-10));\n --_titleFg: light-dark(var(--gray-1), var(--gray-10));\n --_subtitleFg: light-dark(var(--gray-4), var(--gray-7));\n --_docTypeFg: light-dark(var(--gray-1), var(--gray-10));\n --_docTypeBg: light-dark(var(--shade-10), var(--tint-1));\n --_publishedFg: light-dark(var(--gray-1), var(--gray-10));\n --_publishedBg: light-dark(var(--green-10), var(--green-2));\n --_draftFg: light-dark(var(--gray-1), var(--gray-10));\n --_draftBg: light-dark(var(--yellow-6), var(--yellow-2));\n\n appearance: none;\n\n &:has(:hover, focus) {\n background-color: var(--hoverFocusBg, var(--_hoverFocusBg));\n }\n\n &.selected {\n background-color: var(--selectedBg, var(--_selectedBg));\n color: var(--selectedFg, var(--_selectedFg));\n }\n\n .Title {\n color: var(--titleFg, var(--_titleFg));\n }\n\n &:not(.selected) .Subtitle {\n color: var(--subtitleFg, var(--_subtitleFg));\n }\n\n .Media {\n aspect-ratio: 1;\n inline-size: 33px;\n border-color: var(--gray-8);\n }\n\n .DocType {\n color: var(--docTypeFg, var(--_docTypeFg));\n background-color: var(--docTypeBg, var(--_docTypeBg));\n }\n\n .Published {\n color: var(--publishedFg, var(--_publishedFg));\n background-color: var(--publishedBg, var(--_publishedBg));\n }\n\n .Draft {\n color: var(--draftFg, var(--_draftFg));\n background-color: var(--draftBg, var(--_draftBg));\n }\n\n :is(.Published, .Draft) figcaption {\n @container (width < 52ch) {\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n height: 1px;\n overflow: hidden;\n position: absolute;\n white-space: nowrap;\n width: 1px;\n }\n }\n\n }\n `}</style>\n <button\n onClick={onClick}\n ref={ref as React.Ref<HTMLButtonElement>}\n className={`DocumentPreviewLayout block si-100 text-start p-1 radius1 ${selected ? 'selected' : ''}`}\n >\n <div className=\"container-inline flex align-items-center gap-2 font-sans\">\n <figure className=\"Media border0 border-solid flex-none flex align-items-center justify-content-center object-cover\">\n {PreviewMedia}\n </figure>\n\n <div className=\"leading2 flex-grow overflow-hidden\">\n <p className=\"Title text-1 font-medium truncate\">{title}</p>\n {subtitle && <p className=\"Subtitle text-1 truncate\">{subtitle}</p>}\n </div>\n\n {docType && (\n <figure className=\"DocType inline-block pb-5 pi-3 radius-pill text-2\">\n <figcaption className=\"inline\">{docType}</figcaption>\n </figure>\n )}\n\n {/* @TODO: finalize UI for this */}\n {status === 'published' && (\n <figure className=\"Published inline-block pb-5 pi-3 radius-pill text-2\">\n ✔︎ <figcaption className=\"inline\">published</figcaption>\n </figure>\n )}\n\n {/* @TODO: finalize UI for this */}\n {status === 'draft' && (\n <figure className=\"Draft inline-block pb-5 pi-3 radius-pill text-2\">\n ⛑︎ <figcaption className=\"inline\">draft</figcaption>\n </figure>\n )}\n </div>\n </button>\n </>\n )\n },\n)\n\nDocumentPreviewLayout.displayName = 'DocumentPreviewLayout'\n","import {Button, Card, Container, Flex, Heading, Stack} from '@sanity/ui'\nimport {type ReactElement} from 'react'\n\nimport {useAuthState} from '../../hooks/auth/useAuthState'\nimport {useHandleCallback} from '../../hooks/auth/useHandleCallback'\nimport {useLoginUrls} from '../../hooks/auth/useLoginUrls'\n\n/**\n * Component that handles Sanity authentication flow and renders login provider options\n *\n * @public\n *\n * @returns Rendered component\n *\n * @remarks\n * The component handles three states:\n * 1. Loading state during token exchange\n * 2. Success state after successful authentication\n * 3. Provider selection UI when not authenticated\n *\n * @example\n * ```tsx\n * const config = { projectId: 'your-project-id', dataset: 'production' }\n * return <LoginLinks sanityInstance={config} />\n * ```\n */\nexport const LoginLinks = (): ReactElement => {\n const loginUrls = useLoginUrls()\n const authState = useAuthState()\n useHandleCallback()\n\n if (authState.type === 'logging-in') {\n return <div>Logging in...</div>\n }\n\n // Show success state after authentication\n if (authState.type === 'logged-in') {\n return <div>You are logged in</div>\n }\n\n /**\n * Render provider selection UI\n * Uses Sanity UI components for consistent styling\n */\n return (\n <Card height=\"fill\" overflow=\"auto\" paddingX={4}>\n <Flex height=\"fill\" direction=\"column\" align=\"center\" justify=\"center\" paddingTop={4}>\n <Container width={0}>\n <Stack space={4}>\n <Heading align=\"center\" size={1}>\n Choose login provider\n </Heading>\n\n <Stack space={2}>\n {loginUrls.map((provider, index) => (\n <Button\n key={`${provider.url}_${index}`}\n as=\"a\"\n href={provider.url}\n mode=\"ghost\"\n tone=\"default\"\n space={3}\n padding={3}\n text={provider.title}\n />\n ))}\n </Stack>\n </Stack>\n </Container>\n </Flex>\n </Card>\n )\n}\n"],"names":["StyledFlex","Fragment","Container"],"mappings":";;;;;;;;;;AAWO,MAAM,kBAAkB,MAAM;AAAA,EACnC,YAAY,OAAgB;AAExB,WAAO,SAAU,YACf,SACF,aAAa,SACb,OAAO,MAAM,WAAY,WAEzB,MAAM,MAAM,OAAO,IAEnB,MAAM,GAGR,KAAK,QAAQ;AAAA,EAAA;AAEjB;ACrBA,MAAM,QAAQ;AAAA,EACZ;AAAA,IACE,KAAK;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,EAAA;AAEX,GAEM,aAAa,OAAO,IAAI;AAAA;AAAA;AAAA;AAAA;AAYvB,SAAS,cAA+B;AAE3C,SAAA,qBAAC,MAAK,EAAA,WAAU,UAAS,KAAK,GAAG,SAAQ,UAAS,OAAM,UAAS,YAAY,GAC3E,UAAA;AAAA,IAAA,oBAAC,MAAK,EAAA,MAAM,GACV,UAAA,oBAAC,aAAW,CAAA,GACd;AAAA,IAEC,oBAAA,MAAA,EAAK,OAAM,UAAS,KAAK,GACvB,UAAM,MAAA,IAAI,CAAC,MAAM,UAChB,qBAAC,UACC,EAAA,UAAA;AAAA,MAAA,oBAAC,cAAW,OAAK,IAAC,MAAM,GACtB,8BAAC,KAAE,EAAA,MAAM,KAAK,KAAK,QAAO,UAAS,KAAI,uBACpC,UAAA,KAAK,MACR,CAAA,GACF;AAAA,MAEC,QAAQ,MAAM,SAAS,KACtB,oBAAC,QAAK,MAAM,GAAG,OAAK,IAAC,UAErB,SAAA,CAAA;AAAA,IAAA,KAVW,KAAK,KAYpB,CACD,EACH,CAAA;AAAA,EAAA,GACF;AAEJ;AC/CA,MAAM,OAAO,OAAO;AAAA;AAAA;AAAA,GAKd,YAAY,OAAO,IAAI;AAAA;AAAA;AAAA;AAAA,GAMvB,aAAa,OAAO,IAAI,KAExB,eAAe,OAAO,IAAI;AAAA;AAAA;AAsCzB,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,6BAAU,aAAY,EAAA;AAAA,EACtB;AACF,GAAsC;AACpC,6BACG,MACC,EAAA,UAAA,qBAAC,aAAU,WAAU,UAAS,KAAK,GACjC,UAAA;AAAA,IAAA,oBAAC,YAAW,EAAA,QAAM,IAAC,QAAQ,GAAG,UAAU,GACtC,UAAA,qBAAC,MAAK,EAAA,WAAU,UAAS,KAAK,GAC3B,UAAA;AAAA,MACC,UAAA,oBAAC,QAAK,cAAY,IAAC,UAAU,GAAG,eAAe,GAC5C,UACH,OAAA,CAAA;AAAA,MAGD,YACE,oBAAA,cAAA,EAAa,UAAU,GAAG,WAAU,UAClC,SACH,CAAA;AAAA,IAAA,EAAA,CAEJ,EACF,CAAA;AAAA,IAEC;AAAA,EAAA,EAAA,CACH,EACF,CAAA;AAEJ;ACnFA,MAAM,eAAe,OAAO,IAAI;AAAA;AAAA;AAUzB,SAAS,MAAM,EAAC,QAAQ,UAAwC;AAEnE,SAAA,oBAAC,eAAY,QAAgB,QAC3B,+BAAC,MAAK,EAAA,WAAU,UAAS,KAAK,GAC5B,UAAA;AAAA,IAAA,oBAAC,WAAQ,IAAG,MAAK,MAAM,GAAG,OAAM,UAAS,UAEzC,wBAAA,CAAA;AAAA,IAEA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,8BACG,cAAa,EAAA,OAAM,UAAS,SAAQ,UACnC,UAAC,oBAAA,SAAA,CAAA,CAAQ,EACX,CAAA;AAAA,QAGF,8BAAC,WAAU,CAAA,CAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EACb,EAAA,CACF,EACF,CAAA;AAEJ;AAEA,SAAS,YAAY;AACnB,QAAM,YAAY,aAAa;AAG7B,SAAA,oBAAC,MAAK,EAAA,WAAU,UAAS,KAAK,GAC3B,UAAU,UAAA,IAAI,CAAC,EAAC,OAAO,IAAA,MACrB,oBAAA,QAAA,EAAiB,MAAM,OAAO,IAAG,KAAI,MAAM,KAAK,MAAK,QAAA,GAAzC,GAAiD,CAC/D,EACH,CAAA;AAEJ;AClDA,MAAMA,eAAa,OAAO,IAAI;AAAA;AAAA;AAWvB,SAAS,cAAc,EAAC,QAAQ,UAA4C;AACjF,QAAM,iBAAiB,kBAAkB;AAEzC,SAAA,UAAU,MAAM;AACd,UAAM,MAAM,IAAI,IAAI,SAAS,IAAI;AACjC,mBAAe,IAAI,SAAS,CAAC,EAAE,KAAK,CAAC,wBAAwB;AACvD,6BAGF,QAAQ,aAAa,MAAM,IAAI,mBAAmB;AAAA,IAAA,CAErD;AAAA,EAAA,GACA,CAAC,cAAc,CAAC,GAGjB,oBAAC,eAAY,QAAgB,QAC3B,UAAC,qBAAAA,cAAA,EAAW,WAAU,UAAS,SAAQ,UAAS,OAAM,UAAS,KAAK,GAClE,UAAA;AAAA,IAAC,oBAAA,MAAA,EAAK,MAAM,GAAG,UAAe,wBAAA;AAAA,IAC9B,oBAAC,SAAQ,EAAA,MAAM,EAAG,CAAA;AAAA,EAAA,EAAA,CACpB,EACF,CAAA;AAEJ;AC1BA,MAAM,aAAa,OAAO,IAAI;AAAA;AAAA;AAUvB,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqC;AAC/B,MAAA,EAAE,iBAAiB,WAAkB,OAAA;AACzC,QAAM,SAAS,UAAA,GAET,cAAc,YAAY,YAAY;AACpC,UAAA,UACN,mBAAmB;AAAA,EAAA,GAClB,CAAC,QAAQ,kBAAkB,CAAC;AAG7B,SAAA,oBAAC,eAAY,QAAgB,QAC3B,+BAAC,YAAW,EAAA,WAAU,UAAS,KAAK,GAClC,UAAA;AAAA,IAAA,qBAAC,MAAK,EAAA,WAAU,UAAS,KAAK,GAC5B,UAAA;AAAA,MAAC,oBAAA,MAAA,EAAK,IAAG,MAAK,OAAM,UAAS,QAAO,QAAO,MAAM,GAAG,UAEpD,uBAAA,CAAA;AAAA,0BACC,MAAK,EAAA,MAAM,GAAG,OAAM,UAAS,UAE9B,+DAAA,CAAA;AAAA,IAAA,GACF;AAAA,wBACC,QAAO,EAAA,MAAK,SAAQ,MAAK,WAAU,SAAS,YAAa,CAAA;AAAA,EAAA,EAAA,CAC5D,EACF,CAAA;AAEJ;ACGO,SAAS,aAAa;AAAA,EAC3B,sBAAsB;AAAA,EACtB,GAAG;AACL,GAAuC;AAC/B,QAAA,EAAC,QAAQ,WAAU,OACnB,oBAAoB,QAAQ,MACzB,SAAuC,eAA8B;AAC1E,WAAQ,oBAAA,qBAAA,EAAqB,GAAG,eAAe,QAAgB,QAAgB;AAAA,EAEhF,GAAA,CAAC,QAAQ,QAAQ,mBAAmB,CAAC;AAExC,6BACG,eAAc,EAAA,mBACb,8BAAC,YAAY,EAAA,GAAG,MAAO,CAAA,GACzB;AAEJ;AAOA,SAAS,WAAW;AAAA,EAClB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,QAAM,YAAY,aAAa;AAE/B,UAAQ,UAAU,MAAM;AAAA,IACtB,KAAK,cAAc;AACX,YAAA,IAAI,UAAU,UAAU,KAAK;AAAA,IAErC,KAAK,cAAc;AACV,aAAA,oBAAC,mBAAmB,EAAA,GAAG,MAAO,CAAA;AAAA,IAEvC,KAAK,cAAc;AACV,aAAA;AAAA,IAET;AACS,aAAA,oBAAC,gBAAgB,EAAA,GAAG,MAAO,CAAA;AAAA,EAAA;AAGxC;AC9Fa,MAAA,qBAAqB,CAAC,UAG7B,qBAAAC,YAAA,EAAA,UAAA;AAAA,EAAA,oBAAC,SAAO,EAAA,UAAA;AAAA;AAAA;AAAA;AAAA,SAIN;AAAA,EACD,oBAAA,MAAA,EAAG,WAAU,qCAAqC,gBAAM,SAAS,CAAA;AAAA,EACpE,CAAA;AAIJ,mBAAmB,cAAc;ACbpB,MAAA,qBAAqB,CAAC,UAC1B,oBAAC,QAAG,WAAU,gCAAgC,gBAAM,SAAS,CAAA;AAGtE,mBAAmB,cAAc;ACgB1B,MAAM,wBAAwB;AAAA,EACnC,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX;AAAA,KAEF,QACgB;AAEhB,QAAI,CAAC;AACH,aAAS,oBAAAA,YAAA,EAAA;AAGP,QAAA;AAEJ,QAAI,OAAO,KAAK;AACd,YAAM,UAAU,IAAI,IAAI,MAAM,GAAG;AACjC,cAAQ,aAAa,IAAI,KAAK,IAAI,GAClC,QAAQ,aAAa,IAAI,KAAK,IAAI,GAClC,QAAQ,aAAa,IAAI,OAAO,MAAM;AAChC,YAAA,WAAW,QAAQ,SAAS;AAElC,qBAAgB,oBAAA,OAAA,EAAI,KAAK,UAAU,KAAI,IAAG;AAAA,IAC5C;AACE,yCAAgB,cAAa,EAAA;AAG/B,WAEI,qBAAAA,YAAA,EAAA,UAAA;AAAA,MAAA,oBAAC,SAAO,EAAA,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAmEN;AAAA,MACF;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,WAAW,6DAA6D,WAAW,aAAa,EAAE;AAAA,UAElG,UAAA,qBAAC,OAAI,EAAA,WAAU,4DACb,UAAA;AAAA,YAAC,oBAAA,UAAA,EAAO,WAAU,oGACf,UACH,cAAA;AAAA,YAEA,qBAAC,OAAI,EAAA,WAAU,sCACb,UAAA;AAAA,cAAC,oBAAA,KAAA,EAAE,WAAU,qCAAqC,UAAM,OAAA;AAAA,cACvD,YAAY,oBAAC,KAAE,EAAA,WAAU,4BAA4B,UAAS,SAAA,CAAA;AAAA,YAAA,GACjE;AAAA,YAEC,WACE,oBAAA,UAAA,EAAO,WAAU,qDAChB,8BAAC,cAAW,EAAA,WAAU,UAAU,UAAA,QAAA,CAAQ,EAC1C,CAAA;AAAA,YAID,WAAW,eACT,qBAAA,UAAA,EAAO,WAAU,uDAAsD,UAAA;AAAA,cAAA;AAAA,cAClE,oBAAA,cAAA,EAAW,WAAU,UAAS,UAAS,YAAA,CAAA;AAAA,YAAA,GAC7C;AAAA,YAID,WAAW,WACT,qBAAA,UAAA,EAAO,WAAU,mDAAkD,UAAA;AAAA,cAAA;AAAA,cAC9D,oBAAA,cAAA,EAAW,WAAU,UAAS,UAAK,QAAA,CAAA;AAAA,YAAA,EACzC,CAAA;AAAA,UAAA,EAEJ,CAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,GACF;AAAA,EAAA;AAGN;AAEA,sBAAsB,cAAc;AChJ7B,MAAM,aAAa,MAAoB;AAC5C,QAAM,YAAY,gBACZ,YAAY,aAAa;AAG/B,SAFA,kBAEI,GAAA,UAAU,SAAS,eACd,oBAAC,SAAI,UAAa,iBAAA,IAIvB,UAAU,SAAS,kCACb,OAAI,EAAA,UAAA,oBAAA,CAAiB,IAQ5B,oBAAA,MAAA,EAAK,QAAO,QAAO,UAAS,QAAO,UAAU,GAC5C,8BAAC,MAAK,EAAA,QAAO,QAAO,WAAU,UAAS,OAAM,UAAS,SAAQ,UAAS,YAAY,GACjF,8BAACC,aAAU,EAAA,OAAO,GAChB,UAAC,qBAAA,OAAA,EAAM,OAAO,GACZ,UAAA;AAAA,IAAA,oBAAC,SAAQ,EAAA,OAAM,UAAS,MAAM,GAAG,UAEjC,yBAAA;AAAA,IAEA,oBAAC,SAAM,OAAO,GACX,oBAAU,IAAI,CAAC,UAAU,UACxB;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,IAAG;AAAA,QACH,MAAM,SAAS;AAAA,QACf,MAAK;AAAA,QACL,MAAK;AAAA,QACL,OAAO;AAAA,QACP,SAAS;AAAA,QACT,MAAM,SAAS;AAAA,MAAA;AAAA,MAPV,GAAG,SAAS,GAAG,IAAI,KAAK;AAAA,IAAA,CAShC,EACH,CAAA;AAAA,EAAA,GACF,EAAA,CACF,EACF,CAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"components.js","sources":["../src/components/auth/AuthError.ts","../src/components/auth/LoginFooter.tsx","../src/components/auth/LoginLayout.tsx","../src/components/auth/Login.tsx","../src/components/auth/LoginCallback.tsx","../src/components/auth/LoginError.tsx","../src/components/auth/AuthBoundary.tsx","../src/components/DocumentGridLayout/DocumentGridLayout.tsx","../src/components/DocumentListLayout/DocumentListLayout.tsx","../src/components/DocumentPreviewLayout/DocumentPreviewLayout.tsx","../src/components/Login/LoginLinks.tsx"],"sourcesContent":["/**\n * Error class for authentication-related errors. Wraps errors thrown during the\n * authentication flow.\n *\n * @remarks\n * This class provides a consistent error type for authentication failures while\n * preserving the original error as the cause. If the original error has a\n * message property, it will be used as the error message.\n *\n * @alpha\n */\nexport class AuthError extends Error {\n constructor(error: unknown) {\n if (\n typeof error === 'object' &&\n !!error &&\n 'message' in error &&\n typeof error.message === 'string'\n ) {\n super(error.message)\n } else {\n super()\n }\n\n this.cause = error\n }\n}\n","import {SanityLogo} from '@sanity/logos'\nimport {Flex, Text} from '@sanity/ui'\nimport {Fragment} from 'react'\n\nconst LINKS = [\n {\n url: 'https://slack.sanity.io/',\n i18nKey: 'workspaces.community-title',\n title: 'Community',\n },\n {\n url: 'https://www.sanity.io/docs',\n i18nKey: 'workspaces.docs-title',\n title: 'Docs',\n },\n {\n url: 'https://www.sanity.io/legal/privacy',\n i18nKey: 'workspaces.privacy-title',\n title: 'Privacy',\n },\n {\n url: 'https://www.sanity.io',\n i18nKey: 'workspaces.sanity-io-title',\n title: 'sanity.io',\n },\n]\n\n/**\n * Default footer component for login screens showing Sanity branding and legal\n * links.\n *\n * @alpha\n */\nexport function LoginFooter(): React.ReactNode {\n return (\n <Flex direction=\"column\" gap={4} justify=\"center\" align=\"center\" paddingTop={2}>\n <Text size={3}>\n <SanityLogo />\n </Text>\n\n <Flex align=\"center\" gap={2}>\n {LINKS.map((link, index) => (\n <Fragment key={link.title}>\n <Text muted size={1}>\n <a\n href={link.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{color: 'inherit'}}\n >\n {link.title}\n </a>\n </Text>\n\n {index < LINKS.length - 1 && (\n <Text size={1} muted>\n •\n </Text>\n )}\n </Fragment>\n ))}\n </Flex>\n </Flex>\n )\n}\n","import {Card, Flex} from '@sanity/ui'\n\nimport {LoginFooter} from './LoginFooter'\n\n/**\n * @alpha\n */\nexport interface LoginLayoutProps {\n /** Optional header content rendered at top of card */\n header?: React.ReactNode\n\n /** Optional footer content rendered below card. Defaults to an internal login footer */\n footer?: React.ReactNode\n\n /** Main content rendered in card body */\n children?: React.ReactNode\n}\n\n/**\n * Layout component for login-related screens providing consistent styling and structure.\n * Renders content in a centered card with optional header and footer sections.\n *\n * Can be used to build custom login screens for the AuthBoundary component, including:\n * - Login provider selection (LoginComponent)\n * - OAuth callback handling (CallbackComponent)\n * - Error states (LoginErrorComponent)\n *\n * @example\n * ```tsx\n * // Custom login screen using the layout\n * function CustomLogin({header, footer}: LoginLayoutProps) {\n * return (\n * <LoginLayout\n * header={header}\n * footer={footer}\n * >\n * <CustomLoginContent />\n * </LoginLayout>\n * )\n * }\n *\n * // Use with AuthBoundary\n * <AuthBoundary\n * LoginComponent={CustomLogin}\n * header={<Logo />}\n * >\n * <ProtectedContent />\n * </AuthBoundary>\n * ```\n *\n * @alpha\n */\nexport function LoginLayout({\n children,\n footer = <LoginFooter />,\n header,\n}: LoginLayoutProps): React.ReactNode {\n return (\n <div style={{width: '100%', display: 'flex'}}>\n <Flex direction=\"column\" gap={4} style={{width: '320px', margin: 'auto', display: 'flex'}}>\n <Card border radius={2} paddingY={4}>\n <Flex direction=\"column\" gap={4}>\n {header && (\n <Card borderBottom paddingX={4} paddingBottom={3}>\n {header}\n </Card>\n )}\n\n {children && (\n <Flex paddingX={4} direction=\"column\" style={{minHeight: '154px'}}>\n {children}\n </Flex>\n )}\n </Flex>\n </Card>\n\n {footer}\n </Flex>\n </div>\n )\n}\n","import {Button, Flex, Heading, Spinner} from '@sanity/ui'\nimport {type JSX, Suspense} from 'react'\n\nimport {useLoginUrls} from '../../hooks/auth/useLoginUrls'\nimport {LoginLayout, type LoginLayoutProps} from './LoginLayout'\n\n/**\n * Login component that displays available authentication providers.\n * Renders a list of login options with a loading fallback while providers load.\n *\n * @alpha\n */\nexport function Login({header, footer}: LoginLayoutProps): JSX.Element {\n return (\n <LoginLayout header={header} footer={footer}>\n <Flex direction=\"column\" gap={4}>\n <Heading as=\"h1\" size={1} align=\"center\">\n Choose login provider\n </Heading>\n\n <Suspense\n fallback={\n <Flex align=\"center\" justify=\"center\" style={{height: '123px'}}>\n <Spinner />\n </Flex>\n }\n >\n <Providers />\n </Suspense>\n </Flex>\n </LoginLayout>\n )\n}\n\nfunction Providers() {\n const loginUrls = useLoginUrls()\n\n return (\n <Flex direction=\"column\" gap={3}>\n {loginUrls.map(({title, url}) => (\n <Button key={url} text={title} as=\"a\" href={url} mode=\"ghost\" />\n ))}\n </Flex>\n )\n}\n","import {Flex, Spinner, Text} from '@sanity/ui'\nimport {useEffect} from 'react'\n\nimport {useHandleCallback} from '../../hooks/auth/useHandleCallback'\nimport {LoginLayout, type LoginLayoutProps} from './LoginLayout'\n\n/**\n/**\n * Component shown during auth callback processing that handles login completion.\n * Automatically processes the auth callback when mounted and updates the URL\n * to remove callback parameters without triggering a page reload.\n *\n * @alpha\n */\nexport function LoginCallback({header, footer}: LoginLayoutProps): React.ReactNode {\n const handleCallback = useHandleCallback()\n\n useEffect(() => {\n const url = new URL(location.href)\n handleCallback(url.toString()).then((replacementLocation) => {\n if (replacementLocation) {\n // history API with `replaceState` is used to prevent a reload but still\n // remove the short-lived token from the URL\n history.replaceState(null, '', replacementLocation)\n }\n })\n }, [handleCallback])\n\n return (\n <LoginLayout header={header} footer={footer}>\n <Flex direction=\"column\" justify=\"center\" align=\"center\" gap={4} style={{margin: 'auto'}}>\n <Text size={1}>Logging you in…</Text>\n <Spinner size={4} />\n </Flex>\n </LoginLayout>\n )\n}\n","import {Button, Flex, Text} from '@sanity/ui'\nimport {useCallback} from 'react'\nimport {type FallbackProps} from 'react-error-boundary'\n\nimport {useLogOut} from '../../hooks/auth/useLogOut'\nimport {AuthError} from './AuthError'\nimport {LoginLayout, type LoginLayoutProps} from './LoginLayout'\n\n/**\n * @alpha\n */\nexport type LoginErrorProps = FallbackProps & LoginLayoutProps\n\n/**\n * Displays authentication error details and provides retry functionality.\n * Only handles {@link AuthError} instances - rethrows other error types.\n *\n * @alpha\n */\nexport function LoginError({\n error,\n resetErrorBoundary,\n header,\n footer,\n}: LoginErrorProps): React.ReactNode {\n if (!(error instanceof AuthError)) throw error\n const logout = useLogOut()\n\n const handleRetry = useCallback(async () => {\n await logout()\n resetErrorBoundary()\n }, [logout, resetErrorBoundary])\n\n return (\n <LoginLayout header={header} footer={footer}>\n <Flex direction=\"column\" gap={4} style={{margin: 'auto'}}>\n <Flex direction=\"column\" gap={3}>\n <Text as=\"h2\" align=\"center\" weight=\"bold\" size={3}>\n Authentication Error\n </Text>\n <Text size={1} align=\"center\">\n Please try again or contact support if the problem persists.\n </Text>\n </Flex>\n <Button text=\"Retry\" tone=\"primary\" onClick={handleRetry} />\n </Flex>\n </LoginLayout>\n )\n}\n","import {AuthStateType} from '@sanity/sdk'\nimport {useMemo} from 'react'\nimport {ErrorBoundary, type FallbackProps} from 'react-error-boundary'\n\nimport {useAuthState} from '../../hooks/auth/useAuthState'\nimport {AuthError} from './AuthError'\nimport {Login} from './Login'\nimport {LoginCallback} from './LoginCallback'\nimport {LoginError, type LoginErrorProps} from './LoginError'\nimport type {LoginLayoutProps} from './LoginLayout'\n\n/**\n * @alpha\n */\nexport interface AuthBoundaryProps extends LoginLayoutProps {\n /**\n * Custom component to render the login screen.\n * Receives all login layout props. Defaults to {@link Login}.\n */\n LoginComponent?: React.ComponentType<LoginLayoutProps>\n\n /**\n * Custom component to render during OAuth callback processing.\n * Receives all login layout props. Defaults to {@link LoginCallback}.\n */\n CallbackComponent?: React.ComponentType<LoginLayoutProps>\n\n /**\n * Custom component to render when authentication errors occur.\n * Receives login layout props and error boundary props. Defaults to\n * {@link LoginError}\n */\n LoginErrorComponent?: React.ComponentType<LoginErrorProps>\n}\n\n/**\n * A component that handles authentication flow and error boundaries for a\n * protected section of the application.\n *\n * @remarks\n * This component manages different authentication states and renders the\n * appropriate components based on that state.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AuthBoundary header={<MyLogo />}>\n * <ProtectedContent />\n * </AuthBoundary>\n * )\n * }\n * ```\n *\n * @alpha\n */\nexport function AuthBoundary({\n LoginErrorComponent = LoginError,\n ...props\n}: AuthBoundaryProps): React.ReactNode {\n const {header, footer} = props\n const FallbackComponent = useMemo(() => {\n return function LoginComponentWithLayoutProps(fallbackProps: FallbackProps) {\n return <LoginErrorComponent {...fallbackProps} header={header} footer={footer} />\n }\n }, [header, footer, LoginErrorComponent])\n\n return (\n <ErrorBoundary FallbackComponent={FallbackComponent}>\n <AuthSwitch {...props} />\n </ErrorBoundary>\n )\n}\n\ninterface AuthSwitchProps extends LoginLayoutProps {\n LoginComponent?: React.ComponentType<LoginLayoutProps>\n CallbackComponent?: React.ComponentType<LoginLayoutProps>\n}\n\nfunction AuthSwitch({\n LoginComponent = Login,\n CallbackComponent = LoginCallback,\n children,\n ...props\n}: AuthSwitchProps) {\n const authState = useAuthState()\n\n switch (authState.type) {\n case AuthStateType.ERROR: {\n throw new AuthError(authState.error)\n }\n case AuthStateType.LOGGING_IN: {\n return <CallbackComponent {...props} />\n }\n case AuthStateType.LOGGED_IN: {\n return children\n }\n default: {\n return <LoginComponent {...props} />\n }\n }\n}\n","import '../../css/styles.css'\n\nimport type {PropsWithChildren, ReactElement} from 'react'\n\n/**\n * @public\n */\nexport const DocumentGridLayout = (props: PropsWithChildren): ReactElement => {\n return (\n <>\n <style>{`\n .DocumentGridLayout {\n grid-template-columns: repeat(auto-fit, minmax(38ch, 1fr));\n }\n `}</style>\n <ol className=\"DocumentGridLayout list-none grid\">{props.children}</ol>\n </>\n )\n}\n\nDocumentGridLayout.displayName = 'DocumentGridLayout'\n","import '../../css/styles.css'\n\nimport type {PropsWithChildren, ReactElement} from 'react'\n\n/**\n * @public\n */\nexport const DocumentListLayout = (props: PropsWithChildren): ReactElement => {\n return <ol className=\"DocumentListLayout list-none\">{props.children}</ol>\n}\n\nDocumentListLayout.displayName = 'DocumentListLayout'\n","import 'inter-ui/inter.css'\nimport '../../css/styles.css'\n\nimport {DocumentIcon} from '@sanity/icons'\nimport {forwardRef, type JSX} from 'react'\n\n/**\n * @public\n */\nexport interface DocumentPreviewLayoutProps {\n docType?: string\n media?: {type: string; url: string} | null | undefined\n onClick?: () => void\n selected?: boolean\n status?: string\n subtitle?: string\n title: string\n}\n\n/**\n * This is a component that renders a document preview.\n *\n * @public\n *\n * @param props - The props for the DocumentPreviewLayout component.\n * @returns - The DocumentPreviewLayout component.\n */\nexport const DocumentPreviewLayout = forwardRef(\n (\n {\n docType,\n media,\n onClick,\n selected = false,\n status = '',\n subtitle = '',\n title,\n }: DocumentPreviewLayoutProps,\n ref: React.Ref<HTMLElement>,\n ): JSX.Element => {\n // @TODO: empty state\n if (!title) {\n return <></>\n }\n\n let PreviewMedia\n\n if (media?.url) {\n const baseUrl = new URL(media.url)\n baseUrl.searchParams.set('h', '66')\n baseUrl.searchParams.set('w', '66')\n baseUrl.searchParams.set('fit', 'crop')\n const mediaUrl = baseUrl.toString()\n // media url string params for sanity img\n PreviewMedia = <img src={mediaUrl} alt=\"\" />\n } else {\n PreviewMedia = <DocumentIcon />\n }\n\n return (\n <>\n <style>{`\n .DocumentPreviewLayout {\n --_hoverFocusBg: light-dark(var(--shade-11), var(--tint-1));\n --_selectedBg: light-dark(var(--blue-10), var(--blue-2));\n --_selectedFg: light-dark(var(--gray-1), var(--gray-10));\n --_titleFg: light-dark(var(--gray-1), var(--gray-10));\n --_subtitleFg: light-dark(var(--gray-4), var(--gray-7));\n --_docTypeFg: light-dark(var(--gray-1), var(--gray-10));\n --_docTypeBg: light-dark(var(--shade-10), var(--tint-1));\n --_publishedFg: light-dark(var(--gray-1), var(--gray-10));\n --_publishedBg: light-dark(var(--green-10), var(--green-2));\n --_draftFg: light-dark(var(--gray-1), var(--gray-10));\n --_draftBg: light-dark(var(--yellow-6), var(--yellow-2));\n\n appearance: none;\n\n &:has(:hover, focus) {\n background-color: var(--hoverFocusBg, var(--_hoverFocusBg));\n }\n\n &.selected {\n background-color: var(--selectedBg, var(--_selectedBg));\n color: var(--selectedFg, var(--_selectedFg));\n }\n\n .Title {\n color: var(--titleFg, var(--_titleFg));\n }\n\n &:not(.selected) .Subtitle {\n color: var(--subtitleFg, var(--_subtitleFg));\n }\n\n .Media {\n aspect-ratio: 1;\n inline-size: 33px;\n border-color: var(--gray-8);\n }\n\n .DocType {\n color: var(--docTypeFg, var(--_docTypeFg));\n background-color: var(--docTypeBg, var(--_docTypeBg));\n }\n\n .Published {\n color: var(--publishedFg, var(--_publishedFg));\n background-color: var(--publishedBg, var(--_publishedBg));\n }\n\n .Draft {\n color: var(--draftFg, var(--_draftFg));\n background-color: var(--draftBg, var(--_draftBg));\n }\n\n :is(.Published, .Draft) figcaption {\n @container (width < 52ch) {\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n height: 1px;\n overflow: hidden;\n position: absolute;\n white-space: nowrap;\n width: 1px;\n }\n }\n\n }\n `}</style>\n <button\n onClick={onClick}\n ref={ref as React.Ref<HTMLButtonElement>}\n className={`DocumentPreviewLayout block si-100 text-start p-1 radius1 ${selected ? 'selected' : ''}`}\n >\n <div className=\"container-inline flex align-items-center gap-2 font-sans\">\n <figure className=\"Media border0 border-solid flex-none flex align-items-center justify-content-center object-cover\">\n {PreviewMedia}\n </figure>\n\n <div className=\"leading2 flex-grow overflow-hidden\">\n <p className=\"Title text-1 font-medium truncate\">{title}</p>\n {subtitle && <p className=\"Subtitle text-1 truncate\">{subtitle}</p>}\n </div>\n\n {docType && (\n <figure className=\"DocType inline-block pb-5 pi-3 radius-pill text-2\">\n <figcaption className=\"inline\">{docType}</figcaption>\n </figure>\n )}\n\n {/* @TODO: finalize UI for this */}\n {status === 'published' && (\n <figure className=\"Published inline-block pb-5 pi-3 radius-pill text-2\">\n ✔︎ <figcaption className=\"inline\">published</figcaption>\n </figure>\n )}\n\n {/* @TODO: finalize UI for this */}\n {status === 'draft' && (\n <figure className=\"Draft inline-block pb-5 pi-3 radius-pill text-2\">\n ⛑︎ <figcaption className=\"inline\">draft</figcaption>\n </figure>\n )}\n </div>\n </button>\n </>\n )\n },\n)\n\nDocumentPreviewLayout.displayName = 'DocumentPreviewLayout'\n","import {Button, Card, Container, Flex, Heading, Stack} from '@sanity/ui'\nimport {type ReactElement} from 'react'\n\nimport {useAuthState} from '../../hooks/auth/useAuthState'\nimport {useHandleCallback} from '../../hooks/auth/useHandleCallback'\nimport {useLoginUrls} from '../../hooks/auth/useLoginUrls'\n\n/**\n * Component that handles Sanity authentication flow and renders login provider options\n *\n * @public\n *\n * @returns Rendered component\n *\n * @remarks\n * The component handles three states:\n * 1. Loading state during token exchange\n * 2. Success state after successful authentication\n * 3. Provider selection UI when not authenticated\n *\n * @example\n * ```tsx\n * const config = { projectId: 'your-project-id', dataset: 'production' }\n * return <LoginLinks sanityInstance={config} />\n * ```\n */\nexport const LoginLinks = (): ReactElement => {\n const loginUrls = useLoginUrls()\n const authState = useAuthState()\n useHandleCallback()\n\n if (authState.type === 'logging-in') {\n return <div>Logging in...</div>\n }\n\n // Show success state after authentication\n if (authState.type === 'logged-in') {\n return <div>You are logged in</div>\n }\n\n /**\n * Render provider selection UI\n * Uses Sanity UI components for consistent styling\n */\n return (\n <Card height=\"fill\" overflow=\"auto\" paddingX={4}>\n <Flex height=\"fill\" direction=\"column\" align=\"center\" justify=\"center\" paddingTop={4}>\n <Container width={0}>\n <Stack space={4}>\n <Heading align=\"center\" size={1}>\n Choose login provider\n </Heading>\n\n <Stack space={2}>\n {loginUrls.map((provider, index) => (\n <Button\n key={`${provider.url}_${index}`}\n as=\"a\"\n href={provider.url}\n mode=\"ghost\"\n tone=\"default\"\n space={3}\n padding={3}\n text={provider.title}\n />\n ))}\n </Stack>\n </Stack>\n </Container>\n </Flex>\n </Card>\n )\n}\n"],"names":["Fragment"],"mappings":"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"}
package/dist/hooks.d.ts CHANGED
@@ -4,7 +4,6 @@ import {CurrentUser} from '@sanity/types'
4
4
  import {DocumentHandle} from '@sanity/sdk'
5
5
  import {DocumentListOptions} from '@sanity/sdk'
6
6
  import {PreviewValue} from '@sanity/sdk'
7
- import {RefObject} from 'react'
8
7
  import type {SanityInstance} from '@sanity/sdk'
9
8
 
10
9
  /**
@@ -169,7 +168,7 @@ export declare function usePreview({
169
168
  */
170
169
  export declare interface UsePreviewOptions {
171
170
  document: DocumentHandle
172
- ref?: RefObject<HTMLElement>
171
+ ref: HTMLElement | null
173
172
  }
174
173
 
175
174
  /**
package/dist/hooks.js CHANGED
@@ -49,7 +49,7 @@ function usePreview({
49
49
  ([entry]) => observer.next(entry.isIntersecting),
50
50
  { rootMargin: "0px", threshold: 0 }
51
51
  );
52
- return ref?.current && intersectionObserver.observe(ref.current), () => intersectionObserver.disconnect();
52
+ return ref && intersectionObserver.observe(ref), () => intersectionObserver.disconnect();
53
53
  }).pipe(
54
54
  startWith(!1),
55
55
  distinctUntilChanged(),
package/dist/hooks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sources":["../src/hooks/auth/useAuthToken.tsx","../src/hooks/auth/useCurrentUser.tsx","../src/hooks/documentCollection/useDocuments.ts","../src/hooks/preview/usePreview.tsx"],"sourcesContent":["import {getTokenState} from '@sanity/sdk'\n\nimport {createStateSourceHook} from '../helpers/createStateSourceHook'\n\n/**\n * Hook to get the currently logged in user\n * @public\n * @returns The current user or null if not authenticated\n */\nexport const useAuthToken = createStateSourceHook(getTokenState)\n","import {getCurrentUserState} from '@sanity/sdk'\n\nimport {createStateSourceHook} from '../helpers/createStateSourceHook'\n\n/**\n * Hook to get the currently logged in user\n * @public\n * @returns The current user or null if not authenticated\n */\nexport const useCurrentUser = createStateSourceHook(getCurrentUserState)\n","import {createDocumentListStore, type DocumentHandle, type DocumentListOptions} from '@sanity/sdk'\nimport {useCallback, useEffect, useState, useSyncExternalStore} from 'react'\n\nimport {useSanityInstance} from '../context/useSanityInstance'\n\n/**\n * @public\n */\nexport interface UseDocuments {\n loadMore: () => void\n results: DocumentHandle[]\n isPending: boolean\n hasMore: boolean\n count: number\n}\n\ntype DocumentListStore = ReturnType<typeof createDocumentListStore>\ntype DocumentListState = ReturnType<DocumentListStore['getState']>['getCurrent']\nconst STABLE_EMPTY = {\n results: [],\n isPending: false,\n hasMore: false,\n count: 0,\n}\n\n/**\n * Hook to get the list of documents for specified options\n *\n * @public\n *\n * @param options - options for the document list\n * @returns result of the document list and function to load more\n */\nexport function useDocuments(options: DocumentListOptions = {}): UseDocuments {\n const instance = useSanityInstance()\n\n // NOTE: useState is used because it guaranteed to return a stable reference\n // across renders\n const [ref] = useState<{\n storeInstance: DocumentListStore | null\n getCurrent: DocumentListState\n initialOptions: DocumentListOptions\n }>(() => ({\n storeInstance: null,\n getCurrent: () => STABLE_EMPTY,\n initialOptions: options,\n }))\n\n // serialize options to ensure it only calls `setOptions` when the values\n // themselves changes (in cases where devs put config inline)\n const serializedOptions = JSON.stringify(options)\n useEffect(() => {\n ref.storeInstance?.setOptions(JSON.parse(serializedOptions))\n }, [ref, serializedOptions])\n\n const subscribe = useCallback(\n (onStoreChanged: () => void) => {\n // to match the lifecycle of `useSyncExternalState`, we create the store\n // instance after subscribe and mutate the ref to connect everything\n ref.storeInstance = createDocumentListStore(instance)\n ref.storeInstance.setOptions(ref.initialOptions)\n const state = ref.storeInstance.getState()\n ref.getCurrent = state.getCurrent\n const unsubscribe = state.subscribe(onStoreChanged)\n\n return () => {\n // unsubscribe to clean up the state subscriptions\n unsubscribe()\n // dispose of the instance\n ref.storeInstance?.dispose()\n }\n },\n [instance, ref],\n )\n\n const getSnapshot = useCallback(() => {\n return ref.getCurrent()\n }, [ref])\n\n const state = useSyncExternalStore(subscribe, getSnapshot)\n\n const loadMore = useCallback(() => {\n ref.storeInstance?.loadMore()\n }, [ref])\n\n return {loadMore, ...state}\n}\n","import {type DocumentHandle, getPreviewState, type PreviewValue, resolvePreview} from '@sanity/sdk'\nimport {type RefObject, useCallback, useMemo, useSyncExternalStore} from 'react'\nimport {distinctUntilChanged, EMPTY, Observable, startWith, switchMap} from 'rxjs'\n\nimport {useSanityInstance} from '../context/useSanityInstance'\n\n/**\n * @alpha\n */\nexport interface UsePreviewOptions {\n document: DocumentHandle\n ref?: RefObject<HTMLElement>\n}\n\n/**\n * @alpha\n */\nexport function usePreview({\n document: {_id, _type},\n ref,\n}: UsePreviewOptions): [PreviewValue, boolean] {\n const instance = useSanityInstance()\n\n const stateSource = useMemo(\n () => getPreviewState(instance, {document: {_id, _type}}),\n [instance, _id, _type],\n )\n\n // Create subscribe function for useSyncExternalStore\n const subscribe = useCallback(\n (onStoreChanged: () => void) => {\n const subscription = new Observable<boolean>((observer) => {\n // for environments that don't have an intersection observer\n if (typeof IntersectionObserver === 'undefined') return\n\n const intersectionObserver = new IntersectionObserver(\n ([entry]) => observer.next(entry.isIntersecting),\n {rootMargin: '0px', threshold: 0},\n )\n if (ref?.current) intersectionObserver.observe(ref.current)\n return () => intersectionObserver.disconnect()\n })\n .pipe(\n startWith(false),\n distinctUntilChanged(),\n switchMap((isVisible) =>\n isVisible\n ? new Observable<void>((obs) => {\n return stateSource.subscribe(() => obs.next())\n })\n : EMPTY,\n ),\n )\n .subscribe({next: onStoreChanged})\n\n return () => subscription.unsubscribe()\n },\n [stateSource, ref],\n )\n\n // Create getSnapshot function to return current state\n const getSnapshot = useCallback(() => {\n const previewTuple = stateSource.getCurrent()\n if (!previewTuple[0]) throw resolvePreview(instance, {document: {_id, _type}})\n return previewTuple as [PreviewValue, boolean]\n }, [_id, _type, instance, stateSource])\n\n return useSyncExternalStore(subscribe, getSnapshot)\n}\n"],"names":["state"],"mappings":";;;;;AASa,MAAA,eAAe,sBAAsB,aAAa,GCAlD,iBAAiB,sBAAsB,mBAAmB,GCSjE,eAAe;AAAA,EACnB,SAAS,CAAC;AAAA,EACV,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AACT;AAUgB,SAAA,aAAa,UAA+B,IAAkB;AAC5E,QAAM,WAAW,kBAAkB,GAI7B,CAAC,GAAG,IAAI,SAIX,OAAO;AAAA,IACR,eAAe;AAAA,IACf,YAAY,MAAM;AAAA,IAClB,gBAAgB;AAAA,EAChB,EAAA,GAII,oBAAoB,KAAK,UAAU,OAAO;AAChD,YAAU,MAAM;AACd,QAAI,eAAe,WAAW,KAAK,MAAM,iBAAiB,CAAC;AAAA,EAAA,GAC1D,CAAC,KAAK,iBAAiB,CAAC;AAE3B,QAAM,YAAY;AAAA,IAChB,CAAC,mBAA+B;AAG1B,UAAA,gBAAgB,wBAAwB,QAAQ,GACpD,IAAI,cAAc,WAAW,IAAI,cAAc;AACzCA,YAAAA,SAAQ,IAAI,cAAc,SAAS;AACzC,UAAI,aAAaA,OAAM;AACjB,YAAA,cAAcA,OAAM,UAAU,cAAc;AAElD,aAAO,MAAM;AAEC,uBAEZ,IAAI,eAAe,QAAQ;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,CAAC,UAAU,GAAG;AAAA,EAGV,GAAA,cAAc,YAAY,MACvB,IAAI,WAAW,GACrB,CAAC,GAAG,CAAC,GAEF,QAAQ,qBAAqB,WAAW,WAAW;AAMlD,SAAA,EAAC,UAJS,YAAY,MAAM;AACjC,QAAI,eAAe,SAAS;AAAA,KAC3B,CAAC,GAAG,CAAC,GAEU,GAAG,MAAK;AAC5B;ACrEO,SAAS,WAAW;AAAA,EACzB,UAAU,EAAC,KAAK,MAAK;AAAA,EACrB;AACF,GAA+C;AACvC,QAAA,WAAW,qBAEX,cAAc;AAAA,IAClB,MAAM,gBAAgB,UAAU,EAAC,UAAU,EAAC,KAAK,MAAK,GAAE;AAAA,IACxD,CAAC,UAAU,KAAK,KAAK;AAAA,KAIjB,YAAY;AAAA,IAChB,CAAC,mBAA+B;AAC9B,YAAM,eAAe,IAAI,WAAoB,CAAC,aAAa;AAErD,YAAA,OAAO,uBAAyB,IAAa;AAEjD,cAAM,uBAAuB,IAAI;AAAA,UAC/B,CAAC,CAAC,KAAK,MAAM,SAAS,KAAK,MAAM,cAAc;AAAA,UAC/C,EAAC,YAAY,OAAO,WAAW,EAAC;AAAA,QAClC;AACI,eAAA,KAAK,WAAS,qBAAqB,QAAQ,IAAI,OAAO,GACnD,MAAM,qBAAqB,WAAW;AAAA,MAC9C,CAAA,EACE;AAAA,QACC,UAAU,EAAK;AAAA,QACf,qBAAqB;AAAA,QACrB;AAAA,UAAU,CAAC,cACT,YACI,IAAI,WAAiB,CAAC,QACb,YAAY,UAAU,MAAM,IAAI,KAAK,CAAC,CAC9C,IACD;AAAA,QAAA;AAAA,MAGP,EAAA,UAAU,EAAC,MAAM,gBAAe;AAE5B,aAAA,MAAM,aAAa,YAAY;AAAA,IACxC;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EAAA,GAIb,cAAc,YAAY,MAAM;AAC9B,UAAA,eAAe,YAAY,WAAW;AAC5C,QAAI,CAAC,aAAa,CAAC,EAAS,OAAA,eAAe,UAAU,EAAC,UAAU,EAAC,KAAK,SAAO;AACtE,WAAA;AAAA,KACN,CAAC,KAAK,OAAO,UAAU,WAAW,CAAC;AAE/B,SAAA,qBAAqB,WAAW,WAAW;AACpD;"}
1
+ {"version":3,"file":"hooks.js","sources":["../src/hooks/auth/useAuthToken.tsx","../src/hooks/auth/useCurrentUser.tsx","../src/hooks/documentCollection/useDocuments.ts","../src/hooks/preview/usePreview.tsx"],"sourcesContent":["import {getTokenState} from '@sanity/sdk'\n\nimport {createStateSourceHook} from '../helpers/createStateSourceHook'\n\n/**\n * Hook to get the currently logged in user\n * @public\n * @returns The current user or null if not authenticated\n */\nexport const useAuthToken = createStateSourceHook(getTokenState)\n","import {getCurrentUserState} from '@sanity/sdk'\n\nimport {createStateSourceHook} from '../helpers/createStateSourceHook'\n\n/**\n * Hook to get the currently logged in user\n * @public\n * @returns The current user or null if not authenticated\n */\nexport const useCurrentUser = createStateSourceHook(getCurrentUserState)\n","import {createDocumentListStore, type DocumentHandle, type DocumentListOptions} from '@sanity/sdk'\nimport {useCallback, useEffect, useState, useSyncExternalStore} from 'react'\n\nimport {useSanityInstance} from '../context/useSanityInstance'\n\n/**\n * @public\n */\nexport interface UseDocuments {\n loadMore: () => void\n results: DocumentHandle[]\n isPending: boolean\n hasMore: boolean\n count: number\n}\n\ntype DocumentListStore = ReturnType<typeof createDocumentListStore>\ntype DocumentListState = ReturnType<DocumentListStore['getState']>['getCurrent']\nconst STABLE_EMPTY = {\n results: [],\n isPending: false,\n hasMore: false,\n count: 0,\n}\n\n/**\n * Hook to get the list of documents for specified options\n *\n * @public\n *\n * @param options - options for the document list\n * @returns result of the document list and function to load more\n */\nexport function useDocuments(options: DocumentListOptions = {}): UseDocuments {\n const instance = useSanityInstance()\n\n // NOTE: useState is used because it guaranteed to return a stable reference\n // across renders\n const [ref] = useState<{\n storeInstance: DocumentListStore | null\n getCurrent: DocumentListState\n initialOptions: DocumentListOptions\n }>(() => ({\n storeInstance: null,\n getCurrent: () => STABLE_EMPTY,\n initialOptions: options,\n }))\n\n // serialize options to ensure it only calls `setOptions` when the values\n // themselves changes (in cases where devs put config inline)\n const serializedOptions = JSON.stringify(options)\n useEffect(() => {\n ref.storeInstance?.setOptions(JSON.parse(serializedOptions))\n }, [ref, serializedOptions])\n\n const subscribe = useCallback(\n (onStoreChanged: () => void) => {\n // to match the lifecycle of `useSyncExternalState`, we create the store\n // instance after subscribe and mutate the ref to connect everything\n ref.storeInstance = createDocumentListStore(instance)\n ref.storeInstance.setOptions(ref.initialOptions)\n const state = ref.storeInstance.getState()\n ref.getCurrent = state.getCurrent\n const unsubscribe = state.subscribe(onStoreChanged)\n\n return () => {\n // unsubscribe to clean up the state subscriptions\n unsubscribe()\n // dispose of the instance\n ref.storeInstance?.dispose()\n }\n },\n [instance, ref],\n )\n\n const getSnapshot = useCallback(() => {\n return ref.getCurrent()\n }, [ref])\n\n const state = useSyncExternalStore(subscribe, getSnapshot)\n\n const loadMore = useCallback(() => {\n ref.storeInstance?.loadMore()\n }, [ref])\n\n return {loadMore, ...state}\n}\n","import {type DocumentHandle, getPreviewState, type PreviewValue, resolvePreview} from '@sanity/sdk'\nimport {useCallback, useMemo, useSyncExternalStore} from 'react'\nimport {distinctUntilChanged, EMPTY, Observable, startWith, switchMap} from 'rxjs'\n\nimport {useSanityInstance} from '../context/useSanityInstance'\n\n/**\n * @alpha\n */\nexport interface UsePreviewOptions {\n document: DocumentHandle\n ref: HTMLElement | null\n}\n\n/**\n * @alpha\n */\nexport function usePreview({\n document: {_id, _type},\n ref,\n}: UsePreviewOptions): [PreviewValue, boolean] {\n const instance = useSanityInstance()\n\n const stateSource = useMemo(\n () => getPreviewState(instance, {document: {_id, _type}}),\n [instance, _id, _type],\n )\n\n // Create subscribe function for useSyncExternalStore\n const subscribe = useCallback(\n (onStoreChanged: () => void) => {\n const subscription = new Observable<boolean>((observer) => {\n // for environments that don't have an intersection observer\n if (typeof IntersectionObserver === 'undefined') return\n\n const intersectionObserver = new IntersectionObserver(\n ([entry]) => observer.next(entry.isIntersecting),\n {rootMargin: '0px', threshold: 0},\n )\n if (ref) intersectionObserver.observe(ref)\n return () => intersectionObserver.disconnect()\n })\n .pipe(\n startWith(false),\n distinctUntilChanged(),\n switchMap((isVisible) =>\n isVisible\n ? new Observable<void>((obs) => {\n return stateSource.subscribe(() => obs.next())\n })\n : EMPTY,\n ),\n )\n .subscribe({next: onStoreChanged})\n\n return () => subscription.unsubscribe()\n },\n [stateSource, ref],\n )\n\n // Create getSnapshot function to return current state\n const getSnapshot = useCallback(() => {\n const previewTuple = stateSource.getCurrent()\n if (!previewTuple[0]) throw resolvePreview(instance, {document: {_id, _type}})\n return previewTuple as [PreviewValue, boolean]\n }, [_id, _type, instance, stateSource])\n\n return useSyncExternalStore(subscribe, getSnapshot)\n}\n"],"names":["state"],"mappings":";;;;;AASa,MAAA,eAAe,sBAAsB,aAAa,GCAlD,iBAAiB,sBAAsB,mBAAmB,GCSjE,eAAe;AAAA,EACnB,SAAS,CAAC;AAAA,EACV,WAAW;AAAA,EACX,SAAS;AAAA,EACT,OAAO;AACT;AAUgB,SAAA,aAAa,UAA+B,IAAkB;AAC5E,QAAM,WAAW,kBAAkB,GAI7B,CAAC,GAAG,IAAI,SAIX,OAAO;AAAA,IACR,eAAe;AAAA,IACf,YAAY,MAAM;AAAA,IAClB,gBAAgB;AAAA,EAChB,EAAA,GAII,oBAAoB,KAAK,UAAU,OAAO;AAChD,YAAU,MAAM;AACd,QAAI,eAAe,WAAW,KAAK,MAAM,iBAAiB,CAAC;AAAA,EAAA,GAC1D,CAAC,KAAK,iBAAiB,CAAC;AAE3B,QAAM,YAAY;AAAA,IAChB,CAAC,mBAA+B;AAG1B,UAAA,gBAAgB,wBAAwB,QAAQ,GACpD,IAAI,cAAc,WAAW,IAAI,cAAc;AACzCA,YAAAA,SAAQ,IAAI,cAAc,SAAS;AACzC,UAAI,aAAaA,OAAM;AACjB,YAAA,cAAcA,OAAM,UAAU,cAAc;AAElD,aAAO,MAAM;AAEC,uBAEZ,IAAI,eAAe,QAAQ;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,CAAC,UAAU,GAAG;AAAA,EAGV,GAAA,cAAc,YAAY,MACvB,IAAI,WAAW,GACrB,CAAC,GAAG,CAAC,GAEF,QAAQ,qBAAqB,WAAW,WAAW;AAMlD,SAAA,EAAC,UAJS,YAAY,MAAM;AACjC,QAAI,eAAe,SAAS;AAAA,KAC3B,CAAC,GAAG,CAAC,GAEU,GAAG,MAAK;AAC5B;ACrEO,SAAS,WAAW;AAAA,EACzB,UAAU,EAAC,KAAK,MAAK;AAAA,EACrB;AACF,GAA+C;AACvC,QAAA,WAAW,qBAEX,cAAc;AAAA,IAClB,MAAM,gBAAgB,UAAU,EAAC,UAAU,EAAC,KAAK,MAAK,GAAE;AAAA,IACxD,CAAC,UAAU,KAAK,KAAK;AAAA,KAIjB,YAAY;AAAA,IAChB,CAAC,mBAA+B;AAC9B,YAAM,eAAe,IAAI,WAAoB,CAAC,aAAa;AAErD,YAAA,OAAO,uBAAyB,IAAa;AAEjD,cAAM,uBAAuB,IAAI;AAAA,UAC/B,CAAC,CAAC,KAAK,MAAM,SAAS,KAAK,MAAM,cAAc;AAAA,UAC/C,EAAC,YAAY,OAAO,WAAW,EAAC;AAAA,QAClC;AACA,eAAI,OAAK,qBAAqB,QAAQ,GAAG,GAClC,MAAM,qBAAqB,WAAW;AAAA,MAC9C,CAAA,EACE;AAAA,QACC,UAAU,EAAK;AAAA,QACf,qBAAqB;AAAA,QACrB;AAAA,UAAU,CAAC,cACT,YACI,IAAI,WAAiB,CAAC,QACb,YAAY,UAAU,MAAM,IAAI,KAAK,CAAC,CAC9C,IACD;AAAA,QAAA;AAAA,MAGP,EAAA,UAAU,EAAC,MAAM,gBAAe;AAE5B,aAAA,MAAM,aAAa,YAAY;AAAA,IACxC;AAAA,IACA,CAAC,aAAa,GAAG;AAAA,EAAA,GAIb,cAAc,YAAY,MAAM;AAC9B,UAAA,eAAe,YAAY,WAAW;AAC5C,QAAI,CAAC,aAAa,CAAC,EAAS,OAAA,eAAe,UAAU,EAAC,UAAU,EAAC,KAAK,SAAO;AACtE,WAAA;AAAA,KACN,CAAC,KAAK,OAAO,UAAU,WAAW,CAAC;AAE/B,SAAA,qBAAqB,WAAW,WAAW;AACpD;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/sdk-react",
3
- "version": "0.0.0-alpha.2",
3
+ "version": "0.0.0-alpha.3",
4
4
  "private": false,
5
5
  "description": "Sanity SDK React toolkit for Content OS",
6
6
  "keywords": [
@@ -59,13 +59,6 @@
59
59
  "dist",
60
60
  "src"
61
61
  ],
62
- "lint-staged": {
63
- "!(*.{ts,tsx})": "prettier --write",
64
- "*.{ts,tsx}": [
65
- "eslint --fix",
66
- "prettier --write"
67
- ]
68
- },
69
62
  "browserslist": "extends @sanity/browserslist-config",
70
63
  "prettier": "@sanity/prettier-config",
71
64
  "dependencies": {
@@ -73,14 +66,11 @@
73
66
  "@sanity/icons": "^3.5.2",
74
67
  "@sanity/logos": "^2.1.13",
75
68
  "@sanity/ui": "^2.8.19",
76
- "@types/lodash-es": "^4.17.12",
77
69
  "inter-ui": "^4.1.0",
78
- "lodash-es": "^4.17.21",
79
70
  "react-error-boundary": "^4.1.2",
80
71
  "rollup-plugin-import-css": "^3.5.7",
81
72
  "rxjs": "^7.8.1",
82
- "zustand": "^5.0.1",
83
- "@sanity/sdk": "0.0.0-alpha.2"
73
+ "@sanity/sdk": "0.0.0-alpha.3"
84
74
  },
85
75
  "devDependencies": {
86
76
  "@sanity/client": "^6.24.1",
@@ -89,25 +79,24 @@
89
79
  "@storybook/react": "^8.4.7",
90
80
  "@testing-library/jest-dom": "^6.6.3",
91
81
  "@testing-library/react": "^16.1.0",
92
- "@types/react": "^18.3.18",
93
- "@types/react-dom": "^18.3.5",
82
+ "@types/react": "^19.0.3",
83
+ "@types/react-dom": "^19.0.2",
94
84
  "@vitejs/plugin-react": "^4.3.4",
95
85
  "@vitest/coverage-v8": "2.1.8",
96
86
  "eslint": "^9.17.0",
97
87
  "jsdom": "^25.0.1",
98
- "lint-staged": "^15.2.10",
99
88
  "prettier": "^3.4.2",
100
- "react": "^18.3.1",
101
- "react-dom": "^18.3.1",
89
+ "react": "^19.0.0",
90
+ "react-dom": "^19.0.0",
102
91
  "typescript": "^5.7.2",
92
+ "vite": "^5.4.11",
103
93
  "vitest": "^2.1.8",
104
94
  "@repo/config-eslint": "0.0.0",
105
95
  "@repo/package.config": "0.0.1"
106
96
  },
107
97
  "peerDependencies": {
108
- "react": "^18.0.0",
109
- "react-dom": "^18.0.0",
110
- "styled-components": "^6.1.13"
98
+ "react": "^18.0.0 || ^19.0.0",
99
+ "react-dom": "^18.0.0 || ^19.0.0"
111
100
  },
112
101
  "engines": {
113
102
  "node": ">=20.0.0"
@@ -2,7 +2,7 @@ import 'inter-ui/inter.css'
2
2
  import '../../css/styles.css'
3
3
 
4
4
  import {DocumentIcon} from '@sanity/icons'
5
- import {forwardRef} from 'react'
5
+ import {forwardRef, type JSX} from 'react'
6
6
 
7
7
  /**
8
8
  * @public
@@ -1,22 +1,9 @@
1
1
  import {Button, Flex, Heading, Spinner} from '@sanity/ui'
2
- import {Suspense} from 'react'
3
- import styled from 'styled-components'
2
+ import {type JSX, Suspense} from 'react'
4
3
 
5
4
  import {useLoginUrls} from '../../hooks/auth/useLoginUrls'
6
5
  import {LoginLayout, type LoginLayoutProps} from './LoginLayout'
7
6
 
8
- /**
9
- * @alpha
10
- */
11
- export interface LoginProps {
12
- header?: React.ReactNode
13
- footer?: React.ReactNode
14
- }
15
-
16
- const FallbackRoot = styled(Flex)`
17
- height: 123px;
18
- `
19
-
20
7
  /**
21
8
  * Login component that displays available authentication providers.
22
9
  * Renders a list of login options with a loading fallback while providers load.
@@ -33,9 +20,9 @@ export function Login({header, footer}: LoginLayoutProps): JSX.Element {
33
20
 
34
21
  <Suspense
35
22
  fallback={
36
- <FallbackRoot align="center" justify="center">
23
+ <Flex align="center" justify="center" style={{height: '123px'}}>
37
24
  <Spinner />
38
- </FallbackRoot>
25
+ </Flex>
39
26
  }
40
27
  >
41
28
  <Providers />
@@ -1,14 +1,10 @@
1
1
  import {Flex, Spinner, Text} from '@sanity/ui'
2
2
  import {useEffect} from 'react'
3
- import styled from 'styled-components'
4
3
 
5
4
  import {useHandleCallback} from '../../hooks/auth/useHandleCallback'
6
5
  import {LoginLayout, type LoginLayoutProps} from './LoginLayout'
7
6
 
8
- const StyledFlex = styled(Flex)`
9
- margin: auto;
10
- `
11
-
7
+ /**
12
8
  /**
13
9
  * Component shown during auth callback processing that handles login completion.
14
10
  * Automatically processes the auth callback when mounted and updates the URL
@@ -32,10 +28,10 @@ export function LoginCallback({header, footer}: LoginLayoutProps): React.ReactNo
32
28
 
33
29
  return (
34
30
  <LoginLayout header={header} footer={footer}>
35
- <StyledFlex direction="column" justify="center" align="center" gap={4}>
31
+ <Flex direction="column" justify="center" align="center" gap={4} style={{margin: 'auto'}}>
36
32
  <Text size={1}>Logging you in…</Text>
37
33
  <Spinner size={4} />
38
- </StyledFlex>
34
+ </Flex>
39
35
  </LoginLayout>
40
36
  )
41
37
  }
@@ -1,7 +1,6 @@
1
1
  import {Button, Flex, Text} from '@sanity/ui'
2
2
  import {useCallback} from 'react'
3
3
  import {type FallbackProps} from 'react-error-boundary'
4
- import styled from 'styled-components'
5
4
 
6
5
  import {useLogOut} from '../../hooks/auth/useLogOut'
7
6
  import {AuthError} from './AuthError'
@@ -12,10 +11,6 @@ import {LoginLayout, type LoginLayoutProps} from './LoginLayout'
12
11
  */
13
12
  export type LoginErrorProps = FallbackProps & LoginLayoutProps
14
13
 
15
- const StyledFlex = styled(Flex)`
16
- margin: auto;
17
- `
18
-
19
14
  /**
20
15
  * Displays authentication error details and provides retry functionality.
21
16
  * Only handles {@link AuthError} instances - rethrows other error types.
@@ -38,7 +33,7 @@ export function LoginError({
38
33
 
39
34
  return (
40
35
  <LoginLayout header={header} footer={footer}>
41
- <StyledFlex direction="column" gap={4}>
36
+ <Flex direction="column" gap={4} style={{margin: 'auto'}}>
42
37
  <Flex direction="column" gap={3}>
43
38
  <Text as="h2" align="center" weight="bold" size={3}>
44
39
  Authentication Error
@@ -48,7 +43,7 @@ export function LoginError({
48
43
  </Text>
49
44
  </Flex>
50
45
  <Button text="Retry" tone="primary" onClick={handleRetry} />
51
- </StyledFlex>
46
+ </Flex>
52
47
  </LoginLayout>
53
48
  )
54
49
  }
@@ -1,7 +1,6 @@
1
1
  import {SanityLogo} from '@sanity/logos'
2
2
  import {Flex, Text} from '@sanity/ui'
3
3
  import {Fragment} from 'react'
4
- import styled from 'styled-components'
5
4
 
6
5
  const LINKS = [
7
6
  {
@@ -26,12 +25,6 @@ const LINKS = [
26
25
  },
27
26
  ]
28
27
 
29
- const StyledText = styled(Text)`
30
- a {
31
- color: inherit;
32
- }
33
- `
34
-
35
28
  /**
36
29
  * Default footer component for login screens showing Sanity branding and legal
37
30
  * links.
@@ -48,11 +41,16 @@ export function LoginFooter(): React.ReactNode {
48
41
  <Flex align="center" gap={2}>
49
42
  {LINKS.map((link, index) => (
50
43
  <Fragment key={link.title}>
51
- <StyledText muted size={1}>
52
- <a href={link.url} target="_blank" rel="noopener noreferrer">
44
+ <Text muted size={1}>
45
+ <a
46
+ href={link.url}
47
+ target="_blank"
48
+ rel="noopener noreferrer"
49
+ style={{color: 'inherit'}}
50
+ >
53
51
  {link.title}
54
52
  </a>
55
- </StyledText>
53
+ </Text>
56
54
 
57
55
  {index < LINKS.length - 1 && (
58
56
  <Text size={1} muted>
@@ -1,5 +1,4 @@
1
1
  import {Card, Flex} from '@sanity/ui'
2
- import styled from 'styled-components'
3
2
 
4
3
  import {LoginFooter} from './LoginFooter'
5
4
 
@@ -17,23 +16,6 @@ export interface LoginLayoutProps {
17
16
  children?: React.ReactNode
18
17
  }
19
18
 
20
- const Root = styled.div`
21
- width: 100%;
22
- display: flex;
23
- `
24
-
25
- const Container = styled(Flex)`
26
- width: 320px;
27
- margin: auto;
28
- display: flex;
29
- `
30
-
31
- const StyledCard = styled(Card)``
32
-
33
- const ChildrenFlex = styled(Flex)`
34
- min-height: 154px;
35
- `
36
-
37
19
  /**
38
20
  * Layout component for login-related screens providing consistent styling and structure.
39
21
  * Renders content in a centered card with optional header and footer sections.
@@ -74,9 +56,9 @@ export function LoginLayout({
74
56
  header,
75
57
  }: LoginLayoutProps): React.ReactNode {
76
58
  return (
77
- <Root>
78
- <Container direction="column" gap={4}>
79
- <StyledCard border radius={2} paddingY={4}>
59
+ <div style={{width: '100%', display: 'flex'}}>
60
+ <Flex direction="column" gap={4} style={{width: '320px', margin: 'auto', display: 'flex'}}>
61
+ <Card border radius={2} paddingY={4}>
80
62
  <Flex direction="column" gap={4}>
81
63
  {header && (
82
64
  <Card borderBottom paddingX={4} paddingBottom={3}>
@@ -85,15 +67,15 @@ export function LoginLayout({
85
67
  )}
86
68
 
87
69
  {children && (
88
- <ChildrenFlex paddingX={4} direction="column">
70
+ <Flex paddingX={4} direction="column" style={{minHeight: '154px'}}>
89
71
  {children}
90
- </ChildrenFlex>
72
+ </Flex>
91
73
  )}
92
74
  </Flex>
93
- </StyledCard>
75
+ </Card>
94
76
 
95
77
  {footer}
96
- </Container>
97
- </Root>
78
+ </Flex>
79
+ </div>
98
80
  )
99
81
  }
@@ -1,6 +1,6 @@
1
1
  import {type DocumentHandle, getPreviewState, type PreviewValue, resolvePreview} from '@sanity/sdk'
2
2
  import {act, render, screen} from '@testing-library/react'
3
- import {Suspense, useRef} from 'react'
3
+ import {Suspense, useState} from 'react'
4
4
  import type {Mock} from 'vitest'
5
5
 
6
6
  import {usePreview} from './usePreview'
@@ -44,11 +44,11 @@ const mockDocument: DocumentHandle = {
44
44
  }
45
45
 
46
46
  function TestComponent({document}: {document: DocumentHandle}) {
47
- const ref = useRef<HTMLDivElement>(null)
47
+ const [ref, setRef] = useState<HTMLElement | null>(null)
48
48
  const [previewValue, pending] = usePreview({document, ref})
49
49
 
50
50
  return (
51
- <div ref={ref}>
51
+ <div ref={setRef}>
52
52
  <h1>{previewValue.title}</h1>
53
53
  <p>{previewValue.subtitle}</p>
54
54
  {pending && <div>Pending...</div>}
@@ -107,7 +107,7 @@ describe('usePreview', () => {
107
107
  expect(eventsUnsubscribe).toHaveBeenCalled()
108
108
  })
109
109
 
110
- test('it suspends and resolves data when element becomes visible', async () => {
110
+ test.skip('it suspends and resolves data when element becomes visible', async () => {
111
111
  // Initial setup with pending state
112
112
  getCurrent.mockReturnValueOnce([null, true])
113
113
  const resolvePromise = Promise.resolve<PreviewValue>({
@@ -1,5 +1,5 @@
1
1
  import {type DocumentHandle, getPreviewState, type PreviewValue, resolvePreview} from '@sanity/sdk'
2
- import {type RefObject, useCallback, useMemo, useSyncExternalStore} from 'react'
2
+ import {useCallback, useMemo, useSyncExternalStore} from 'react'
3
3
  import {distinctUntilChanged, EMPTY, Observable, startWith, switchMap} from 'rxjs'
4
4
 
5
5
  import {useSanityInstance} from '../context/useSanityInstance'
@@ -9,7 +9,7 @@ import {useSanityInstance} from '../context/useSanityInstance'
9
9
  */
10
10
  export interface UsePreviewOptions {
11
11
  document: DocumentHandle
12
- ref?: RefObject<HTMLElement>
12
+ ref: HTMLElement | null
13
13
  }
14
14
 
15
15
  /**
@@ -37,7 +37,7 @@ export function usePreview({
37
37
  ([entry]) => observer.next(entry.isIntersecting),
38
38
  {rootMargin: '0px', threshold: 0},
39
39
  )
40
- if (ref?.current) intersectionObserver.observe(ref.current)
40
+ if (ref) intersectionObserver.observe(ref)
41
41
  return () => intersectionObserver.disconnect()
42
42
  })
43
43
  .pipe(