From a6b7b57df35cbb4acee863b003bd04c61a571b7b Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Mon, 10 Mar 2025 08:25:54 -0700 Subject: [PATCH 01/16] feat: add directions for publishing on empty schema --- .../web/app/src/components/ui/empty-list.tsx | 54 +++++++++++++++---- .../web/app/src/components/ui/input-copy.tsx | 2 +- packages/web/app/src/pages/target-apps.tsx | 8 ++- .../src/pages/target-explorer-deprecated.tsx | 8 ++- .../app/src/pages/target-explorer-type.tsx | 8 ++- .../app/src/pages/target-explorer-unused.tsx | 8 ++- .../web/app/src/pages/target-explorer.tsx | 8 ++- packages/web/app/src/pages/target-history.tsx | 10 +++- packages/web/app/src/pages/target.tsx | 4 +- 9 files changed, 86 insertions(+), 24 deletions(-) diff --git a/packages/web/app/src/components/ui/empty-list.tsx b/packages/web/app/src/components/ui/empty-list.tsx index 4eab0c95ee..a10ce38a1b 100644 --- a/packages/web/app/src/components/ui/empty-list.tsx +++ b/packages/web/app/src/components/ui/empty-list.tsx @@ -1,24 +1,28 @@ -import { ReactElement } from 'react'; +import { ReactElement, ReactNode } from 'react'; import magnifier from '../../../public/images/figures/magnifier.svg?url'; +import { ProjectType } from '@/gql/graphql'; import { cn } from '@/lib/utils'; import { Card } from './card'; import { DocsLink } from './docs-note'; import { Heading } from './heading'; +import { InputCopy } from './input-copy'; export const EmptyList = ({ title, description, docsUrl, className, + children, }: { title: string; description: string; docsUrl?: string | null; + children?: ReactNode | null; className?: string; }): ReactElement => { return ( {title} {description} + {children} {docsUrl && Read about it in the documentation} ); @@ -43,13 +48,44 @@ export const noSchema = ( /> ); -export const noSchemaVersion = ( - -); +// @todo consider monolith vs distributed etc +export const NoSchemaVersion = ({ + projectType, +}: { + projectType: ProjectType | null; +}): ReactElement => { + const isDistributed = + projectType === ProjectType.Federation || projectType === ProjectType.Stitching; + return ( + + <> +
+ First check that the schema is valid and compatible with the state of the registry. +
+
+ --url ' : ''}`} + alignment="center" + /> +
+
+ Then publish the schema. For distributed systems, it's recommended to publish the schema + after the service is deployed. +
+
+ --url ' : ''}`} + alignment="center" + /> +
+ +
+ ); +}; export const noValidSchemaVersion = ( ) : !data.data?.target?.latestSchemaVersion ? ( - noSchemaVersion + ) : !data.data.target.appDeployments ? ( ) : ( - noSchemaVersion + )} )} diff --git a/packages/web/app/src/pages/target-explorer-type.tsx b/packages/web/app/src/pages/target-explorer-type.tsx index 6f0e060c6d..3b24a3f0e2 100644 --- a/packages/web/app/src/pages/target-explorer-type.tsx +++ b/packages/web/app/src/pages/target-explorer-type.tsx @@ -19,7 +19,7 @@ import { } from '@/components/target/explorer/provider'; import { GraphQLScalarTypeComponent } from '@/components/target/explorer/scalar-type'; import { GraphQLUnionTypeComponent } from '@/components/target/explorer/union-type'; -import { noSchemaVersion } from '@/components/ui/empty-list'; +import { NoSchemaVersion } from '@/components/ui/empty-list'; import { Meta } from '@/components/ui/meta'; import { Subtitle, Title } from '@/components/ui/page'; import { QueryError } from '@/components/ui/query-error'; @@ -162,6 +162,10 @@ const TargetExplorerTypenamePageQuery = graphql(` } } } + project { + id + type + } } operationsStats( selector: { @@ -263,7 +267,7 @@ function TypeExplorerPageContent(props: { styleDeprecated /> ) : type ? ( - noSchemaVersion + ) : (
Not found
)} diff --git a/packages/web/app/src/pages/target-explorer-unused.tsx b/packages/web/app/src/pages/target-explorer-unused.tsx index 07f66d29d7..ea71863ffd 100644 --- a/packages/web/app/src/pages/target-explorer-unused.tsx +++ b/packages/web/app/src/pages/target-explorer-unused.tsx @@ -6,7 +6,7 @@ import { SchemaVariantFilter } from '@/components/target/explorer/filter'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Button } from '@/components/ui/button'; import { DateRangePicker, presetLast7Days } from '@/components/ui/date-range-picker'; -import { EmptyList, noSchemaVersion } from '@/components/ui/empty-list'; +import { EmptyList, NoSchemaVersion } from '@/components/ui/empty-list'; import { Link } from '@/components/ui/link'; import { Meta } from '@/components/ui/meta'; import { Subtitle, Title } from '@/components/ui/page'; @@ -237,6 +237,10 @@ const UnusedSchemaExplorer_UnusedSchemaQuery = graphql(` ...UnusedSchemaView_UnusedSchemaExplorerFragment } } + project { + id + type + } } operationsStats( selector: { @@ -355,7 +359,7 @@ function UnusedSchemaExplorer(props: { /> ) : ( - noSchemaVersion + )} )} diff --git a/packages/web/app/src/pages/target-explorer.tsx b/packages/web/app/src/pages/target-explorer.tsx index 9e6b1cabc0..30c4622e5a 100644 --- a/packages/web/app/src/pages/target-explorer.tsx +++ b/packages/web/app/src/pages/target-explorer.tsx @@ -16,7 +16,7 @@ import { useSchemaExplorerContext, } from '@/components/target/explorer/provider'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; -import { noSchemaVersion, noValidSchemaVersion } from '@/components/ui/empty-list'; +import { NoSchemaVersion, noValidSchemaVersion } from '@/components/ui/empty-list'; import { Meta } from '@/components/ui/meta'; import { Subtitle, Title } from '@/components/ui/page'; import { QueryError } from '@/components/ui/query-error'; @@ -135,6 +135,10 @@ const TargetExplorerPageQuery = graphql(` ...ExplorerPage_SchemaExplorerFragment } } + project { + id + type + } } operationsStats( selector: { @@ -269,7 +273,7 @@ function ExplorerPageContent(props: { ) : latestSchemaVersion ? ( noValidSchemaVersion ) : ( - noSchemaVersion + )} )} diff --git a/packages/web/app/src/pages/target-history.tsx b/packages/web/app/src/pages/target-history.tsx index bca7b936ed..fc3b38a0ea 100644 --- a/packages/web/app/src/pages/target-history.tsx +++ b/packages/web/app/src/pages/target-history.tsx @@ -3,7 +3,7 @@ import { useQuery } from 'urql'; import { Page, TargetLayout } from '@/components/layouts/target'; import { BadgeRounded } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; -import { noSchemaVersion } from '@/components/ui/empty-list'; +import { NoSchemaVersion } from '@/components/ui/empty-list'; import { Meta } from '@/components/ui/meta'; import { Subtitle, Title } from '@/components/ui/page'; import { QueryError } from '@/components/ui/query-error'; @@ -176,6 +176,10 @@ const TargetHistoryPageQuery = graphql(` targetSlug: $targetSlug } ) { + project { + id + type + } id latestSchemaVersion { id @@ -268,7 +272,9 @@ function HistoryPageContent(props: { Versions Recently published schemas. - {query.fetching ? null : noSchemaVersion} + {query.fetching ? null : ( + + )} ); } diff --git a/packages/web/app/src/pages/target.tsx b/packages/web/app/src/pages/target.tsx index b92b055a38..66d5c23841 100644 --- a/packages/web/app/src/pages/target.tsx +++ b/packages/web/app/src/pages/target.tsx @@ -10,7 +10,7 @@ import { CommandInput, CommandItem, } from '@/components/ui/command'; -import { EmptyList, noSchema, noSchemaVersion } from '@/components/ui/empty-list'; +import { EmptyList, noSchema, NoSchemaVersion } from '@/components/ui/empty-list'; import { Meta } from '@/components/ui/meta'; import { Subtitle, Title } from '@/components/ui/page'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; @@ -169,7 +169,7 @@ function SchemaView(props: { const { latestSchemaVersion } = target; if (!latestSchemaVersion) { - return noSchemaVersion; + return ; } if (!latestSchemaVersion.schemas.nodes.length) { From 2fe28834b64e1afd7b4de4542ced3e6255a4ae66 Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Mon, 10 Mar 2025 21:39:04 -0700 Subject: [PATCH 02/16] Changeset --- .changeset/fuzzy-plums-repeat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fuzzy-plums-repeat.md diff --git a/.changeset/fuzzy-plums-repeat.md b/.changeset/fuzzy-plums-repeat.md new file mode 100644 index 0000000000..bec4ce73b3 --- /dev/null +++ b/.changeset/fuzzy-plums-repeat.md @@ -0,0 +1,5 @@ +--- +'hive': patch +--- + +Added directions for publishing on no schema component From 969ad58cfd8102988c0e86b9176aab3433a65d10 Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Mon, 10 Mar 2025 21:44:34 -0700 Subject: [PATCH 03/16] Fix type --- packages/web/app/src/pages/target-apps.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/app/src/pages/target-apps.tsx b/packages/web/app/src/pages/target-apps.tsx index 0ed802abd4..0c1cd48066 100644 --- a/packages/web/app/src/pages/target-apps.tsx +++ b/packages/web/app/src/pages/target-apps.tsx @@ -258,7 +258,7 @@ function TargetAppsView(props: { ) : !data.data?.target?.latestSchemaVersion ? ( - + ) : !data.data.target.appDeployments ? ( Date: Tue, 11 Mar 2025 16:43:45 +0800 Subject: [PATCH 04/16] feat(app): organization access token management ui (#6556) --- .changeset/neat-ladybugs-pay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/neat-ladybugs-pay.md b/.changeset/neat-ladybugs-pay.md index cf066fa5d1..79e2811137 100644 --- a/.changeset/neat-ladybugs-pay.md +++ b/.changeset/neat-ladybugs-pay.md @@ -1,5 +1,5 @@ --- -'hive': major +'hive': minor --- Add organization access tokens; a new way to issue access tokens for performing actions with the CLI From b49e5e47184f220e99fb2c0549c14a45f367126d Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Mon, 10 Mar 2025 08:25:54 -0700 Subject: [PATCH 05/16] feat: add directions for publishing on empty schema --- packages/web/app/src/components/ui/input-copy.tsx | 2 +- packages/web/app/src/pages/target-apps.tsx | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/web/app/src/components/ui/input-copy.tsx b/packages/web/app/src/components/ui/input-copy.tsx index 47a6615d55..b98bb12e32 100644 --- a/packages/web/app/src/components/ui/input-copy.tsx +++ b/packages/web/app/src/components/ui/input-copy.tsx @@ -31,7 +31,7 @@ export function InputCopy(props: { value: string; alignment?: 'center' | 'left' type="text" value={props.value} readOnly - className="bg-secondary truncate text-white" + className={`bg-secondary truncate text-white${props.alignment === 'center' ? 'text-center' : ''}`} onFocus={ev => ev.target.select()} /> diff --git a/packages/web/app/src/pages/target-apps.tsx b/packages/web/app/src/pages/target-apps.tsx index 0c1cd48066..b3f3a842ad 100644 --- a/packages/web/app/src/pages/target-apps.tsx +++ b/packages/web/app/src/pages/target-apps.tsx @@ -258,7 +258,11 @@ function TargetAppsView(props: { ) : !data.data?.target?.latestSchemaVersion ? ( +<<<<<<< HEAD +======= + +>>>>>>> b4163643d (feat: add directions for publishing on empty schema) ) : !data.data.target.appDeployments ? ( Date: Mon, 10 Mar 2025 21:44:34 -0700 Subject: [PATCH 06/16] Fix type --- packages/web/app/src/pages/target-apps.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/web/app/src/pages/target-apps.tsx b/packages/web/app/src/pages/target-apps.tsx index b3f3a842ad..0c1cd48066 100644 --- a/packages/web/app/src/pages/target-apps.tsx +++ b/packages/web/app/src/pages/target-apps.tsx @@ -258,11 +258,7 @@ function TargetAppsView(props: { ) : !data.data?.target?.latestSchemaVersion ? ( -<<<<<<< HEAD -======= - ->>>>>>> b4163643d (feat: add directions for publishing on empty schema) ) : !data.data.target.appDeployments ? ( Date: Tue, 11 Mar 2025 09:11:02 -0700 Subject: [PATCH 07/16] Update packages/web/app/src/components/ui/input-copy.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- packages/web/app/src/components/ui/input-copy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/app/src/components/ui/input-copy.tsx b/packages/web/app/src/components/ui/input-copy.tsx index b98bb12e32..216b81f1ec 100644 --- a/packages/web/app/src/components/ui/input-copy.tsx +++ b/packages/web/app/src/components/ui/input-copy.tsx @@ -31,7 +31,7 @@ export function InputCopy(props: { value: string; alignment?: 'center' | 'left' type="text" value={props.value} readOnly - className={`bg-secondary truncate text-white${props.alignment === 'center' ? 'text-center' : ''}`} + className={`bg-secondary truncate text-white ${props.alignment === 'center' ? 'text-center' : ''}`} onFocus={ev => ev.target.select()} /> From c2add1fb30f1b42804749970bc5cd70233c40608 Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:16:11 -0700 Subject: [PATCH 08/16] Separate check noschema from others; adjust ui per feedback --- .../web/app/src/components/ui/empty-list.tsx | 68 ++++++++++++------- .../web/app/src/components/ui/input-copy.tsx | 4 +- packages/web/app/src/pages/target-apps.tsx | 5 +- packages/web/app/src/pages/target-checks.tsx | 15 +++- .../src/pages/target-explorer-deprecated.tsx | 5 +- .../app/src/pages/target-explorer-type.tsx | 5 +- .../app/src/pages/target-explorer-unused.tsx | 5 +- .../web/app/src/pages/target-explorer.tsx | 5 +- packages/web/app/src/pages/target-history.tsx | 5 +- packages/web/app/src/pages/target.tsx | 2 +- 10 files changed, 83 insertions(+), 36 deletions(-) diff --git a/packages/web/app/src/components/ui/empty-list.tsx b/packages/web/app/src/components/ui/empty-list.tsx index a10ce38a1b..d8d17fcb3a 100644 --- a/packages/web/app/src/components/ui/empty-list.tsx +++ b/packages/web/app/src/components/ui/empty-list.tsx @@ -2,6 +2,7 @@ import { ReactElement, ReactNode } from 'react'; import magnifier from '../../../public/images/figures/magnifier.svg?url'; import { ProjectType } from '@/gql/graphql'; import { cn } from '@/lib/utils'; +import { InlineCode } from '../v2/inline-code'; import { Card } from './card'; import { DocsLink } from './docs-note'; import { Heading } from './heading'; @@ -48,41 +49,58 @@ export const noSchema = ( /> ); -// @todo consider monolith vs distributed etc export const NoSchemaVersion = ({ - projectType, + projectType = null, + recommendedAction = 'none', }: { projectType: ProjectType | null; + recommendedAction: 'publish' | 'check' | 'none'; }): ReactElement => { - const isDistributed = - projectType === ProjectType.Federation || projectType === ProjectType.Stitching; + let children: ReactElement | null = null; + if (recommendedAction !== 'none') { + const isDistributed = + projectType === ProjectType.Federation || projectType === ProjectType.Stitching; + + if (recommendedAction === 'check') { + children = ( + <> +
+ It's recommended to check that the schema is valid and compatible with the state of the + registry before publishing. +
+
+ --url ' : ''}`} + /> +
+ + ); + } else if (recommendedAction === 'publish') { + children = ( + <> + {isDistributed && ( +
+ For distributed systems, it's recommended to publish the schema after the service is + deployed. +
+ )} +
+ --url ' : ''}`} + /> +
+ + ); + } + } + return ( - <> -
- First check that the schema is valid and compatible with the state of the registry. -
-
- --url ' : ''}`} - alignment="center" - /> -
-
- Then publish the schema. For distributed systems, it's recommended to publish the schema - after the service is deployed. -
-
- --url ' : ''}`} - alignment="center" - /> -
- + {children}
); }; diff --git a/packages/web/app/src/components/ui/input-copy.tsx b/packages/web/app/src/components/ui/input-copy.tsx index 216b81f1ec..d9a9288c24 100644 --- a/packages/web/app/src/components/ui/input-copy.tsx +++ b/packages/web/app/src/components/ui/input-copy.tsx @@ -4,7 +4,7 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { useClipboard } from '@/lib/hooks'; -export function InputCopy(props: { value: string; alignment?: 'center' | 'left' }) { +export function InputCopy(props: { value: string }) { const [isCopied, setIsCopied] = useState(false); const copyToClipboard = useClipboard(); @@ -31,7 +31,7 @@ export function InputCopy(props: { value: string; alignment?: 'center' | 'left' type="text" value={props.value} readOnly - className={`bg-secondary truncate text-white ${props.alignment === 'center' ? 'text-center' : ''}`} + className="bg-secondary truncate text-white" onFocus={ev => ev.target.select()} /> diff --git a/packages/web/app/src/pages/target-apps.tsx b/packages/web/app/src/pages/target-apps.tsx index 0c1cd48066..40cfc5b712 100644 --- a/packages/web/app/src/pages/target-apps.tsx +++ b/packages/web/app/src/pages/target-apps.tsx @@ -258,7 +258,10 @@ function TargetAppsView(props: { ) : !data.data?.target?.latestSchemaVersion ? ( - + ) : !data.data.target.appDeployments ? (
- {hasActiveSchemaCheck ? 'List is empty' : 'Your schema check list is empty'} + {hasActiveSchemaCheck ? ( + 'List is empty' + ) : ( + + )}
{hasActiveSchemaCheck diff --git a/packages/web/app/src/pages/target-explorer-deprecated.tsx b/packages/web/app/src/pages/target-explorer-deprecated.tsx index 4b27a3cf02..c8ea236049 100644 --- a/packages/web/app/src/pages/target-explorer-deprecated.tsx +++ b/packages/web/app/src/pages/target-explorer-deprecated.tsx @@ -307,7 +307,10 @@ function DeprecatedSchemaExplorer(props: { /> ) : ( - + )} )} diff --git a/packages/web/app/src/pages/target-explorer-type.tsx b/packages/web/app/src/pages/target-explorer-type.tsx index 3b24a3f0e2..7e7343c4cd 100644 --- a/packages/web/app/src/pages/target-explorer-type.tsx +++ b/packages/web/app/src/pages/target-explorer-type.tsx @@ -267,7 +267,10 @@ function TypeExplorerPageContent(props: { styleDeprecated /> ) : type ? ( - + ) : (
Not found
)} diff --git a/packages/web/app/src/pages/target-explorer-unused.tsx b/packages/web/app/src/pages/target-explorer-unused.tsx index ea71863ffd..f75ff1caf0 100644 --- a/packages/web/app/src/pages/target-explorer-unused.tsx +++ b/packages/web/app/src/pages/target-explorer-unused.tsx @@ -359,7 +359,10 @@ function UnusedSchemaExplorer(props: { /> ) : ( - + )} )} diff --git a/packages/web/app/src/pages/target-explorer.tsx b/packages/web/app/src/pages/target-explorer.tsx index 30c4622e5a..857a1b0351 100644 --- a/packages/web/app/src/pages/target-explorer.tsx +++ b/packages/web/app/src/pages/target-explorer.tsx @@ -273,7 +273,10 @@ function ExplorerPageContent(props: { ) : latestSchemaVersion ? ( noValidSchemaVersion ) : ( - + )} )} diff --git a/packages/web/app/src/pages/target-history.tsx b/packages/web/app/src/pages/target-history.tsx index fc3b38a0ea..90e71a6d0c 100644 --- a/packages/web/app/src/pages/target-history.tsx +++ b/packages/web/app/src/pages/target-history.tsx @@ -273,7 +273,10 @@ function HistoryPageContent(props: { Recently published schemas. {query.fetching ? null : ( - + )} ); diff --git a/packages/web/app/src/pages/target.tsx b/packages/web/app/src/pages/target.tsx index 66d5c23841..b43a3fb6c1 100644 --- a/packages/web/app/src/pages/target.tsx +++ b/packages/web/app/src/pages/target.tsx @@ -169,7 +169,7 @@ function SchemaView(props: { const { latestSchemaVersion } = target; if (!latestSchemaVersion) { - return ; + return ; } if (!latestSchemaVersion.schemas.nodes.length) { From 57b4608051742b8ce9f0276ca8d796c1e04aa087 Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:24:31 -0700 Subject: [PATCH 09/16] Revert changeset --- .changeset/neat-ladybugs-pay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/neat-ladybugs-pay.md b/.changeset/neat-ladybugs-pay.md index 79e2811137..cf066fa5d1 100644 --- a/.changeset/neat-ladybugs-pay.md +++ b/.changeset/neat-ladybugs-pay.md @@ -1,5 +1,5 @@ --- -'hive': minor +'hive': major --- Add organization access tokens; a new way to issue access tokens for performing actions with the CLI From 60c2aa7b39326db77a5e9a623ef121b65e2853bc Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:27:04 -0700 Subject: [PATCH 10/16] Fix typecheck --- packages/web/app/src/components/ui/empty-list.tsx | 1 - packages/web/app/src/pages/target-explorer-unused.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/web/app/src/components/ui/empty-list.tsx b/packages/web/app/src/components/ui/empty-list.tsx index d8d17fcb3a..f1a5037954 100644 --- a/packages/web/app/src/components/ui/empty-list.tsx +++ b/packages/web/app/src/components/ui/empty-list.tsx @@ -6,7 +6,6 @@ import { InlineCode } from '../v2/inline-code'; import { Card } from './card'; import { DocsLink } from './docs-note'; import { Heading } from './heading'; -import { InputCopy } from './input-copy'; export const EmptyList = ({ title, diff --git a/packages/web/app/src/pages/target-explorer-unused.tsx b/packages/web/app/src/pages/target-explorer-unused.tsx index f75ff1caf0..37d5eafce5 100644 --- a/packages/web/app/src/pages/target-explorer-unused.tsx +++ b/packages/web/app/src/pages/target-explorer-unused.tsx @@ -361,7 +361,7 @@ function UnusedSchemaExplorer(props: { ) : ( )} From eb353a7b7d6f423c492b0caa0bc72b9460a36720 Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Wed, 12 Mar 2025 23:01:47 -0700 Subject: [PATCH 11/16] implement new code component --- packages/web/app/src/components/ui/code.tsx | 46 +++++++++++++++++++ .../web/app/src/components/ui/empty-list.tsx | 14 +++--- packages/web/app/src/components/ui/icon.tsx | 6 +-- packages/web/app/src/lib/hooks/use-hover.ts | 39 ++++++++++++++++ packages/web/app/src/lib/hooks/use-timed.ts | 14 ++++++ 5 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 packages/web/app/src/components/ui/code.tsx create mode 100644 packages/web/app/src/lib/hooks/use-hover.ts create mode 100644 packages/web/app/src/lib/hooks/use-timed.ts diff --git a/packages/web/app/src/components/ui/code.tsx b/packages/web/app/src/components/ui/code.tsx new file mode 100644 index 0000000000..16bd4cb497 --- /dev/null +++ b/packages/web/app/src/components/ui/code.tsx @@ -0,0 +1,46 @@ +import { CheckIcon, CopyIcon } from './icon'; +import cn from 'clsx' +import type { ComponentProps, FC } from 'react' +import { useHover } from '@/lib/hooks/use-hover'; +import { useTimed } from '@/lib/hooks/use-timed'; + +export const Code: FC< + ComponentProps<'code'> +> = ({ children, className, ...props }) => { + const [copied, startCopyTimer] = useTimed(1500); + const [ref, hovering] = useHover(); + return ( + + + {children} + + + + ) +} diff --git a/packages/web/app/src/components/ui/empty-list.tsx b/packages/web/app/src/components/ui/empty-list.tsx index f1a5037954..f4fbe6eb6f 100644 --- a/packages/web/app/src/components/ui/empty-list.tsx +++ b/packages/web/app/src/components/ui/empty-list.tsx @@ -2,10 +2,10 @@ import { ReactElement, ReactNode } from 'react'; import magnifier from '../../../public/images/figures/magnifier.svg?url'; import { ProjectType } from '@/gql/graphql'; import { cn } from '@/lib/utils'; -import { InlineCode } from '../v2/inline-code'; import { Card } from './card'; import { DocsLink } from './docs-note'; import { Heading } from './heading'; +import { Code } from './code'; export const EmptyList = ({ title, @@ -68,9 +68,9 @@ export const NoSchemaVersion = ({ registry before publishing.
- --url ' : ''}`} - /> + + {`hive schema:check ${isDistributed ? '--service --url ' : ''}`} +
); @@ -84,9 +84,9 @@ export const NoSchemaVersion = ({ )}
- --url ' : ''}`} - /> + + {`hive schema:publish ${isDistributed ? '--service --url ' : ''}`} +
); diff --git a/packages/web/app/src/components/ui/icon.tsx b/packages/web/app/src/components/ui/icon.tsx index 7cd76d6302..b94ad81f4a 100644 --- a/packages/web/app/src/components/ui/icon.tsx +++ b/packages/web/app/src/components/ui/icon.tsx @@ -231,11 +231,11 @@ export const ArrowDownIcon = ({ className }: IconProps): ReactElement => ( ); -export const CheckIcon = ({ className }: IconProps): ReactElement => ( +export const CheckIcon = ({ className, size }: IconProps & { size?: number }): ReactElement => ( (null); + + const handleMouseEnter = useCallback(() => { + setHovering(true); + }, []); + + const handleMouseLeave = useCallback(() => { + setHovering(false); + }, []); + + const customRef = useCallback( + (node: HTMLElement) => { + if (previousNode.current?.nodeType === Node.ELEMENT_NODE) { + previousNode.current.removeEventListener( + 'mouseenter', + handleMouseEnter + ); + previousNode.current.removeEventListener( + 'mouseleave', + handleMouseLeave + ); + } + + if (node?.nodeType === Node.ELEMENT_NODE) { + node.addEventListener('mouseenter', handleMouseEnter); + node.addEventListener('mouseleave', handleMouseLeave); + } + + previousNode.current = node; + }, + [handleMouseEnter, handleMouseLeave] + ); + + return [customRef, hovering] as const; +} diff --git a/packages/web/app/src/lib/hooks/use-timed.ts b/packages/web/app/src/lib/hooks/use-timed.ts new file mode 100644 index 0000000000..be0cb4c9d0 --- /dev/null +++ b/packages/web/app/src/lib/hooks/use-timed.ts @@ -0,0 +1,14 @@ +import { useState } from 'react'; + +export function useTimed(wait: number = 1000) { + const [timer, setTimer] = useState(null); + const handler = () => { + if (timer) { + clearTimeout(timer); + } + setTimer(setTimeout(() => { + setTimer(null) + }, wait)); + } + return [timer !== null, handler] as const; +} From 9fb922c71c333c4169b6302e6ecde8fc0abad1a1 Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Wed, 12 Mar 2025 23:07:23 -0700 Subject: [PATCH 12/16] Prettier --- packages/web/app/src/components/ui/code.tsx | 26 +++++++------------ .../web/app/src/components/ui/empty-list.tsx | 4 +-- packages/web/app/src/lib/hooks/use-hover.ts | 14 +++------- packages/web/app/src/lib/hooks/use-timed.ts | 12 +++++---- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/packages/web/app/src/components/ui/code.tsx b/packages/web/app/src/components/ui/code.tsx index 16bd4cb497..c1ca4a55a3 100644 --- a/packages/web/app/src/components/ui/code.tsx +++ b/packages/web/app/src/components/ui/code.tsx @@ -1,25 +1,19 @@ -import { CheckIcon, CopyIcon } from './icon'; -import cn from 'clsx' -import type { ComponentProps, FC } from 'react' +import type { ComponentProps, FC } from 'react'; +import cn from 'clsx'; import { useHover } from '@/lib/hooks/use-hover'; import { useTimed } from '@/lib/hooks/use-timed'; +import { CheckIcon, CopyIcon } from './icon'; -export const Code: FC< - ComponentProps<'code'> -> = ({ children, className, ...props }) => { +export const Code: FC> = ({ children, className, ...props }) => { const [copied, startCopyTimer] = useTimed(1500); const [ref, hovering] = useHover(); return ( - ) -} + ); +}; diff --git a/packages/web/app/src/components/ui/empty-list.tsx b/packages/web/app/src/components/ui/empty-list.tsx index f4fbe6eb6f..6bc76c69fa 100644 --- a/packages/web/app/src/components/ui/empty-list.tsx +++ b/packages/web/app/src/components/ui/empty-list.tsx @@ -3,9 +3,9 @@ import magnifier from '../../../public/images/figures/magnifier.svg?url'; import { ProjectType } from '@/gql/graphql'; import { cn } from '@/lib/utils'; import { Card } from './card'; +import { Code } from './code'; import { DocsLink } from './docs-note'; import { Heading } from './heading'; -import { Code } from './code'; export const EmptyList = ({ title, @@ -85,7 +85,7 @@ export const NoSchemaVersion = ({ )}
- {`hive schema:publish ${isDistributed ? '--service --url ' : ''}`} + {`hive schema:publish ${isDistributed ? '--service --url ' : ''}`}
diff --git a/packages/web/app/src/lib/hooks/use-hover.ts b/packages/web/app/src/lib/hooks/use-hover.ts index 93b3741d9b..65026742e8 100644 --- a/packages/web/app/src/lib/hooks/use-hover.ts +++ b/packages/web/app/src/lib/hooks/use-hover.ts @@ -1,4 +1,4 @@ -import { useState, useCallback, useRef } from 'react'; +import { useCallback, useRef, useState } from 'react'; export function useHover() { const [hovering, setHovering] = useState(false); @@ -15,14 +15,8 @@ export function useHover() { const customRef = useCallback( (node: HTMLElement) => { if (previousNode.current?.nodeType === Node.ELEMENT_NODE) { - previousNode.current.removeEventListener( - 'mouseenter', - handleMouseEnter - ); - previousNode.current.removeEventListener( - 'mouseleave', - handleMouseLeave - ); + previousNode.current.removeEventListener('mouseenter', handleMouseEnter); + previousNode.current.removeEventListener('mouseleave', handleMouseLeave); } if (node?.nodeType === Node.ELEMENT_NODE) { @@ -32,7 +26,7 @@ export function useHover() { previousNode.current = node; }, - [handleMouseEnter, handleMouseLeave] + [handleMouseEnter, handleMouseLeave], ); return [customRef, hovering] as const; diff --git a/packages/web/app/src/lib/hooks/use-timed.ts b/packages/web/app/src/lib/hooks/use-timed.ts index be0cb4c9d0..a7336441fb 100644 --- a/packages/web/app/src/lib/hooks/use-timed.ts +++ b/packages/web/app/src/lib/hooks/use-timed.ts @@ -4,11 +4,13 @@ export function useTimed(wait: number = 1000) { const [timer, setTimer] = useState(null); const handler = () => { if (timer) { - clearTimeout(timer); + clearTimeout(timer); } - setTimer(setTimeout(() => { - setTimer(null) - }, wait)); - } + setTimer( + setTimeout(() => { + setTimer(null); + }, wait), + ); + }; return [timer !== null, handler] as const; } From d3b3d3dcee87d9e1dd62986e1b5045d4d3612f28 Mon Sep 17 00:00:00 2001 From: jdolle <1841898+jdolle@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:29:20 -0700 Subject: [PATCH 13/16] More intuitive selection and clipboard support --- packages/web/app/src/components/ui/code.tsx | 26 +++++++++++++++++---- packages/web/app/src/lib/hooks/use-timed.ts | 10 +++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/web/app/src/components/ui/code.tsx b/packages/web/app/src/components/ui/code.tsx index c1ca4a55a3..175208ce1e 100644 --- a/packages/web/app/src/components/ui/code.tsx +++ b/packages/web/app/src/components/ui/code.tsx @@ -1,4 +1,4 @@ -import type { ComponentProps, FC } from 'react'; +import { useRef, type ComponentProps, type FC } from 'react'; import cn from 'clsx'; import { useHover } from '@/lib/hooks/use-hover'; import { useTimed } from '@/lib/hooks/use-timed'; @@ -7,13 +7,30 @@ import { CheckIcon, CopyIcon } from './icon'; export const Code: FC> = ({ children, className, ...props }) => { const [copied, startCopyTimer] = useTimed(1500); const [ref, hovering] = useHover(); + const codeRef = useRef(null) + // in case this browser does not support this newer API... + const navigatorClipboardSupport = typeof navigator.clipboard?.writeText === 'function'; return ( { + if (codeRef.current) { + const selection = window.getSelection(); + const range = document.createRange(); + range.setStart(codeRef.current, 0); + range.setEnd(codeRef.current, codeRef.current.childNodes.length); + selection?.removeAllRanges(); + selection?.addRange(range); + } + }} > > = ({ children, className, ...props {children}