diff --git a/package.json b/package.json index 20287c8f0f..fcf63309d5 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@types/prop-types": "^15.7.4", "cheerio": "^1.0.0-rc.10", "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "dompurify": "^3.4.0", "fast-glob": "^3.3.3", "front-matter": "^4.0.2", @@ -96,6 +97,7 @@ "react-medium-image-zoom": "^5.4.1", "react-select": "^5.7.0", "remark-gfm": "^1.0.0", + "tailwind-merge": "^2.5.5", "typescript": "^4.6.3", "use-keyboard-shortcut": "^1.1.6", "util": "^0.12.4" diff --git a/src/components/ButtonWithTooltip/ButtonWithTooltip.tsx b/src/components/ButtonWithTooltip/ButtonWithTooltip.tsx index 44bd0243e6..bb64fc67d4 100644 --- a/src/components/ButtonWithTooltip/ButtonWithTooltip.tsx +++ b/src/components/ButtonWithTooltip/ButtonWithTooltip.tsx @@ -1,5 +1,5 @@ import React, { HTMLAttributes, MouseEvent, useState, useRef } from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { button, tooltipClass, isVisible, notificationClass } from './ButtonWithTooltip.module.css'; interface Props extends HTMLAttributes { diff --git a/src/components/CodeEditor/Chrome.tsx b/src/components/CodeEditor/Chrome.tsx index 9e240c35dd..c2261885ec 100644 --- a/src/components/CodeEditor/Chrome.tsx +++ b/src/components/CodeEditor/Chrome.tsx @@ -1,4 +1,4 @@ -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import React from 'react'; import { SmallMenuLabel } from 'src/components/Menu/Label'; import APIKeyIndicator from 'src/components/blocks/software/Code/ApiKeyIndicator'; diff --git a/src/components/Examples/ExamplesCheckbox.tsx b/src/components/Examples/ExamplesCheckbox.tsx index 3e79cf2e05..1b756abe85 100644 --- a/src/components/Examples/ExamplesCheckbox.tsx +++ b/src/components/Examples/ExamplesCheckbox.tsx @@ -1,6 +1,6 @@ import React from 'react'; import Icon from '@ably/ui/core/Icon'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; const ExamplesCheckbox = ({ label, diff --git a/src/components/Examples/ExamplesFilter.tsx b/src/components/Examples/ExamplesFilter.tsx index 0ca712d337..fad7c32ba1 100644 --- a/src/components/Examples/ExamplesFilter.tsx +++ b/src/components/Examples/ExamplesFilter.tsx @@ -4,7 +4,7 @@ import Icon from '@ably/ui/core/Icon'; import { Input } from 'src/components/ui/Input'; import { products } from '../../data/examples'; import Button from '@ably/ui/core/Button'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import Badge from '@ably/ui/core/Badge'; import ExamplesCheckbox from './ExamplesCheckbox'; import { SelectedFilters } from './ExamplesContent'; diff --git a/src/components/Examples/ExamplesGrid.tsx b/src/components/Examples/ExamplesGrid.tsx index 1fde8d68dc..595b1c1898 100644 --- a/src/components/Examples/ExamplesGrid.tsx +++ b/src/components/Examples/ExamplesGrid.tsx @@ -3,7 +3,7 @@ import Badge from '@ably/ui/core/Badge'; import Icon from '@ably/ui/core/Icon'; import { IconName } from '@ably/ui/core/Icon/types'; import { ProductName, products as dataProducts } from '@ably/ui/core/ProductTile/data'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { Image, ImageProps } from '../Image'; import { DEFAULT_EXAMPLE_LANGUAGES } from '../../data/examples/'; import { Example } from '../../data/examples/types'; diff --git a/src/components/Examples/ExamplesRenderer.tsx b/src/components/Examples/ExamplesRenderer.tsx index 8ccadbc0e3..79f25aec59 100644 --- a/src/components/Examples/ExamplesRenderer.tsx +++ b/src/components/Examples/ExamplesRenderer.tsx @@ -8,7 +8,7 @@ import { updateAblyConnectionKey } from 'src/utilities/update-ably-connection-ke import { IconName } from '@ably/ui/core/Icon/types'; import SegmentedControl from '@ably/ui/core/SegmentedControl'; import dotGrid from './images/dot-grid.svg'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { getRandomChannelName } from '../../utilities/get-random-channel-name'; // Shared tsconfig for proper ES2020+ transpilation in Sandpack import examplesTsConfig from '../../../examples/tsconfig.json'; diff --git a/src/components/Homepage/PlatformAndProducts.tsx b/src/components/Homepage/PlatformAndProducts.tsx index 5899cfd574..7ae8e40019 100644 --- a/src/components/Homepage/PlatformAndProducts.tsx +++ b/src/components/Homepage/PlatformAndProducts.tsx @@ -1,5 +1,5 @@ import ProductTile from '@ably/ui/core/ProductTile'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { Image, ImageProps, getImageFromList } from 'src/components/Image'; import type { PlatformProductsSectionData, PlatformCardData } from 'src/data/content/types'; import { navigate } from '../Link'; diff --git a/src/components/Layout/Breadcrumbs.tsx b/src/components/Layout/Breadcrumbs.tsx index f0c080adab..31121740af 100644 --- a/src/components/Layout/Breadcrumbs.tsx +++ b/src/components/Layout/Breadcrumbs.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useLayoutContext } from 'src/contexts/layout-context'; import Link from '../Link'; import Icon from '@ably/ui/core/Icon'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { hierarchicalKey } from './utils/nav'; const linkStyles = diff --git a/src/components/Layout/CopyForLLM.tsx b/src/components/Layout/CopyForLLM.tsx index c176feaa44..bbd979803f 100644 --- a/src/components/Layout/CopyForLLM.tsx +++ b/src/components/Layout/CopyForLLM.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useLocation } from '@reach/router'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import Icon from '@ably/ui/core/Icon'; import { IconName } from '@ably/ui/core/Icon/types'; import { track } from '@ably/ui/core/insights'; diff --git a/src/components/Layout/Footer.tsx b/src/components/Layout/Footer.tsx index 79c8761358..936733a5c3 100644 --- a/src/components/Layout/Footer.tsx +++ b/src/components/Layout/Footer.tsx @@ -3,7 +3,7 @@ import { useLocation } from '@reach/router'; import Icon from '@ably/ui/core/Icon'; import { IconName } from '@ably/ui/core/Icon/types'; import Status, { StatusUrl } from '@ably/ui/core/Status'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import type { PageContextType } from './Layout'; import { useLayoutContext } from 'src/contexts/layout-context'; import Button from '@ably/ui/core/Button'; diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index 6d8aa70d4d..08e1e6e9fb 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -4,12 +4,12 @@ import { graphql, useStaticQuery } from 'gatsby'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import * as Tooltip from '@radix-ui/react-tooltip'; import { throttle } from 'es-toolkit/compat'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import Icon from '@ably/ui/core/Icon'; import TabMenu from '@ably/ui/core/TabMenu'; import Logo from '@ably/ui/core/images/logo/ably-logo.svg'; import { track } from '@ably/ui/core/insights'; -import { componentMaxHeight, HEADER_BOTTOM_MARGIN, HEADER_HEIGHT } from '@ably/ui/core/utils/heights'; +import { componentMaxHeight, HEADER_BOTTOM_MARGIN, HEADER_HEIGHT } from 'src/utilities/heights'; import { IconName } from '@ably/ui/core/Icon/types'; import LeftSidebar from './LeftSidebar'; import ProductBar from './ProductBar'; diff --git a/src/components/Layout/LanguageSelector.tsx b/src/components/Layout/LanguageSelector.tsx index d698114cd2..1e9a4e911e 100644 --- a/src/components/Layout/LanguageSelector.tsx +++ b/src/components/Layout/LanguageSelector.tsx @@ -3,8 +3,8 @@ import { useLocation } from '@reach/router'; import Badge from '@ably/ui/core/Badge'; import Icon from '@ably/ui/core/Icon'; import { IconName } from '@ably/ui/core/Icon/types'; -import cn from '@ably/ui/core/utils/cn'; -import { componentMaxHeight, HEADER_BOTTOM_MARGIN, HEADER_HEIGHT } from '@ably/ui/core/utils/heights'; +import cn from 'src/utilities/cn'; +import { componentMaxHeight, HEADER_BOTTOM_MARGIN, HEADER_HEIGHT } from 'src/utilities/heights'; import { track } from '@ably/ui/core/insights'; import { languageData, languageInfo } from 'src/data/languages'; import { LanguageKey } from 'src/data/languages/types'; diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 11a5a8c98d..4917689a22 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { PageProps } from 'gatsby'; import { useLocation } from '@reach/router'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import '../../styles/global.css'; import { Container } from 'src/components/Container'; diff --git a/src/components/Layout/LeftSidebar.tsx b/src/components/Layout/LeftSidebar.tsx index fbaaffa024..16de250f8a 100644 --- a/src/components/Layout/LeftSidebar.tsx +++ b/src/components/Layout/LeftSidebar.tsx @@ -2,8 +2,8 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { graphql, useStaticQuery } from 'gatsby'; import { useLocation } from '@reach/router'; import * as Accordion from '@radix-ui/react-accordion'; -import cn from '@ably/ui/core/utils/cn'; -import { HEADER_HEIGHT } from '@ably/ui/core/utils/heights'; +import cn from 'src/utilities/cn'; +import { HEADER_HEIGHT } from 'src/utilities/heights'; import Icon from '@ably/ui/core/Icon'; import { productData } from 'src/data'; diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index 4122b2c135..32af50478e 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -11,7 +11,7 @@ import React, { import { navigate, PageProps } from 'gatsby'; import CodeSnippet from '@ably/ui/core/CodeSnippet'; import type { CodeSnippetProps, SDKType } from '@ably/ui/core/CodeSnippet'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { getRandomChannelName } from '../../utilities/get-random-channel-name'; diff --git a/src/components/Layout/ProductBar.tsx b/src/components/Layout/ProductBar.tsx index cdf38cbe3c..e3b4558f63 100644 --- a/src/components/Layout/ProductBar.tsx +++ b/src/components/Layout/ProductBar.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import Icon from '@ably/ui/core/Icon'; import { IconName } from '@ably/ui/core/Icon/types'; diff --git a/src/components/Layout/RightSidebar.tsx b/src/components/Layout/RightSidebar.tsx index aa57ab2c1f..9adb944b36 100644 --- a/src/components/Layout/RightSidebar.tsx +++ b/src/components/Layout/RightSidebar.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState, useCallback } from 'react'; import { useLocation } from '@reach/router'; -import cn from '@ably/ui/core/utils/cn'; -import { componentMaxHeight, HEADER_HEIGHT, HEADER_BOTTOM_MARGIN } from '@ably/ui/core/utils/heights'; +import cn from 'src/utilities/cn'; +import { componentMaxHeight, HEADER_HEIGHT, HEADER_BOTTOM_MARGIN } from 'src/utilities/heights'; import { PRODUCT_BAR_HEIGHT } from './utils/heights'; import { useLayoutContext } from 'src/contexts/layout-context'; import { languageData } from 'src/data/languages'; diff --git a/src/components/Layout/mdx/Admonition.tsx b/src/components/Layout/mdx/Admonition.tsx index bde12dd358..e775d8c563 100644 --- a/src/components/Layout/mdx/Admonition.tsx +++ b/src/components/Layout/mdx/Admonition.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import Aside from '../../blocks/dividers/Aside'; const LEGACY_ADMONITION_TYPES = ['new', 'updated', 'experimental', 'public-preview']; diff --git a/src/components/Layout/mdx/MethodSignature.tsx b/src/components/Layout/mdx/MethodSignature.tsx index 7dbe3e291b..d352a7d279 100644 --- a/src/components/Layout/mdx/MethodSignature.tsx +++ b/src/components/Layout/mdx/MethodSignature.tsx @@ -1,5 +1,5 @@ import React, { useRef, useLayoutEffect, useState } from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; interface MethodSignatureProps { children: React.ReactNode; diff --git a/src/components/Layout/mdx/NestedTable/NestedTable.tsx b/src/components/Layout/mdx/NestedTable/NestedTable.tsx index 49473280d1..fafada3fd0 100644 --- a/src/components/Layout/mdx/NestedTable/NestedTable.tsx +++ b/src/components/Layout/mdx/NestedTable/NestedTable.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo } from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { TableData, useNestedTable } from './NestedTableContext'; import { parseTableChildren } from './parseTable'; import { NestedTablePropertyRow } from './NestedTablePropertyRow'; diff --git a/src/components/Layout/mdx/NestedTable/NestedTableExpandButton.tsx b/src/components/Layout/mdx/NestedTable/NestedTableExpandButton.tsx index d9d8d47b9a..406c5b0d9b 100644 --- a/src/components/Layout/mdx/NestedTable/NestedTableExpandButton.tsx +++ b/src/components/Layout/mdx/NestedTable/NestedTableExpandButton.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; interface NestedTableExpandButtonProps { typeName: string; diff --git a/src/components/Layout/mdx/NestedTable/NestedTablePropertyRow.tsx b/src/components/Layout/mdx/NestedTable/NestedTablePropertyRow.tsx index 53984d0fbf..b773eb1c95 100644 --- a/src/components/Layout/mdx/NestedTable/NestedTablePropertyRow.tsx +++ b/src/components/Layout/mdx/NestedTable/NestedTablePropertyRow.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { TableData, TableProperty, useNestedTable } from './NestedTableContext'; import { NestedTableExpandButton } from './NestedTableExpandButton'; diff --git a/src/components/Layout/mdx/PageHeader.tsx b/src/components/Layout/mdx/PageHeader.tsx index 553bc8e15e..4099f60b7f 100644 --- a/src/components/Layout/mdx/PageHeader.tsx +++ b/src/components/Layout/mdx/PageHeader.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; type PageHeaderProps = { title: string; diff --git a/src/components/Layout/mdx/Table.tsx b/src/components/Layout/mdx/Table.tsx index 8a54549ad9..104db03c12 100644 --- a/src/components/Layout/mdx/Table.tsx +++ b/src/components/Layout/mdx/Table.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; // Table Root Component export interface TableRootProps extends React.HTMLAttributes { diff --git a/src/components/Layout/mdx/Tabs.tsx b/src/components/Layout/mdx/Tabs.tsx index 83a27c1f46..06931ac103 100644 --- a/src/components/Layout/mdx/Tabs.tsx +++ b/src/components/Layout/mdx/Tabs.tsx @@ -1,6 +1,6 @@ import React, { isValidElement, ReactNode } from 'react'; import * as RadixTabs from '@radix-ui/react-tabs'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; interface TabProps { value: string; diff --git a/src/components/Layout/utils/styles.ts b/src/components/Layout/utils/styles.ts index c36da25bca..179dd6cc41 100644 --- a/src/components/Layout/utils/styles.ts +++ b/src/components/Layout/utils/styles.ts @@ -1,4 +1,4 @@ -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; export const tooltipContentClassName = cn( 'px-2 py-1 bg-neutral-1000 dark:bg-neutral-300 text-neutral-200 dark:text-neutral-1100 ui-text-p3 font-medium rounded-lg relative z-50 mt-2', diff --git a/src/components/Menu/Label/index.tsx b/src/components/Menu/Label/index.tsx index 480cd6741c..b9d4d24808 100644 --- a/src/components/Menu/Label/index.tsx +++ b/src/components/Menu/Label/index.tsx @@ -1,5 +1,5 @@ import React, { FunctionComponent as FC, HTMLProps } from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { smallMenuLabel } from './Label.module.css'; diff --git a/src/components/SDKsPage/Card/index.tsx b/src/components/SDKsPage/Card/index.tsx index d022f0eceb..7f487f15df 100644 --- a/src/components/SDKsPage/Card/index.tsx +++ b/src/components/SDKsPage/Card/index.tsx @@ -1,7 +1,7 @@ import Icon from '@ably/ui/core/Icon'; import Link from 'src/components/Link'; import { btn_sdks } from '../sdks.module.css'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; type ImagesSDK = { src: string; diff --git a/src/components/SDKsPage/MainSection/index.tsx b/src/components/SDKsPage/MainSection/index.tsx index 0b2ee22eea..8224cdf70c 100644 --- a/src/components/SDKsPage/MainSection/index.tsx +++ b/src/components/SDKsPage/MainSection/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { container } from '../sdks.module.css'; import CardGrid from '../Card/CardGrid'; import { data } from '../data'; diff --git a/src/components/ui/ButtonGroup.tsx b/src/components/ui/ButtonGroup.tsx index 2049f3f750..d17fcd01ea 100644 --- a/src/components/ui/ButtonGroup.tsx +++ b/src/components/ui/ButtonGroup.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; import { Separator } from './Separator'; const buttonGroupVariants = cva( diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx index 06d36a5921..1ee85f4075 100644 --- a/src/components/ui/Input.tsx +++ b/src/components/ui/Input.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; function Input({ className, type, ...props }: React.ComponentProps<'input'>) { return ( diff --git a/src/components/ui/Separator.tsx b/src/components/ui/Separator.tsx index aba660d413..dc46212a48 100644 --- a/src/components/ui/Separator.tsx +++ b/src/components/ui/Separator.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import * as SeparatorPrimitive from '@radix-ui/react-separator'; -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; function Separator({ className, diff --git a/src/components/ui/Skeleton.tsx b/src/components/ui/Skeleton.tsx index e6aacaa945..53a544373c 100644 --- a/src/components/ui/Skeleton.tsx +++ b/src/components/ui/Skeleton.tsx @@ -1,4 +1,4 @@ -import cn from '@ably/ui/core/utils/cn'; +import cn from 'src/utilities/cn'; function Skeleton({ className, ...props }: React.ComponentProps<'div'>) { return ( diff --git a/src/utilities/cn.ts b/src/utilities/cn.ts new file mode 100644 index 0000000000..cc49780094 --- /dev/null +++ b/src/utilities/cn.ts @@ -0,0 +1,10 @@ +import clsx, { type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +/** + * Merge class names with clsx and resolve Tailwind class conflicts with + * tailwind-merge. Local replacement for @ably/ui/core/utils/cn (DX-1128). + */ +const cn = (...inputs: ClassValue[]): string => twMerge(clsx(inputs)); + +export default cn; diff --git a/src/utilities/heights.ts b/src/utilities/heights.ts new file mode 100644 index 0000000000..8bb2b64a65 --- /dev/null +++ b/src/utilities/heights.ts @@ -0,0 +1,11 @@ +/** + * Layout height helpers. Local replacement for @ably/ui/core/utils/heights + * (DX-1128). + */ +export const HEADER_HEIGHT = 64; +export const HEADER_BOTTOM_MARGIN = 24; + +export const componentMaxHeight = (...heights: number[]): string => { + const totalHeight = `${heights.reduce((sum, height) => sum + height, 0)}px`; + return `calc(min(100dvh, 100vh) - ${totalHeight})`; +};