From 6c8726b58c2020c8e3b61a96bbe6ab6304ff0985 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 9 Mar 2026 10:22:57 +1100 Subject: [PATCH] fix: performance improvements (#2581) --- .../embed/authoring/configure-fields-view.tsx | 4 +- .../embed-direct-template-client-page.tsx | 4 +- .../embed/embed-document-signing-page-v1.tsx | 4 +- .../multi-sign-document-signing-view.tsx | 68 ++++++++++--------- .../direct-template/direct-template-page.tsx | 4 +- .../document-signing-page-view-v1.tsx | 4 +- .../document/document-certificate-qr-view.tsx | 12 ++-- .../general/document/document-edit-form.tsx | 4 +- .../envelope-editor-fields-page.tsx | 7 +- .../envelope-editor-preview-page.tsx | 46 +++++++++---- .../envelope-file-selector.tsx | 7 +- .../pdf-viewer/envelope-pdf-viewer.tsx | 6 +- .../general/pdf-viewer/pdf-viewer-lazy.tsx | 34 ++++++++++ .../pdf-viewer/pdf-viewer-page-image.tsx | 9 ++- .../general/pdf-viewer/pdf-viewer.tsx | 54 +++++++++++---- .../general/template/template-edit-form.tsx | 4 +- .../t.$teamUrl+/documents.$id._index.tsx | 4 +- .../t.$teamUrl+/documents.$id._layout.tsx | 5 ++ .../t.$teamUrl+/templates.$id._index.tsx | 4 +- .../t.$teamUrl+/templates.$id._layout.tsx | 12 +++- .../routes/embed+/v2+/authoring+/_layout.tsx | 2 + .../v2+/authoring+/envelope.create._index.tsx | 2 + .../v2+/authoring+/envelope.edit.$id.tsx | 6 +- .../api/files/routes/get-envelope-item-pdf.ts | 8 +++ apps/remix/vite.config.ts | 1 + .../e2e/api/v1/template-field-prefill.spec.ts | 4 +- .../e2e/api/v2/template-field-prefill.spec.ts | 4 +- .../field/envelope-field-tooltip.tsx | 5 +- .../ui/components/field/field-tooltip.tsx | 2 +- packages/ui/styles/theme.css | 9 +++ 30 files changed, 240 insertions(+), 99 deletions(-) create mode 100644 apps/remix/app/components/general/pdf-viewer/pdf-viewer-lazy.tsx diff --git a/apps/remix/app/components/embed/authoring/configure-fields-view.tsx b/apps/remix/app/components/embed/authoring/configure-fields-view.tsx index f651cd981..25a3448d7 100644 --- a/apps/remix/app/components/embed/authoring/configure-fields-view.tsx +++ b/apps/remix/app/components/embed/authoring/configure-fields-view.tsx @@ -29,7 +29,7 @@ import { Sheet, SheetContent, SheetTrigger } from '@documenso/ui/primitives/shee import { useToast } from '@documenso/ui/primitives/use-toast'; import { FieldAdvancedSettingsDrawer } from '~/components/embed/authoring/field-advanced-settings-drawer'; -import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer'; +import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy'; import type { TConfigureEmbedFormSchema } from './configure-document-view.types'; import type { TConfigureFieldsFormSchema } from './configure-fields-view.types'; @@ -549,7 +549,7 @@ export const ConfigureFieldsView = ({
{normalizedDocumentData && ( - + )} diff --git a/apps/remix/app/components/embed/embed-direct-template-client-page.tsx b/apps/remix/app/components/embed/embed-direct-template-client-page.tsx index 0add3413d..8d86d6944 100644 --- a/apps/remix/app/components/embed/embed-direct-template-client-page.tsx +++ b/apps/remix/app/components/embed/embed-direct-template-client-page.tsx @@ -41,7 +41,7 @@ import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signa import { useToast } from '@documenso/ui/primitives/use-toast'; import { BrandingLogo } from '~/components/general/branding-logo'; -import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer'; +import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy'; import { injectCss } from '~/utils/css-vars'; import type { DirectTemplateLocalField } from '../general/direct-template/direct-template-signing-form'; @@ -340,7 +340,7 @@ export const EmbedDirectTemplateClientPage = ({
{/* Viewer */}
- {/* Viewer */}
- -
+
+
{match({ isLoading, document }) .with({ isLoading: true }, () => (
@@ -229,7 +229,7 @@ export const MultiSignDocumentSigningView = ({ 'md:mx-auto md:max-w-2xl': document.status === DocumentStatus.COMPLETED, })} > -
)} + + {hasDocumentLoaded && showPendingFieldTooltip && pendingFields.length > 0 && ( + + + Click to insert field + + + )} + + {/* Fields */} + {hasDocumentLoaded && ( + + )} + + {/* Completed fields */} + {document.status !== DocumentStatus.COMPLETED && ( + + )}
- - {hasDocumentLoaded && showPendingFieldTooltip && pendingFields.length > 0 && ( - - - Click to insert field - - - )} - - {/* Fields */} - {hasDocumentLoaded && ( - - )} - - {/* Completed fields */} - {document.status !== DocumentStatus.COMPLETED && ( - - )} )) .otherwise(() => null)} diff --git a/apps/remix/app/components/general/direct-template/direct-template-page.tsx b/apps/remix/app/components/general/direct-template/direct-template-page.tsx index d14044d45..4dd3038cf 100644 --- a/apps/remix/app/components/general/direct-template/direct-template-page.tsx +++ b/apps/remix/app/components/general/direct-template/direct-template-page.tsx @@ -19,7 +19,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast'; import { useRequiredDocumentSigningAuthContext } from '~/components/general/document-signing/document-signing-auth-provider'; import { useRequiredDocumentSigningContext } from '~/components/general/document-signing/document-signing-provider'; -import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer'; +import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy'; import { DirectTemplateConfigureForm, @@ -152,7 +152,7 @@ export const DirectTemplatePageView = ({ gradient > - -
- { const { envelopeItems } = useCurrentEnvelopeRender(); - const scrollableContainerRef = useRef(null); - return (
@@ -216,12 +214,12 @@ const DocumentCertificateQrV2 = ({ />
-
+
diff --git a/apps/remix/app/components/general/document/document-edit-form.tsx b/apps/remix/app/components/general/document/document-edit-form.tsx index 21b2e307a..ca7043f53 100644 --- a/apps/remix/app/components/general/document/document-edit-form.tsx +++ b/apps/remix/app/components/general/document/document-edit-form.tsx @@ -31,7 +31,7 @@ import type { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/ty import { Stepper } from '@documenso/ui/primitives/stepper'; import { useToast } from '@documenso/ui/primitives/use-toast'; -import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer'; +import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy'; import { useCurrentTeam } from '~/providers/team'; export type DocumentEditFormProps = { @@ -441,7 +441,7 @@ export const DocumentEditForm = ({ gradient > - { return (
-
+
{/* Horizontal envelope item selector */} - + {/* Document View */}
diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx index 086eaa199..aedcd1ed6 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { faker } from '@faker-js/faker/locale/en'; +import type { Faker } from '@faker-js/faker'; import { Trans } from '@lingui/react/macro'; import { FieldType, SigningStatus } from '@prisma/client'; import { FileTextIcon } from 'lucide-react'; @@ -26,9 +26,8 @@ import { EnvelopePdfViewer } from '~/components/general/pdf-viewer/envelope-pdf- import { EnvelopeRendererFileSelector } from './envelope-file-selector'; -// Todo: Envelopes - Dynamically import faker export const EnvelopeEditorPreviewPage = () => { - const { envelope, editorFields } = useCurrentEnvelopeEditor(); + const { envelope, editorFields, editorConfig } = useCurrentEnvelopeEditor(); const { currentEnvelopeItem, fields } = useCurrentEnvelopeRender(); @@ -38,7 +37,20 @@ export const EnvelopeEditorPreviewPage = () => { 'recipient', ); + const [fakerInstance, setFakerInstance] = useState(null); + + useEffect(() => { + void import('@faker-js/faker/locale/en').then((mod) => { + setFakerInstance(mod.faker); + }); + }, []); + const fieldsWithPlaceholders = useMemo(() => { + if (!fakerInstance) { + return []; + } + + const faker = fakerInstance; return fields.map((field) => { const fieldMeta = ZFieldAndMetaSchema.parse(field); @@ -189,7 +201,7 @@ export const EnvelopeEditorPreviewPage = () => { .exhaustive(), }; }); - }, [fields, envelope, envelope.recipients, envelope.documentMeta]); + }, [fields, envelope, envelope.recipients, envelope.documentMeta, fakerInstance]); /** * Set the selected recipient to the first recipient in the envelope. @@ -210,26 +222,30 @@ export const EnvelopeEditorPreviewPage = () => { ...recipient, signingStatus: SigningStatus.SIGNED, }))} + presignToken={editorConfig?.embedded?.presignToken} overrideSettings={{ mode: 'export', }} >
-
+
{/* Horizontal envelope item selector */} - + + + + + Preview Mode + + + Preview what the signed document will look like with placeholder data + + {/* Document View */}
- - - Preview Mode - - - Preview what the signed document will look like with placeholder data - - - {currentEnvelopeItem !== null ? ( +
{envelopeItems.map((doc, i) => ( import('./pdf-viewer')); + +export default function PDFViewerLazy(props: PDFViewerProps) { + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + + const fallback = ( +
+ +
+ ); + + if (!isClient) { + return fallback; + } + + return {PDFViewer && }; +} diff --git a/apps/remix/app/components/general/pdf-viewer/pdf-viewer-page-image.tsx b/apps/remix/app/components/general/pdf-viewer/pdf-viewer-page-image.tsx index 271cedd2c..a050ceaba 100644 --- a/apps/remix/app/components/general/pdf-viewer/pdf-viewer-page-image.tsx +++ b/apps/remix/app/components/general/pdf-viewer/pdf-viewer-page-image.tsx @@ -28,7 +28,14 @@ export const PdfViewerPageImage = ({ imageLoadingState, imageProps }: PdfViewerP )} {/* The PDF image. */} - {imageProps.src && } + {imageProps.src && ( + + )} ); }; diff --git a/apps/remix/app/components/general/pdf-viewer/pdf-viewer.tsx b/apps/remix/app/components/general/pdf-viewer/pdf-viewer.tsx index ac83eb449..487ee5e52 100644 --- a/apps/remix/app/components/general/pdf-viewer/pdf-viewer.tsx +++ b/apps/remix/app/components/general/pdf-viewer/pdf-viewer.tsx @@ -62,14 +62,14 @@ export type PDFViewerProps = { customPageRenderer?: React.FunctionComponent<{ pageData: PageRenderData }>; } & React.HTMLAttributes; -export const PDFViewer = ({ +export default function PDFViewer({ className, data, scrollParentRef, onDocumentLoad, customPageRenderer, ...props -}: PDFViewerProps) => { +}: PDFViewerProps) { const { t } = useLingui(); const { toast } = useToast(); @@ -77,7 +77,7 @@ export const PDFViewer = ({ const [loadingState, setLoadingState] = useState('loading'); - const [pdf, setPdf] = useState(null); + const pdfRef = useRef(null); const [pages, setPages] = useState([]); @@ -86,11 +86,17 @@ export const PDFViewer = ({ return; } + let isCancelled = false; + const fetchMetadata = async () => { try { setLoadingState('loading'); setPages([]); + if (isCancelled) { + return; + } + let result: Uint8Array | null = typeof data === 'string' ? null : new Uint8Array(data); if (typeof data === 'string') { @@ -103,13 +109,24 @@ export const PDFViewer = ({ result = new Uint8Array(await response.arrayBuffer()); } - const loadedPdf = await pdfjsLib.getDocument({ data: result! }).promise; - - if (pdf) { - await pdf.destroy(); + if (isCancelled) { + return; } - setPdf(loadedPdf); + const loadedPdf = await pdfjsLib.getDocument({ data: result! }).promise; + + if (isCancelled) { + await loadedPdf.destroy(); + return; + } + + // Destroy previous PDF if it exists + if (pdfRef.current) { + await pdfRef.current.destroy(); + } + + // eslint-disable-next-line require-atomic-updates + pdfRef.current = loadedPdf; // Fetch the pages const pages = await pMap( @@ -125,10 +142,18 @@ export const PDFViewer = ({ }, ); + if (isCancelled) { + return; + } + setPages(pages); setLoadingState('loaded'); } catch (err) { + if (isCancelled) { + return; + } + console.error(err); setLoadingState('error'); @@ -143,8 +168,11 @@ export const PDFViewer = ({ void fetchMetadata(); return () => { - if (pdf) { - void pdf.destroy(); + isCancelled = true; + + if (pdfRef.current) { + void pdfRef.current.destroy(); + pdfRef.current = null; } }; }, [data]); @@ -178,19 +206,19 @@ export const PDFViewer = ({ {hasError && } {/* Loaded State */} - {loadingState === 'loaded' && pages.length > 0 && pdf && ( + {loadingState === 'loaded' && pages.length > 0 && pdfRef.current && ( )}
); -}; +} type VirtualizedPageListProps = { scrollParentRef: ScrollTarget; diff --git a/apps/remix/app/components/general/template/template-edit-form.tsx b/apps/remix/app/components/general/template/template-edit-form.tsx index 201b780bc..65be073a6 100644 --- a/apps/remix/app/components/general/template/template-edit-form.tsx +++ b/apps/remix/app/components/general/template/template-edit-form.tsx @@ -28,7 +28,7 @@ import { AddTemplateSettingsFormPartial } from '@documenso/ui/primitives/templat import type { TAddTemplateSettingsFormSchema } from '@documenso/ui/primitives/template-flow/add-template-settings.types'; import { useToast } from '@documenso/ui/primitives/use-toast'; -import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer'; +import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy'; import { useCurrentTeam } from '~/providers/team'; export type TemplateEditFormProps = { @@ -313,7 +313,7 @@ export const TemplateEditForm = ({ gradient > - )} - { + return currentParams.id !== nextParams.id; +}; + /** * This file is very similar for templates as well. Any changes here should also be adjusted there as well. * diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx index 23400f21e..25ad62615 100644 --- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx +++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx @@ -24,7 +24,7 @@ import { EnvelopeRendererFileSelector } from '~/components/general/envelope-edit import { EnvelopeGenericPageRenderer } from '~/components/general/envelope-editor/envelope-generic-page-renderer'; import { GenericErrorLayout } from '~/components/general/generic-error-layout'; import { EnvelopePdfViewer } from '~/components/general/pdf-viewer/envelope-pdf-viewer'; -import { PDFViewer } from '~/components/general/pdf-viewer/pdf-viewer'; +import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy'; import { TemplateDirectLinkBadge } from '~/components/general/template/template-direct-link-badge'; import { TemplatePageViewDocumentsTable } from '~/components/general/template/template-page-view-documents-table'; import { TemplatePageViewInformation } from '~/components/general/template/template-page-view-information'; @@ -210,7 +210,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) { documentMeta={mockedDocumentMeta} /> - { + return currentParams.id !== nextParams.id; +}; + /** * This file is very similar for documents as well. Any changes here should also be adjusted there as well. * diff --git a/apps/remix/app/routes/embed+/v2+/authoring+/_layout.tsx b/apps/remix/app/routes/embed+/v2+/authoring+/_layout.tsx index 9e155e49c..d4ed05047 100644 --- a/apps/remix/app/routes/embed+/v2+/authoring+/_layout.tsx +++ b/apps/remix/app/routes/embed+/v2+/authoring+/_layout.tsx @@ -20,6 +20,8 @@ import { injectCss } from '~/utils/css-vars'; import type { Route } from './+types/_layout'; +export const shouldRevalidate = () => false; + export const loader = async ({ request }: Route.LoaderArgs) => { const url = new URL(request.url); diff --git a/apps/remix/app/routes/embed+/v2+/authoring+/envelope.create._index.tsx b/apps/remix/app/routes/embed+/v2+/authoring+/envelope.create._index.tsx index 71317c47d..ab3665545 100644 --- a/apps/remix/app/routes/embed+/v2+/authoring+/envelope.create._index.tsx +++ b/apps/remix/app/routes/embed+/v2+/authoring+/envelope.create._index.tsx @@ -38,6 +38,8 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader'; import type { Route } from './+types/envelope.create._index'; +export const shouldRevalidate = () => false; + export const loader = async ({ request }: Route.LoaderArgs) => { const url = new URL(request.url); diff --git a/apps/remix/app/routes/embed+/v2+/authoring+/envelope.edit.$id.tsx b/apps/remix/app/routes/embed+/v2+/authoring+/envelope.edit.$id.tsx index d96294d69..0a6b758a7 100644 --- a/apps/remix/app/routes/embed+/v2+/authoring+/envelope.edit.$id.tsx +++ b/apps/remix/app/routes/embed+/v2+/authoring+/envelope.edit.$id.tsx @@ -3,7 +3,7 @@ import { useLayoutEffect, useMemo, useState } from 'react'; import { Trans, useLingui } from '@lingui/react/macro'; import { EnvelopeType } from '@prisma/client'; import { CheckCircle2Icon } from 'lucide-react'; -import { redirect } from 'react-router'; +import { type ShouldRevalidateFunctionArgs, redirect } from 'react-router'; import { EnvelopeEditorProvider } from '@documenso/lib/client-only/providers/envelope-editor-provider'; import type { SupportedLanguageCodes } from '@documenso/lib/constants/i18n'; @@ -30,6 +30,10 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader'; import type { Route } from './+types/envelope.edit.$id'; +export const shouldRevalidate = ({ currentParams, nextParams }: ShouldRevalidateFunctionArgs) => { + return currentParams.id !== nextParams.id; +}; + export const loader = async ({ request, params }: Route.LoaderArgs) => { const url = new URL(request.url); diff --git a/apps/remix/server/api/files/routes/get-envelope-item-pdf.ts b/apps/remix/server/api/files/routes/get-envelope-item-pdf.ts index 587be164e..7f1b25aef 100644 --- a/apps/remix/server/api/files/routes/get-envelope-item-pdf.ts +++ b/apps/remix/server/api/files/routes/get-envelope-item-pdf.ts @@ -7,6 +7,7 @@ import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token'; import { getTeamById } from '@documenso/lib/server-only/team/get-team'; import type { DocumentDataVersion } from '@documenso/lib/types/document'; +import { sha256 } from '@documenso/lib/universal/crypto'; import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server'; import { prisma } from '@documenso/prisma'; @@ -122,6 +123,12 @@ export const handleEnvelopeItemPdfRequest = async ({ const documentDataToUse = version === 'current' ? envelopeItem.documentData.data : envelopeItem.documentData.initialData; + const etag = Buffer.from(sha256(documentDataToUse)).toString('hex'); + + if (c.req.header('If-None-Match') === etag) { + return c.status(304); + } + const file = await getFileServerSide({ type: envelopeItem.documentData.type, data: documentDataToUse, @@ -137,6 +144,7 @@ export const handleEnvelopeItemPdfRequest = async ({ // Note: Only set these headers on success. c.header('Content-Type', 'application/pdf'); + c.header('ETag', etag); c.header('Cache-Control', `${cacheStrategy}, max-age=31536000, immutable`); return c.body(file); diff --git a/apps/remix/vite.config.ts b/apps/remix/vite.config.ts index 65dd1ec50..76aebc780 100644 --- a/apps/remix/vite.config.ts +++ b/apps/remix/vite.config.ts @@ -58,6 +58,7 @@ export default defineConfig({ 'playwright', 'playwright-core', '@playwright/browser-chromium', + 'pdfjs-dist', ], }, optimizeDeps: { diff --git a/packages/app-tests/e2e/api/v1/template-field-prefill.spec.ts b/packages/app-tests/e2e/api/v1/template-field-prefill.spec.ts index 15bd2a43e..d2a7f106b 100644 --- a/packages/app-tests/e2e/api/v1/template-field-prefill.spec.ts +++ b/packages/app-tests/e2e/api/v1/template-field-prefill.spec.ts @@ -197,7 +197,7 @@ test.describe('Template Field Prefill API v1', () => { // 7. Navigate to the template await page.goto( - `${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, + `${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, ); // 8. Create a document from the template with prefilled fields @@ -488,7 +488,7 @@ test.describe('Template Field Prefill API v1', () => { // 7. Navigate to the template await page.goto( - `${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, + `${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, ); // 8. Create a document from the template without prefilled fields diff --git a/packages/app-tests/e2e/api/v2/template-field-prefill.spec.ts b/packages/app-tests/e2e/api/v2/template-field-prefill.spec.ts index 97525de6c..80bddbccc 100644 --- a/packages/app-tests/e2e/api/v2/template-field-prefill.spec.ts +++ b/packages/app-tests/e2e/api/v2/template-field-prefill.spec.ts @@ -197,7 +197,7 @@ test.describe('Template Field Prefill API v2', () => { // 7. Navigate to the template await page.goto( - `${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, + `${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, ); // 8. Create a document from the template with prefilled fields using v2 API @@ -485,7 +485,7 @@ test.describe('Template Field Prefill API v2', () => { // 7. Navigate to the template await page.goto( - `${WEBAPP_BASE_URL}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, + `${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`, ); // 8. Create a document from the template without prefilled fields using v2 API diff --git a/packages/ui/components/field/envelope-field-tooltip.tsx b/packages/ui/components/field/envelope-field-tooltip.tsx index 25dde4ad4..0cf834ecc 100644 --- a/packages/ui/components/field/envelope-field-tooltip.tsx +++ b/packages/ui/components/field/envelope-field-tooltip.tsx @@ -124,7 +124,10 @@ export function EnvelopeFieldToolTip({ - + {children} diff --git a/packages/ui/components/field/field-tooltip.tsx b/packages/ui/components/field/field-tooltip.tsx index 027978292..3fceb0589 100644 --- a/packages/ui/components/field/field-tooltip.tsx +++ b/packages/ui/components/field/field-tooltip.tsx @@ -65,7 +65,7 @@ export function FieldToolTip({ children, color, className = '', field }: FieldTo diff --git a/packages/ui/styles/theme.css b/packages/ui/styles/theme.css index 7a5a8b8e6..2f05479ba 100644 --- a/packages/ui/styles/theme.css +++ b/packages/ui/styles/theme.css @@ -234,6 +234,15 @@ scrollbar-gutter: stable; } +.scrollbar-hidden { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.scrollbar-hidden::-webkit-scrollbar { + display: none; +} + /* .custom-scrollbar::-webkit-scrollbar-track { border-radius: 10px; }