diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..9674c422c4 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +src/components/Icon/glyphs/ diff --git a/.prettierignore b/.prettierignore index bcc212213e..370a00e498 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,4 +5,10 @@ src/pages/**/*.md examples/**/README.md # Fixture files should not be formatted by Prettier -data/onPostBuild/__fixtures__/*.mdx \ No newline at end of file +data/onPostBuild/__fixtures__/*.mdx + +# Vendored @ably/ui design tokens, reset and component CSS (DX-1128) - do not reformat +src/styles/ui/ + +# Vendored @ably/ui icon glyphs (DX-1128) - do not reformat +src/components/Icon/glyphs/ diff --git a/package.json b/package.json index 20287c8f0f..f969f33922 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@codesandbox/sandpack-react": "^2.20.0", "@codesandbox/sandpack-themes": "^2.0.21", "@gfx/zopfli": "^1.0.15", + "@heroicons/react": "^2.2.0", "@mdx-js/react": "^2.3.0", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-dropdown-menu": "^2.1.16", @@ -58,7 +59,9 @@ "@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", + "es-toolkit": "^1.44.0", "fast-glob": "^3.3.3", "front-matter": "^4.0.2", "fs-extra": "^11.3.4", @@ -81,6 +84,8 @@ "gatsby-transformer-remark": "6.16.0", "gatsby-transformer-sharp": "5.16.0", "gatsby-transformer-yaml": "5.16.0", + "highlight.js": "^11.11.1", + "highlightjs-curl": "^1.3.0", "htmr": "^1.0.2", "js-yaml": "^4.1.1", "lodash": "^4.18.1", @@ -96,6 +101,8 @@ "react-medium-image-zoom": "^5.4.1", "react-select": "^5.7.0", "remark-gfm": "^1.0.0", + "swr": "^2.4.0", + "tailwind-merge": "^2.5.5", "typescript": "^4.6.3", "use-keyboard-shortcut": "^1.1.6", "util": "^0.12.4" diff --git a/setupTests.js b/setupTests.js index 6838e230f6..4f8ce05462 100644 --- a/setupTests.js +++ b/setupTests.js @@ -11,7 +11,7 @@ window.ResizeObserver = ResizeObserver; Element.prototype.scrollIntoView = jest.fn(); -jest.mock('@ably/ui/core/utils/syntax-highlighter', () => ({ +jest.mock('src/utilities/syntax-highlighter', () => ({ highlightSnippet: jest.fn, LINE_HIGHLIGHT_CLASSES: { addition: 'code-line-addition', @@ -23,12 +23,12 @@ jest.mock('@ably/ui/core/utils/syntax-highlighter', () => ({ splitHtmlLines: (html) => html.split('\n'), })); -jest.mock('@ably/ui/core/Code', () => ({ +jest.mock('src/components/ui/Code', () => ({ __esModule: true, default: () => null, })); -jest.mock('@ably/ui/core/utils/syntax-highlighter-registry', () => ({ +jest.mock('src/utilities/syntax-highlighter-registry', () => ({ __esModule: true, default: [], })); 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..42df25f89d 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 Icon from 'src/components/Icon'; +import cn from 'src/utilities/cn'; const ExamplesCheckbox = ({ label, diff --git a/src/components/Examples/ExamplesContent.tsx b/src/components/Examples/ExamplesContent.tsx index 6a4a4a916b..4ed10e4457 100644 --- a/src/components/Examples/ExamplesContent.tsx +++ b/src/components/Examples/ExamplesContent.tsx @@ -6,7 +6,7 @@ import { ImageProps } from '../Image'; import { examples, products } from '../../data/examples/'; import { filterSearchExamples } from './filter-search-examples'; import ExamplesNoResults from './ExamplesNoResults'; -import { ProductName } from '@ably/ui/core/ProductTile/data'; +import { ProductName } from 'src/components/ui/ProductTile/data'; import { useLocation } from '@reach/router'; export type SelectedFilters = { products: ProductName[]; useCases: string[] }; diff --git a/src/components/Examples/ExamplesFilter.tsx b/src/components/Examples/ExamplesFilter.tsx index 0ca712d337..3830b389e9 100644 --- a/src/components/Examples/ExamplesFilter.tsx +++ b/src/components/Examples/ExamplesFilter.tsx @@ -1,16 +1,16 @@ import React, { ChangeEvent, Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/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 Badge from '@ably/ui/core/Badge'; +import Button from 'src/components/ui/Button'; +import cn from 'src/utilities/cn'; +import Badge from 'src/components/ui/Badge'; import ExamplesCheckbox from './ExamplesCheckbox'; import { SelectedFilters } from './ExamplesContent'; import { useOnClickOutside } from 'src/hooks/use-on-click-outside'; import { navigate } from 'gatsby'; -import { ProductName } from '@ably/ui/core/ProductTile/data'; +import { ProductName } from 'src/components/ui/ProductTile/data'; const ExamplesFilter = ({ selected, diff --git a/src/components/Examples/ExamplesGrid.tsx b/src/components/Examples/ExamplesGrid.tsx index 1fde8d68dc..abb033eeab 100644 --- a/src/components/Examples/ExamplesGrid.tsx +++ b/src/components/Examples/ExamplesGrid.tsx @@ -1,9 +1,9 @@ import React, { useCallback } from 'react'; -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 Badge from 'src/components/ui/Badge'; +import Icon from 'src/components/Icon'; +import { IconName } from 'src/components/Icon/types'; +import { ProductName, products as dataProducts } from 'src/components/ui/ProductTile/data'; +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/ExamplesNoResults.tsx b/src/components/Examples/ExamplesNoResults.tsx index ddab95d64c..b8d23c10e1 100644 --- a/src/components/Examples/ExamplesNoResults.tsx +++ b/src/components/Examples/ExamplesNoResults.tsx @@ -1,4 +1,4 @@ -import Badge from '@ably/ui/core/Badge'; +import Badge from 'src/components/ui/Badge'; import { Link } from 'gatsby'; const ExamplesNoResults = () => { diff --git a/src/components/Examples/ExamplesRenderer.tsx b/src/components/Examples/ExamplesRenderer.tsx index 8ccadbc0e3..15138e7547 100644 --- a/src/components/Examples/ExamplesRenderer.tsx +++ b/src/components/Examples/ExamplesRenderer.tsx @@ -5,10 +5,10 @@ import { CodeEditor } from 'src/components/CodeEditor'; import { LanguageKey } from 'src/data/languages/types'; import { ExampleFiles, ExampleWithContent } from 'src/data/examples/types'; import { updateAblyConnectionKey } from 'src/utilities/update-ably-connection-keys'; -import { IconName } from '@ably/ui/core/Icon/types'; -import SegmentedControl from '@ably/ui/core/SegmentedControl'; +import { IconName } from 'src/components/Icon/types'; +import SegmentedControl from 'src/components/ui/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/Changelog.tsx b/src/components/Homepage/Changelog.tsx index b98f44282b..f98b95950e 100644 --- a/src/components/Homepage/Changelog.tsx +++ b/src/components/Homepage/Changelog.tsx @@ -1,8 +1,8 @@ import { useStaticQuery } from 'gatsby'; import { graphql } from 'gatsby'; import { Key } from 'react'; -import Badge from '@ably/ui/core/Badge'; -import FeaturedLink from '@ably/ui/core/FeaturedLink'; +import Badge from 'src/components/ui/Badge'; +import FeaturedLink from 'src/components/ui/FeaturedLink'; import Link from '../Link'; interface ChangelogFeedItemNode { diff --git a/src/components/Homepage/ExamplesSection.tsx b/src/components/Homepage/ExamplesSection.tsx index 6caae9c4a2..dd2995ccc5 100644 --- a/src/components/Homepage/ExamplesSection.tsx +++ b/src/components/Homepage/ExamplesSection.tsx @@ -1,7 +1,7 @@ import type { ExamplesSectionData } from 'src/data/content/types'; import { getImageFromList, ImageProps } from 'src/components/Image'; import { Image } from 'src/components/Image'; -import FeaturedLink from '@ably/ui/core/FeaturedLink'; +import FeaturedLink from 'src/components/ui/FeaturedLink'; export const ExamplesSection = ({ section, images }: { section: ExamplesSectionData; images: ImageProps[] }) => { const imageUrl = getImageFromList(images, section.image); diff --git a/src/components/Homepage/PlatformAndProducts.tsx b/src/components/Homepage/PlatformAndProducts.tsx index 5899cfd574..ee72ecc5c0 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 ProductTile from 'src/components/ui/ProductTile'; +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/Icon/glyphs/icon-gui-ably-badge.js b/src/components/Icon/glyphs/icon-gui-ably-badge.js new file mode 100644 index 0000000000..6baceb59a4 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-ably-badge.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiAblyBadge=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M2.77 19.615 11.849 3H9.4L1 18.373zm18.282 0L11.975 3h2.449l8.399 15.373zm-9.14-6.962 9.014 7.06L19.086 21l-7.174-5.617L4.737 21l-1.84-1.288z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconGuiAblyBadge);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-ai-transport-outline.js b/src/components/Icon/glyphs/icon-gui-prod-ai-transport-outline.js new file mode 100644 index 0000000000..16d3b0e112 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-ai-transport-outline.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdAiTransportOutline=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.307,d:"M11.498 1.016c.334 0 .628.221.72.543l.811 2.841A3.74 3.74 0 0 0 15.6 6.971l2.841.812a.749.749 0 0 1 0 1.44l-2.84.811a3.74 3.74 0 0 0-2.572 2.572l-.812 2.84a.749.749 0 0 1-1.44 0l-.811-2.84a3.74 3.74 0 0 0-2.571-2.572l-2.841-.811a.749.749 0 0 1 0-1.44l2.84-.812a3.74 3.74 0 0 0 2.572-2.57l.812-2.842a.75.75 0 0 1 .72-.543",clipRule:"evenodd"}),React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.307,d:"M7.455 14.642 3.56 18.536M5.059 12.995l-2.097 2.097"}));const ForwardRef=forwardRef(IconGuiProdAiTransportOutline);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-ai-transport-solid.js b/src/components/Icon/glyphs/icon-gui-prod-ai-transport-solid.js new file mode 100644 index 0000000000..77ac77b0ef --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-ai-transport-solid.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdAiTransportSolid=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#FF5416",d:"M19.638 8.502c0 .626-.415 1.177-1.017 1.349l-2.841.812a3.09 3.09 0 0 0-2.122 2.121l-.812 2.842a1.402 1.402 0 0 1-2.697 0l-.812-2.841a3.09 3.09 0 0 0-2.121-2.122L4.374 9.85a1.402 1.402 0 0 1 0-2.697l2.842-.812A3.09 3.09 0 0 0 9.337 4.22l.812-2.841a1.402 1.402 0 0 1 2.697 0l.812 2.841a3.09 3.09 0 0 0 2.122 2.122l2.841.812a1.4 1.4 0 0 1 1.017 1.348M6.993 14.18a.653.653 0 1 1 .924.924l-3.894 3.893a.653.653 0 0 1-.924-.924zM4.597 12.533a.653.653 0 1 1 .924.924l-2.097 2.096a.653.653 0 1 1-.924-.924z"}));const ForwardRef=forwardRef(IconGuiProdAiTransportSolid);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-chat-outline.js b/src/components/Icon/glyphs/icon-gui-prod-chat-outline.js new file mode 100644 index 0000000000..32c07a49d6 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-chat-outline.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdChatOutline=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeWidth:1.5,d:"M7 3h10a3.333 3.333 0 0 1 3.333 3.333v12.402c0 .869-1.031 1.324-1.673.739l-3.802-3.467a.67.67 0 0 0-.45-.174H7A3.333 3.333 0 0 1 3.667 12.5V6.333A3.333 3.333 0 0 1 7 3Z"}));const ForwardRef=forwardRef(IconGuiProdChatOutline);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-chat-solid.js b/src/components/Icon/glyphs/icon-gui-prod-chat-solid.js new file mode 100644 index 0000000000..668af04d5d --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-chat-solid.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdChatSolid=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M2.917 6.333A4.083 4.083 0 0 1 7 2.25h10a4.083 4.083 0 0 1 4.083 4.083v12.402c0 1.52-1.805 2.318-2.929 1.293l-3.778-3.445H7A4.083 4.083 0 0 1 2.917 12.5z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconGuiProdChatSolid);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-liveobjects-outline.js b/src/components/Icon/glyphs/icon-gui-prod-liveobjects-outline.js new file mode 100644 index 0000000000..f0b0510e33 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-liveobjects-outline.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdLiveobjectsOutline=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeWidth:1.5,d:"M14.038 3h2.886A4.076 4.076 0 0 1 21 7.075v2.887M9.962 3H7.075A4.075 4.075 0 0 0 3 7.075v2.887m0 4.076v2.886A4.076 4.076 0 0 0 7.075 21h2.887M21 14.038v2.886A4.076 4.076 0 0 1 16.924 21h-2.886"}),React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.5,d:"m8.434 12.34 2.887 2.887 4.415-6.453"}));const ForwardRef=forwardRef(IconGuiProdLiveobjectsOutline);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-liveobjects-solid.js b/src/components/Icon/glyphs/icon-gui-prod-liveobjects-solid.js new file mode 100644 index 0000000000..8b8261a9cd --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-liveobjects-solid.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdLiveobjectsSolid=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M7 2a5 5 0 0 0-5 5v10a5 5 0 0 0 5 5h10a5 5 0 0 0 5-5V7a5 5 0 0 0-5-5zm9.355 7.197a.75.75 0 0 0-1.238-.847l-3.905 5.707-2.248-2.248a.75.75 0 1 0-1.06 1.06l2.886 2.888a.75.75 0 0 0 1.15-.107z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconGuiProdLiveobjectsSolid);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-livesync-outline.js b/src/components/Icon/glyphs/icon-gui-prod-livesync-outline.js new file mode 100644 index 0000000000..e86ae4c532 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-livesync-outline.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdLivesyncOutline=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeWidth:1.5,d:"M19.703 20H12v-5.884c0-.125.143-.196.242-.12l7.552 5.733a.151.151 0 0 1-.091.271ZM4 10.189v3.773A6.04 6.04 0 0 0 10.038 20H12M4.297 4H12v5.884a.151.151 0 0 1-.242.12L4.206 4.272A.15.15 0 0 1 4.297 4ZM20 13.812v-3.774A6.04 6.04 0 0 0 13.962 4H12"}));const ForwardRef=forwardRef(IconGuiProdLivesyncOutline);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-livesync-solid.js b/src/components/Icon/glyphs/icon-gui-prod-livesync-solid.js new file mode 100644 index 0000000000..73cf8e409c --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-livesync-solid.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdLivesyncSolid=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M20 14.561a.75.75 0 0 1-.75-.75v-3.773a5.29 5.29 0 0 0-5.288-5.288H12.75v5.134a.9.9 0 0 1-1.446.718L3.753 4.868c-.688-.522-.319-1.618.544-1.618h9.665a6.79 6.79 0 0 1 6.788 6.788v3.773a.75.75 0 0 1-.75.75M4 9.438a.75.75 0 0 1 .75.75v3.774a5.29 5.29 0 0 0 5.288 5.288h1.212v-5.134a.901.901 0 0 1 1.446-.718l7.551 5.733c.688.523.319 1.619-.544 1.619h-9.665a6.79 6.79 0 0 1-6.788-6.788v-3.774a.75.75 0 0 1 .75-.75",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconGuiProdLivesyncSolid);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-pubsub-outline.js b/src/components/Icon/glyphs/icon-gui-prod-pubsub-outline.js new file mode 100644 index 0000000000..f731434495 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-pubsub-outline.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdPubsubOutline=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeWidth:1.5,d:"M10.994 3.328 4.106 14.404a.692.692 0 0 0 .587 1.057h7.235c.191 0 .346.155.346.347v4.499c0 .696.913.956 1.28.365l6.889-11.076a.692.692 0 0 0-.588-1.057H12.62a.346.346 0 0 1-.346-.347V3.694c0-.697-.912-.957-1.28-.366Z"}));const ForwardRef=forwardRef(IconGuiProdPubsubOutline);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-pubsub-solid.js b/src/components/Icon/glyphs/icon-gui-prod-pubsub-solid.js new file mode 100644 index 0000000000..9a5853154a --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-pubsub-solid.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdPubsubSolid=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M10.357 2.932c.766-1.232 2.667-.69 2.667.762v4.095h6.831a1.442 1.442 0 0 1 1.225 2.204L14.19 21.068c-.766 1.232-2.667.69-2.667-.761v-4.095h-6.83a1.442 1.442 0 0 1-1.225-2.204z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconGuiProdPubsubSolid);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-spaces-outline.js b/src/components/Icon/glyphs/icon-gui-prod-spaces-outline.js new file mode 100644 index 0000000000..d3b5df6730 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-spaces-outline.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdSpacesOutline=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeWidth:1.5,d:"M8.262 19.89 4.292 5.543a1.018 1.018 0 0 1 1.252-1.252l14.345 3.97c.941.26 1.011 1.568.104 1.928l-6.475 2.567a1.36 1.36 0 0 0-.761.761l-2.567 6.475c-.36.907-1.667.837-1.928-.104Z"}));const ForwardRef=forwardRef(IconGuiProdSpacesOutline);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-prod-spaces-solid.js b/src/components/Icon/glyphs/icon-gui-prod-spaces-solid.js new file mode 100644 index 0000000000..1e5e825d60 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-prod-spaces-solid.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiProdSpacesSolid=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",d:"M3.569 5.744C3.203 4.42 4.42 3.203 5.744 3.57l14.345 3.97c1.634.453 1.756 2.723.18 3.348l-6.474 2.567a.6.6 0 0 0-.34.34l-2.568 6.476c-.625 1.575-2.895 1.453-3.347-.18z"}));const ForwardRef=forwardRef(IconGuiProdSpacesSolid);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-gui-resources.js b/src/components/Icon/glyphs/icon-gui-resources.js new file mode 100644 index 0000000000..79ef55a845 --- /dev/null +++ b/src/components/Icon/glyphs/icon-gui-resources.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconGuiResources=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M5 1.5c-.69 0-1.25.56-1.25 1.25v1H5a.75.75 0 0 1 0 1.5H3.75v1.5H5a.75.75 0 0 1 0 1.5H3.75v1.5H5a.75.75 0 0 1 0 1.5H3.75v1.5H5a.75.75 0 0 1 0 1.5H3.75v1.5H5a.75.75 0 0 1 0 1.5H3.75v1.5H5a.75.75 0 0 1 0 1.5H3.75v1c0 .69.56 1.25 1.25 1.25h13.5c.69 0 1.25-.56 1.25-1.25v-1.5H12A2.75 2.75 0 0 1 9.25 17v-5A2.75 2.75 0 0 1 12 9.25h7.75v-5.5A2.25 2.25 0 0 0 17.5 1.5zM2.25 2.75v1H1a.75.75 0 0 0 0 1.5h1.25v1.5H1a.75.75 0 0 0 0 1.5h1.25v1.5H1a.75.75 0 0 0 0 1.5h1.25v1.5H1a.75.75 0 0 0 0 1.5h1.25v1.5H1a.75.75 0 0 0 0 1.5h1.25v1.5H1a.75.75 0 0 0 0 1.5h1.25v1A2.75 2.75 0 0 0 5 24h13.5a2.75 2.75 0 0 0 2.75-2.75v-1.511A2.75 2.75 0 0 0 23.75 17v-5a2.75 2.75 0 0 0-2.5-2.739V3.75A3.75 3.75 0 0 0 17.5 0H5a2.75 2.75 0 0 0-2.75 2.75m18.25 8H12c-.69 0-1.25.56-1.25 1.25v5c0 .69.56 1.25 1.25 1.25h9c.69 0 1.25-.56 1.25-1.25v-5c0-.69-.56-1.25-1.25-1.25zm-8-6.5a1.5 1.5 0 0 1 1.5-1.5h3a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-3a1.5 1.5 0 0 1-1.5-1.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm1.5 9.508 1.975 1.241L15.5 15.74zm2.542 2.066a.97.97 0 0 0 0-1.65l-2.024-1.272c-.641-.402-1.518.034-1.518.825v2.545c0 .79.877 1.227 1.518.824z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconGuiResources);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-ai-transport-mono.js b/src/components/Icon/glyphs/icon-product-ai-transport-mono.js new file mode 100644 index 0000000000..394752128b --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-ai-transport-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductAiTransportMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:6,d:"M74 4a5 5 0 0 1 4.808 3.626l5.42 18.975a25 25 0 0 0 17.171 17.17l18.975 5.421a5 5 0 0 1 0 9.616l-18.975 5.42A25 25 0 0 0 84.229 81.4l-5.421 18.975a5 5 0 0 1-9.616 0l-5.42-18.975A25 25 0 0 0 46.6 64.229l-18.975-5.421a5 5 0 0 1 0-9.616l18.975-5.42A25 25 0 0 0 63.771 26.6l5.421-18.975A5 5 0 0 1 74 4",clipRule:"evenodd"}),React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:6,d:"m47 95-26 26M31 84 17 98"}));const ForwardRef=forwardRef(IconProductAiTransportMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-ai-transport.js b/src/components/Icon/glyphs/icon-product-ai-transport.js new file mode 100644 index 0000000000..63e77b601b --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-ai-transport.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductAiTransport=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_transport",x1:74,x2:74,y1:4,y2:104,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FF5416"}),React.createElement("stop",{offset:1,stopColor:"#FF2739"}))),React.createElement("path",{fill:"url(#paint0_linear_transport)",fillRule:"evenodd",d:"M74 4a5 5 0 0 1 4.808 3.626l5.42 18.975a25 25 0 0 0 17.171 17.17l18.975 5.421a5 5 0 0 1 0 9.616l-18.975 5.42A25 25 0 0 0 84.229 81.4l-5.421 18.975a5 5 0 0 1-9.616 0l-5.42-18.975A25 25 0 0 0 46.6 64.229l-18.975-5.421a5 5 0 0 1 0-9.616l18.975-5.42A25 25 0 0 0 63.771 26.6l5.421-18.975A5 5 0 0 1 74 4",clipRule:"evenodd"}),React.createElement("path",{stroke:"url(#paint0_linear_transport)",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:6,d:"m47 95-26 26M31 84 17 98"}));const ForwardRef=forwardRef(IconProductAiTransport);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-chat-mono.js b/src/components/Icon/glyphs/icon-product-chat-mono.js new file mode 100644 index 0000000000..fe524b690a --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-chat-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductChatMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeWidth:6,d:"M38 11h52c13.255 0 24 10.745 24 24v70.41c0 5.213-6.19 7.946-10.043 4.434l-22.812-20.8A4 4 0 0 0 78.45 88H38c-13.255 0-24-10.745-24-24V35c0-13.255 10.745-24 24-24Z"}));const ForwardRef=forwardRef(IconProductChatMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-chat.js b/src/components/Icon/glyphs/icon-product-chat.js new file mode 100644 index 0000000000..d673d435cd --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-chat.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductChat=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_3636_438",x1:64,x2:64,y1:29.85,y2:109.883,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FF5416"}),React.createElement("stop",{offset:1,stopColor:"#FF2739"}))),React.createElement("path",{fill:"url(#paint0_linear_3636_438)",fillRule:"evenodd",d:"M11.5 35c0-12.426 10.074-22.5 22.5-22.5h60c12.426 0 22.5 10.074 22.5 22.5v74.41c0 7.385-8.77 11.257-14.227 6.281l-22.812-20.8a1.5 1.5 0 0 0-1.01-.391H34c-12.426 0-22.5-10.074-22.5-22.5z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconProductChat);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-liveobjects-mono.js b/src/components/Icon/glyphs/icon-product-liveobjects-mono.js new file mode 100644 index 0000000000..4350ee9419 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-liveobjects-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductLiveobjectsMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("g",{stroke:"currentColor",strokeLinecap:"round",strokeWidth:6},React.createElement("path",{d:"M76 11h17c13.255 0 24 10.745 24 24v17M52 11H35c-13.255 0-24 10.745-24 24v17m0 24v17c0 13.255 10.745 24 24 24h17m65-41v17c0 13.255-10.745 24-24 24H76"}),React.createElement("path",{strokeLinejoin:"round",d:"m43 66 17 17 26-38"})));const ForwardRef=forwardRef(IconProductLiveobjectsMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-liveobjects.js b/src/components/Icon/glyphs/icon-product-liveobjects.js new file mode 100644 index 0000000000..87967da91d --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-liveobjects.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductLiveobjects=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_3807_520",x1:64,x2:64,y1:27.103,y2:110.608,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FF5416"}),React.createElement("stop",{offset:1,stopColor:"#FF2739"}))),React.createElement("rect",{width:110,height:110,x:9,y:9,fill:"url(#paint0_linear_3807_520)",rx:26}),React.createElement("path",{stroke:"#fff",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:5,d:"m43 66 17 17 26-38"}));const ForwardRef=forwardRef(IconProductLiveobjects);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-livesync-mono.js b/src/components/Icon/glyphs/icon-product-livesync-mono.js new file mode 100644 index 0000000000..94f9413691 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-livesync-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductLivesyncMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeWidth:6,d:"M115.029 117H64V78.015a1 1 0 0 1 1.605-.797l50.029 37.986c.763.579.353 1.796-.605 1.796ZM11 52v25c0 22.091 17.909 40 40 40h13M12.97 11H64v38.985a1 1 0 0 1-1.605.797L12.366 12.797c-.763-.58-.353-1.797.605-1.797ZM117 76V51c0-22.091-17.909-40-40-40H64"}));const ForwardRef=forwardRef(IconProductLivesyncMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-livesync.js b/src/components/Icon/glyphs/icon-product-livesync.js new file mode 100644 index 0000000000..d3f3b64456 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-livesync.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductLivesync=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_3807_387",x1:64,x2:64,y1:101.232,y2:16.969,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FF5416"}),React.createElement("stop",{offset:1,stopColor:"#FF2739"}))),React.createElement("path",{fill:"url(#paint0_linear_3807_387)",fillRule:"evenodd",d:"M13.5 52a2.5 2.5 0 0 0-5 0v25c0 23.472 19.028 42.5 42.5 42.5h64.029c3.353 0 4.787-4.26 2.117-6.288l-50.03-37.985c-2.304-1.75-5.616-.106-5.616 2.788V114.5H51c-20.71 0-37.5-16.79-37.5-37.5zm53-2.015c0 2.894-3.312 4.538-5.617 2.788L10.854 14.788C8.184 12.76 9.618 8.5 12.971 8.5H77c23.472 0 42.5 19.028 42.5 42.5v25a2.5 2.5 0 1 1-5 0V51c0-20.71-16.79-37.5-37.5-37.5H66.5z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconProductLivesync);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-platform-mono.js b/src/components/Icon/glyphs/icon-product-platform-mono.js new file mode 100644 index 0000000000..4a6eb3dfd9 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-platform-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductPlatformMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:20,height:20,fill:"none",viewBox:"0 0 20 20",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",d:"M5.358 8.125 1.874 10l3.483 1.875m0-3.75 4.642 2.5 4.643-2.5m-9.285 0L1.874 6.25 10 1.875l8.125 4.375-3.482 1.875m-9.285 3.75L1.874 13.75 10 18.125l8.125-4.375-3.482-1.875m-9.285 0 4.642 2.5 4.643-2.5m0-3.75L18.125 10l-3.482 1.875"}));const ForwardRef=forwardRef(IconProductPlatformMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-platform.js b/src/components/Icon/glyphs/icon-product-platform.js new file mode 100644 index 0000000000..75299e5982 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-platform.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductPlatform=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:20,height:20,fill:"none",viewBox:"0 0 20 20",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#ff5416",d:"M9.703 1.325a.63.63 0 0 1 .594 0L18.422 5.7a.624.624 0 0 1 0 1.1l-8.126 4.375a.63.63 0 0 1-.593 0L1.578 6.8a.625.625 0 0 1 0-1.1z"}),React.createElement("path",{fill:"#ff5416",d:"m2.72 8.835 6.39 3.44a1.88 1.88 0 0 0 1.78 0l6.39-3.44 1.14.615a.625.625 0 0 1 0 1.1l-8.124 4.375a.63.63 0 0 1-.592 0L1.58 10.55a.626.626 0 0 1 0-1.1z"}),React.createElement("path",{fill:"#ff5416",d:"m9.11 16.026-6.39-3.442-1.14.616a.625.625 0 0 0 0 1.1l8.124 4.375c.184.1.408.1.592 0L18.42 14.3a.626.626 0 0 0 0-1.1l-1.142-.615-6.39 3.442a1.88 1.88 0 0 1-1.778-.001"}));const ForwardRef=forwardRef(IconProductPlatform);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-pubsub-mono.js b/src/components/Icon/glyphs/icon-product-pubsub-mono.js new file mode 100644 index 0000000000..5b1778052e --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-pubsub-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductPubsubMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeWidth:6,d:"M56.604 13.893 16.802 77.888C15.145 80.552 17.061 84 20.2 84H62a2 2 0 0 1 2 2v25.995c0 4.023 5.272 5.529 7.397 2.112l39.802-63.994c1.657-2.665-.259-6.113-3.397-6.113H66a2 2 0 0 1-2-2V16.005c0-4.023-5.271-5.529-7.396-2.112Z"}));const ForwardRef=forwardRef(IconProductPubsubMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-pubsub.js b/src/components/Icon/glyphs/icon-product-pubsub.js new file mode 100644 index 0000000000..6a9e83fb95 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-pubsub.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductPubsub=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_3807_142",x1:64,x2:64,y1:27.434,y2:110.189,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FF5416"}),React.createElement("stop",{offset:1,stopColor:"#FF2739"}))),React.createElement("path",{fill:"url(#paint0_linear_3807_142)",fillRule:"evenodd",d:"M54.48 12.572c3.453-5.551 12.02-3.105 12.02 3.433V41.5h41.302c5.099 0 8.212 5.603 5.519 9.933L73.52 115.428c-3.453 5.551-12.02 3.105-12.02-3.433V86.5H20.198c-5.099 0-8.212-5.603-5.52-9.933z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconProductPubsub);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-spaces-mono.js b/src/components/Icon/glyphs/icon-product-spaces-mono.js new file mode 100644 index 0000000000..4d9171d1b8 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-spaces-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductSpacesMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{stroke:"currentColor",strokeWidth:6,d:"m41.23 109.769-23.404-84.56c-1.244-4.494 2.889-8.627 7.383-7.383l84.56 23.405c5.545 1.535 5.96 9.239.611 11.36L72.213 67.726a8 8 0 0 0-4.487 4.487L52.59 110.38c-2.121 5.349-9.825 4.934-11.36-.611Z"}));const ForwardRef=forwardRef(IconProductSpacesMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-product-spaces.js b/src/components/Icon/glyphs/icon-product-spaces.js new file mode 100644 index 0000000000..e3d7f71960 --- /dev/null +++ b/src/components/Icon/glyphs/icon-product-spaces.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconProductSpaces=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:128,height:128,fill:"none",viewBox:"0 0 128 128",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_3807_221",x1:65.785,x2:65.785,y1:31.716,y2:108.821,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FF5416"}),React.createElement("stop",{offset:1,stopColor:"#FF2739"}))),React.createElement("path",{fill:"url(#paint0_linear_3807_221)",d:"M15.318 25.777c-1.762-6.366 4.093-12.221 10.46-10.46l84.559 23.406c7.856 2.174 8.443 13.088.866 16.093L73.036 69.951a5.5 5.5 0 0 0-3.085 3.085l-15.135 38.167c-3.005 7.577-13.919 6.99-16.093-.866z"}));const ForwardRef=forwardRef(IconProductSpaces);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-social-discord-mono.js b/src/components/Icon/glyphs/icon-social-discord-mono.js new file mode 100644 index 0000000000..de97067081 --- /dev/null +++ b/src/components/Icon/glyphs/icon-social-discord-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconSocialDiscordMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",d:"M20.317 4.607a19.6 19.6 0 0 0-4.885-1.542.074.074 0 0 0-.079.038c-.21.382-.444.88-.608 1.271a18 18 0 0 0-5.487 0c-.163-.4-.406-.89-.617-1.271a.08.08 0 0 0-.079-.038c-1.714.3-3.354.827-4.885 1.542a.07.07 0 0 0-.032.028C.533 9.364-.32 13.976.099 18.532a.08.08 0 0 0 .031.057 19.8 19.8 0 0 0 5.993 3.082.08.08 0 0 0 .084-.028c.462-.642.874-1.318 1.226-2.03a.08.08 0 0 0-.041-.107A13 13 0 0 1 5.52 18.6a.08.08 0 0 1-.008-.13q.19-.145.372-.297a.07.07 0 0 1 .078-.01c3.927 1.824 8.18 1.824 12.061 0a.07.07 0 0 1 .079.01q.18.15.372.297a.08.08 0 0 1-.006.13q-.895.531-1.873.906a.08.08 0 0 0-.041.109c.36.71.772 1.386 1.225 2.028a.075.075 0 0 0 .084.029 19.7 19.7 0 0 0 6.002-3.082.08.08 0 0 0 .032-.056c.5-5.267-.838-9.842-3.549-13.897a.06.06 0 0 0-.031-.03M8.02 15.757c-1.182 0-2.157-1.104-2.157-2.46s.956-2.46 2.157-2.46c1.21 0 2.176 1.113 2.157 2.46 0 1.356-.956 2.46-2.157 2.46m7.975 0c-1.183 0-2.157-1.104-2.157-2.46s.955-2.46 2.157-2.46c1.21 0 2.176 1.113 2.157 2.46 0 1.356-.946 2.46-2.157 2.46"}));const ForwardRef=forwardRef(IconSocialDiscordMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-social-github-mono.js b/src/components/Icon/glyphs/icon-social-github-mono.js new file mode 100644 index 0000000000..4b3987968e --- /dev/null +++ b/src/components/Icon/glyphs/icon-social-github-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconSocialGithubMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M12 0C5.4 0 0 5.42 0 12.043c0 5.32 3.4 9.836 8.2 11.441.6.1.8-.3.8-.602v-2.007c-3.3.702-4-1.606-4-1.606-.5-1.405-1.3-1.806-1.3-1.806-1.1-.703.1-.703.1-.703 1.2.1 1.8 1.204 1.8 1.204 1.1 1.807 2.8 1.305 3.5 1.004.1-.803.4-1.305.8-1.606-2.8-.3-5.6-1.304-5.6-5.92 0-1.306.5-2.41 1.2-3.212 0-.402-.5-1.606.2-3.212 0 0 1-.301 3.3 1.204 1-.3 2-.401 3-.401s2 .1 3 .401c2.3-1.505 3.3-1.204 3.3-1.204.7 1.706.2 2.91.1 3.212.8.802 1.2 1.906 1.2 3.211 0 4.617-2.8 5.62-5.5 5.921.4.402.8 1.104.8 2.208v3.312c0 .301.2.703.8.602 4.9-1.606 8.3-6.122 8.3-11.44C24 5.418 18.6 0 12 0",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconSocialGithubMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-social-github.js b/src/components/Icon/glyphs/icon-social-github.js new file mode 100644 index 0000000000..7a8eab4830 --- /dev/null +++ b/src/components/Icon/glyphs/icon-social-github.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconSocialGithub=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#191717",fillRule:"evenodd",d:"M12 0C5.4 0 0 5.42 0 12.043c0 5.32 3.4 9.836 8.2 11.441.6.1.8-.3.8-.602v-2.007c-3.3.702-4-1.606-4-1.606-.5-1.405-1.3-1.806-1.3-1.806-1.1-.703.1-.703.1-.703 1.2.1 1.8 1.204 1.8 1.204 1.1 1.807 2.8 1.305 3.5 1.004.1-.803.4-1.305.8-1.606-2.8-.3-5.6-1.304-5.6-5.92 0-1.306.5-2.41 1.2-3.212 0-.402-.5-1.606.2-3.212 0 0 1-.301 3.3 1.204 1-.3 2-.401 3-.401s2 .1 3 .401c2.3-1.505 3.3-1.204 3.3-1.204.7 1.706.2 2.91.1 3.212.8.802 1.2 1.906 1.2 3.211 0 4.617-2.8 5.62-5.5 5.921.4.402.8 1.104.8 2.208v3.312c0 .301.2.703.8.602 4.9-1.606 8.3-6.122 8.3-11.44C24 5.418 18.6 0 12 0",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconSocialGithub);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-social-linkedin-mono.js b/src/components/Icon/glyphs/icon-social-linkedin-mono.js new file mode 100644 index 0000000000..c33a2be54b --- /dev/null +++ b/src/components/Icon/glyphs/icon-social-linkedin-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconSocialLinkedinMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",d:"M24 1.765v20.47A1.764 1.764 0 0 1 22.235 24H1.765A1.765 1.765 0 0 1 0 22.235V1.765A1.765 1.765 0 0 1 1.765 0h20.47A1.765 1.765 0 0 1 24 1.765M7.059 9.176h-3.53v11.295h3.53zm.317-3.882a2.033 2.033 0 0 0-2.018-2.047h-.064a2.047 2.047 0 1 0 0 4.094 2.033 2.033 0 0 0 2.082-1.983zm13.095 8.315c0-3.395-2.16-4.715-4.306-4.715a4.02 4.02 0 0 0-3.572 1.821h-.099V9.176H9.176v11.295h3.53v-6.007a2.344 2.344 0 0 1 2.117-2.527h.135c1.122 0 1.955.705 1.955 2.484v6.05h3.53z"}));const ForwardRef=forwardRef(IconSocialLinkedinMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-social-stackoverflow-mono.js b/src/components/Icon/glyphs/icon-social-stackoverflow-mono.js new file mode 100644 index 0000000000..023088a411 --- /dev/null +++ b/src/components/Icon/glyphs/icon-social-stackoverflow-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconSocialStackoverflowMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",fillRule:"evenodd",d:"M19.01 15.47h2.126V24H2v-8.53h2.126v6.397H19.01z",clipRule:"evenodd"}),React.createElement("path",{fill:"currentColor",d:"m6.446 14.83 10.437 2.21.445-2.093-10.438-2.21zM7.818 9.83l9.665 4.517.908-1.939L8.727 7.89zm2.687-4.77 8.195 6.844 1.373-1.648-8.196-6.843zM15.8 0l-1.72 1.28 6.359 8.588 1.72-1.28zM6.252 19.735h10.631v-2.132H6.253z"}));const ForwardRef=forwardRef(IconSocialStackoverflowMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-social-x-mono.js b/src/components/Icon/glyphs/icon-social-x-mono.js new file mode 100644 index 0000000000..3f6f30e3b3 --- /dev/null +++ b/src/components/Icon/glyphs/icon-social-x-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconSocialXMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",d:"M17.729 2h3.257l-7.116 8.133L22.24 21.2h-6.554l-5.134-6.712L4.679 21.2h-3.26l7.612-8.699L1 2h6.721l4.64 6.135zm-1.143 17.25h1.804L6.74 3.847H4.804z"}));const ForwardRef=forwardRef(IconSocialXMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-social-youtube-mono.js b/src/components/Icon/glyphs/icon-social-youtube-mono.js new file mode 100644 index 0000000000..a28a7b70a0 --- /dev/null +++ b/src/components/Icon/glyphs/icon-social-youtube-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconSocialYoutubeMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:24,height:24,fill:"none",viewBox:"0 0 24 24",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",d:"M23.499 6.631a3.01 3.01 0 0 0-2.122-2.128C19.505 4 12 4 12 4s-7.505 0-9.377.503A3.01 3.01 0 0 0 .502 6.631C0 8.51 0 12.425 0 12.425s0 3.917.502 5.795a3.01 3.01 0 0 0 2.121 2.128c1.872.503 9.377.503 9.377.503s7.505 0 9.377-.503a3.01 3.01 0 0 0 2.122-2.128C24 16.342 24 12.426 24 12.426s0-3.917-.502-5.795"}),React.createElement("path",{fill:"#fff",d:"m9.545 15.982 6.273-3.556-6.273-3.557z"}));const ForwardRef=forwardRef(IconSocialYoutubeMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-android-full.js b/src/components/Icon/glyphs/icon-tech-android-full.js new file mode 100644 index 0000000000..dc8461d718 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-android-full.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechAndroidFull=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#ABC43E",fillRule:"evenodd",d:"M29.711 6.88c.84 0 1.52.664 1.52 1.484s-.68 1.484-1.52 1.484c-.838 0-1.52-.665-1.52-1.484 0-.82.682-1.485 1.52-1.485m-11.423 0c.839 0 1.52.664 1.52 1.484s-.681 1.484-1.52 1.484c-.84 0-1.52-.665-1.52-1.484 0-.82.68-1.485 1.52-1.485m23.744 8.425c1.633 0 2.968 1.303 2.968 2.897v11.59c0 1.594-1.335 2.897-2.968 2.897-1.632 0-2.968-1.303-2.968-2.897v-11.59c0-1.594 1.336-2.897 2.968-2.897m-36.065 0c1.632 0 2.967 1.303 2.967 2.897v11.59c0 1.594-1.335 2.897-2.967 2.897S3 31.386 3 29.792v-11.59c0-1.594 1.335-2.897 2.967-2.897m15.624 23.357v6.44c0 1.594-1.335 2.898-2.968 2.898-1.632 0-2.967-1.304-2.967-2.898v-6.44h-2.49c-1.32 0-2.402-1.056-2.402-2.346v-21.11h26.47v21.11c0 1.29-1.08 2.346-2.402 2.346h-2.49v6.44c0 1.594-1.334 2.898-2.966 2.898s-2.967-1.304-2.967-2.898v-6.44zm2.408-35.705c1.998 0 3.89.358 5.589 1l2.5-3.759a.453.453 0 0 1 .617-.128.43.43 0 0 1 .13.604l-2.412 3.628c3.992 1.8 6.712 5.224 6.79 9.166H10.786c.078-3.942 2.798-7.366 6.79-9.166L15.164.674a.43.43 0 0 1 .131-.604.453.453 0 0 1 .618.128l2.5 3.759c1.697-.641 3.59-1 5.587-1",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconTechAndroidFull);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-claude-mono.js b/src/components/Icon/glyphs/icon-tech-claude-mono.js new file mode 100644 index 0000000000..ff92839dc4 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-claude-mono.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechClaudeMono=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:45,height:44,fill:"none",viewBox:"0 0 45 44",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"currentColor",d:"m9.28 29.252 8.677-4.853.145-.423-.145-.234h-.425l-1.451-.09-4.959-.133-4.3-.178-4.165-.222-1.05-.223-.983-1.291.1-.646.883-.59 1.262.112 2.792.189 4.188.29 3.037.177 4.501.468h.715l.1-.29-.245-.178-.19-.178-4.333-2.927-4.69-3.094-2.458-1.781-1.329-.902-.67-.846-.29-1.848 1.206-1.324 1.62.111.412.111 1.642 1.258 3.507 2.705 4.579 3.361.67.557.268-.19.033-.133-.301-.5-2.49-4.486-2.658-4.564L11.3 4.575l-.313-1.136c-.112-.467-.19-.857-.19-1.335l1.374-1.86.76-.244 1.83.245.771.668 1.14 2.593 1.842 4.085 2.86 5.555.837 1.647.447 1.525.167.467h.29v-.267l.235-3.128.436-3.84.424-4.942.145-1.391.692-1.67 1.374-.901 1.072.512.883 1.257-.123.813-.525 3.395-1.028 5.32-.67 3.562h.391l.447-.445 1.81-2.393 3.037-3.785 1.34-1.502L34.62 5.72l1.005-.79h1.898L38.92 7l-.625 2.137-1.955 2.471-1.619 2.093-2.323 3.117-1.452 2.493.134.2.346-.033 5.25-1.113 2.836-.512 3.384-.579 1.53.712.168.724-.604 1.48-3.618.89-4.244.847-6.321 1.491-.078.056.09.111 2.847.267 1.217.067h2.982l5.55.412 1.453.957.87 1.169-.145.89-2.233 1.136-3.016-.713-7.035-1.67-2.413-.6h-.335v.2l2.01 1.959 3.686 3.317 4.612 4.274.235 1.058-.592.834-.625-.089-4.054-3.038-1.564-1.37-3.54-2.971h-.235v.311l.816 1.191 4.31 6.456.224 1.981-.313.646-1.117.39-1.228-.223-2.524-3.528-2.602-3.974-2.1-3.562-.257.145-1.24 13.301-.58.679-1.34.512-1.117-.846-.592-1.369.592-2.705.715-3.528.58-2.805.525-3.484.313-1.158-.022-.078-.257.034-2.636 3.606-4.01 5.398-3.17 3.384-.76.3-1.318-.678.123-1.213.737-1.08 4.389-5.565 2.647-3.451 1.708-1.992-.01-.29h-.101l-11.66 7.547-2.077.267-.893-.835.111-1.369.425-.445L9.29 29.24z"}));const ForwardRef=forwardRef(IconTechClaudeMono);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-csharp.js b/src/components/Icon/glyphs/icon-tech-csharp.js new file mode 100644 index 0000000000..bd77e4d866 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-csharp.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechCsharp=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("clipPath",{id:"clip0_1031_1911"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_1031_1911)"},React.createElement("path",{fill:"#9A4993",d:"M45.68 14.1c0-.801-.172-1.524-.526-2.13q-.513-.906-1.539-1.498C37.935 7.198 32.242 3.924 26.562.65c-1.539-.88-3.011-.855-4.536.04C19.764 2.017 8.43 8.512 5.064 10.471 3.671 11.274 3 12.51 3 14.114v19.775c0 .789.17 1.486.5 2.09.342.619.867 1.145 1.564 1.552 3.38 1.96 14.7 8.441 16.962 9.782 1.525.894 3.01.934 4.536.04 5.68-3.287 11.373-6.548 17.053-9.822.71-.407 1.223-.92 1.565-1.551.328-.605.5-1.302.5-2.091z"}),React.createElement("path",{fill:"#6A1577",d:"M24.393 23.922 3.473 35.966c.343.618.868 1.144 1.565 1.552 3.38 1.959 14.7 8.44 16.962 9.782 1.525.894 3.01.934 4.536.04 5.68-3.288 11.373-6.548 17.053-9.822.71-.408 1.223-.92 1.565-1.552z"}),React.createElement("path",{fill:"#6A1577",d:"M18.2 27.486a7.06 7.06 0 0 0 6.126 3.563 7.04 7.04 0 0 0 6.154-3.603l-6.088-3.524z"}),React.createElement("path",{fill:"#813084",d:"M45.68 14.1c0-.801-.172-1.524-.527-2.13l-20.76 11.952L45.18 35.967c.328-.604.5-1.301.5-2.09z"}),React.createElement("path",{fill:"#fff",fillRule:"evenodd",d:"M30.48 27.446a7.07 7.07 0 0 1-6.153 3.603 7.03 7.03 0 0 1-6.127-3.563 7.054 7.054 0 0 1 12.228-7.034l6.166-3.55c-2.459-4.234-7.034-7.088-12.28-7.088-7.837 0-14.174 6.351-14.174 14.174 0 2.564.683 4.984 1.88 7.061a14.2 14.2 0 0 0 12.307 7.113c5.272 0 9.874-2.88 12.32-7.152zm7.797-6.811H36.87v6.797h1.407zm1.71 0h1.406v6.797h-1.407z",clipRule:"evenodd"}),React.createElement("path",{fill:"#fff",fillRule:"evenodd",d:"M35.74 21.779h6.797v1.407h-6.798zm0 3.103h6.797v1.407h-6.798z",clipRule:"evenodd"})));const ForwardRef=forwardRef(IconTechCsharp);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-flutter.js b/src/components/Icon/glyphs/icon-tech-flutter.js new file mode 100644 index 0000000000..39b383c37b --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-flutter.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechFlutter=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_1031_1893",x1:25.645,x2:26.676,y1:39.281,y2:40.312,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{offset:.2,stopOpacity:.15}),React.createElement("stop",{offset:.85,stopColor:"#616161",stopOpacity:.01})),React.createElement("linearGradient",{id:"paint1_linear_1031_1893",x1:22.466,x2:33.433,y1:42.463,y2:42.463,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{offset:.2,stopOpacity:.55}),React.createElement("stop",{offset:.85,stopColor:"#616161",stopOpacity:.01})),React.createElement("clipPath",{id:"clip0_1031_1893"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_1031_1893)"},React.createElement("path",{fill:"#0D47A1",d:"M22.463 42.463 28 48h14.781L29.855 35.074"}),React.createElement("path",{fill:"#42A5F5",fillOpacity:.8,fillRule:"evenodd",d:"m4 23.998 7.392 7.392L42.78 0H28zm24-1.853h14.781L29.855 35.073l-7.392-7.391z",clipRule:"evenodd"}),React.createElement("path",{fill:"#42A5F5",d:"m15.09 35.077 7.38-7.382 7.379 7.38-7.38 7.382z"}),React.createElement("path",{fill:"url(#paint0_linear_1031_1893)",d:"m22.47 42.458 7.379-7.38 1.03 1.03-7.38 7.38z"}),React.createElement("path",{fill:"url(#paint1_linear_1031_1893)",d:"m22.463 42.463 10.967-3.789-3.575-3.602"})));const ForwardRef=forwardRef(IconTechFlutter);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-go.js b/src/components/Icon/glyphs/icon-tech-go.js new file mode 100644 index 0000000000..53b6091cf9 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-go.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechGo=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#00ACD7",fillRule:"evenodd",d:"M5.75 24.73c-.094 0-.118-.07-.07-.14l.326-.585c.047-.07.14-.14.234-.14h4.674c.094 0 .14.07.14.163l-.046.561c0 .094-.094.164-.164.164zM.093 22.58c-.093 0-.116-.047-.07-.117l.491-.63c.047-.07.164-.118.257-.118h10.657c.094 0 .14.07.117.14l-.187.561c-.023.094-.117.14-.21.14zM3.622 20.43c-.093 0-.117-.047-.07-.117l.491-.631c.047-.07.164-.117.257-.117h8.343c.094 0 .117.07.07.14l-.397.608c-.047.07-.164.14-.234.14zM30.008 20.01c-1.473.373-2.478.654-3.927 1.028-.35.093-.374.116-.677-.234-.351-.397-.608-.654-1.099-.888-1.472-.725-2.898-.514-4.23.35-1.59 1.029-2.407 2.548-2.384 4.44.024 1.87 1.309 3.413 3.155 3.67 1.59.21 2.922-.35 3.973-1.543.21-.257.398-.537.631-.864h-4.51c-.491 0-.608-.304-.444-.701.303-.725.864-1.94 1.192-2.548a.63.63 0 0 1 .584-.374h8.507c-.047.631-.047 1.262-.14 1.893-.258 1.683-.889 3.226-1.917 4.58-1.683 2.221-3.88 3.6-6.66 3.974-2.29.304-4.417-.14-6.287-1.543-1.73-1.308-2.711-3.038-2.968-5.188-.304-2.547.444-4.837 1.986-6.847 1.66-2.174 3.857-3.553 6.544-4.043 2.197-.398 4.3-.14 6.193 1.145 1.239.818 2.127 1.94 2.711 3.295.14.21.047.327-.233.397",clipRule:"evenodd"}),React.createElement("path",{fill:"#00ACD7",fillRule:"evenodd",d:"M37.743 32.933c-2.126-.047-4.066-.654-5.702-2.057-1.379-1.191-2.244-2.71-2.524-4.51-.42-2.64.304-4.978 1.893-7.058 1.706-2.244 3.762-3.412 6.544-3.903 2.383-.42 4.627-.187 6.66 1.192 1.846 1.262 2.992 2.968 3.295 5.212.398 3.155-.514 5.726-2.687 7.922-1.543 1.566-3.436 2.548-5.61 2.992-.63.117-1.261.14-1.869.21m5.562-9.442c-.023-.303-.023-.537-.07-.77-.42-2.314-2.547-3.623-4.767-3.109-2.174.49-3.576 1.87-4.09 4.067-.42 1.822.467 3.669 2.15 4.417 1.285.56 2.57.49 3.81-.14 1.846-.959 2.85-2.455 2.967-4.465",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconTechGo);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-java.js b/src/components/Icon/glyphs/icon-tech-java.js new file mode 100644 index 0000000000..0541dabf9e --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-java.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechJava=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("clipPath",{id:"clip0_1031_1923"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_1031_1923)",clipRule:"evenodd"},React.createElement("path",{fill:"#0B6FB6",fillRule:"evenodd",d:"M15.487 37.987c.538-.432 1.205-.654 1.86-.842-.211.199-.585.445-.421.784.315.41.9.456 1.368.561 1.976.246 3.975.41 5.951.257 1.496-.093 2.97-.362 4.431-.62.841.492 1.719.912 2.619 1.287-1.38.62-2.865 1.017-4.35 1.298-2.501.444-5.085.502-7.6.105-1.11-.199-2.244-.456-3.25-1.006-.385-.222-.794-.48-.958-.912-.129-.339.081-.7.35-.912"}),React.createElement("path",{fill:"#0B6FB6",fillRule:"evenodd",d:"M8.998 41.296c1.076-.362 2.187-.655 3.321-.69.456-.011.924.012 1.321.246-1.356.082-2.713.42-3.928 1.04-.27.152-.573.328-.679.644 0 .362.41.479.679.596 1.695.631 3.507.83 5.285 1.017 5.296.444 10.627.327 15.924-.152 2-.222 4.01-.444 5.951-.994.62-.21 1.321-.42 1.73-.97.246-.328.083-.737-.163-1.017.397.222.959.502.877 1.04-.152.561-.726.854-1.193 1.123-1.496.76-3.145 1.145-4.782 1.484-4.759.936-9.634 1.146-14.475 1.006A81 81 0 0 1 7.97 44.57c-.643-.152-1.367-.281-1.835-.783-.304-.351-.035-.842.257-1.1.737-.677 1.684-1.052 2.607-1.391"}),React.createElement("path",{fill:"#0B6FB6",fillRule:"evenodd",d:"M38.135 45.295c1.181-.41 2.443-.842 3.32-1.777-.152 1.017-1.029 1.718-1.894 2.174-1.66.866-3.52 1.263-5.366 1.59-3.906.62-7.869.772-11.833.702-2.7-.082-5.425-.187-8.079-.748-.631-.176-1.31-.293-1.859-.667 2.362.398 4.759.562 7.156.655 3.94.117 7.892-.035 11.809-.527 2.268-.315 4.548-.7 6.746-1.402"}),React.createElement("path",{stroke:"#0B6FB6",strokeWidth:.706,d:"M38.135 45.295c1.181-.41 2.443-.842 3.32-1.777-.152 1.017-1.029 1.718-1.894 2.174-1.66.866-3.52 1.263-5.366 1.59-3.906.62-7.869.772-11.833.702-2.7-.082-5.425-.187-8.079-.748-.631-.176-1.31-.293-1.859-.667 2.362.398 4.759.562 7.156.655 3.94.117 7.892-.035 11.809-.527 2.268-.315 4.548-.7 6.746-1.402Z"}),React.createElement("path",{fill:"#0B6FB6",fillRule:"evenodd",d:"M14.13 32.89c.609-.48 1.334-.748 2.059-1.006-.234.28-.561.55-.585.936-.012.245.188.432.386.55.386.198.818.268 1.251.326 2.245.234 4.513.363 6.782.211 2.069-.105 4.127-.444 6.173-.807.561.526 1.227.889 1.93 1.204-2.468.725-5.005 1.146-7.554 1.345-2.408.164-4.852.152-7.25-.234-.946-.175-1.916-.374-2.781-.818-.351-.187-.749-.433-.854-.842-.07-.35.187-.667.444-.865"}),React.createElement("path",{fill:"#E11F22",fillRule:"evenodd",d:"M28.56 0c1.098 1.251 1.46 3.04 1.145 4.654-.34 1.812-1.345 3.414-2.526 4.793-.946 1.111-2.034 2.093-3.168 3.005-1.321 1.064-2.62 2.21-3.45 3.707-.7 1.216-.864 2.712-.385 4.033.783 2.268 2.607 3.952 3.601 6.092a49 49 0 0 1-3.18-3.04c-1.053-1.123-2.046-2.315-2.701-3.718-.444-.982-.643-2.128-.304-3.169.339-1.064 1.099-1.917 1.86-2.712 2.115-2.117 4.722-3.672 6.792-5.823 1.146-1.18 2.093-2.595 2.42-4.22.27-1.193.14-2.421-.105-3.602"}),React.createElement("path",{fill:"#E11F22",fillRule:"evenodd",d:"M26.828 13.318c2.269-1.672 4.923-2.736 7.612-3.508-2.127 1.356-4.349 2.595-6.185 4.338-.667.643-1.297 1.38-1.59 2.28-.222.666-.129 1.403.14 2.034.492 1.122 1.321 2.034 1.953 3.063.538.877.631 2 .222 2.947-.62 1.39-1.835 2.408-3.098 3.215-.164.093-.305.269-.503.292.842-.76 1.496-1.835 1.473-2.993-.024-.865-.503-1.625-1.064-2.245-1.157-1.38-2.128-3.133-1.93-4.992.2-1.86 1.532-3.356 2.97-4.431"}),React.createElement("path",{fill:"#0B6FB6",fillRule:"evenodd",d:"M34.873 26.834c.49-.596 1.274-.749 1.987-.842 1.24-.164 2.561.152 3.496 1.005.772.69 1.205 1.766 1.04 2.806-.174 1.205-.981 2.21-1.893 2.97-1.648 1.321-3.683 2.093-5.717 2.607.117-.128.245-.245.397-.339 1.544-.83 3.075-1.8 4.21-3.156.689-.842 1.227-1.941 1.005-3.052-.234-1.286-1.485-2.222-2.76-2.292-.608-.058-1.18.176-1.765.293M11.372 27.898c2.151-.924 4.49-1.52 6.84-1.485-1.368.397-2.771.736-4.057 1.344-.305.164-.655.316-.83.632-.094.245.163.409.35.49.795.328 1.672.375 2.526.457 2.993.199 5.998.082 8.98-.106 2.945-.187 5.869-.584 8.78-1.052-.643.304-1.298.608-1.894.994-.304.21-.678.269-1.017.35-3.578.796-7.25 1.041-10.897 1.1a56 56 0 0 1-4.221-.105c-1.567-.106-3.145-.223-4.677-.632-.445-.129-.912-.269-1.263-.585-.175-.14-.175-.42-.023-.584.386-.398.924-.585 1.403-.819"})));const ForwardRef=forwardRef(IconTechJava);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-javascript.js b/src/components/Icon/glyphs/icon-tech-javascript.js new file mode 100644 index 0000000000..e78b8d407c --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-javascript.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechJavascript=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("clipPath",{id:"clip0_1031_1922"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_1031_1922)"},React.createElement("path",{fill:"#F7DF1E",d:"M46 0H2a2 2 0 0 0-2 2v44a2 2 0 0 0 2 2h44a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2"}),React.createElement("path",{fill:"#000",d:"M32.244 37.5c.967 1.579 2.225 2.74 4.45 2.74 1.868 0 3.062-.935 3.062-2.225 0-1.547-1.227-2.095-3.284-2.995l-1.127-.484c-3.255-1.386-5.417-3.123-5.417-6.796 0-3.382 2.577-5.958 6.605-5.958 2.868 0 4.93.998 6.416 3.612l-3.513 2.255c-.773-1.387-1.607-1.933-2.903-1.933-1.32 0-2.158.838-2.158 1.933 0 1.353.838 1.9 2.773 2.739l1.128.483c3.832 1.643 5.996 3.319 5.996 7.086 0 4.06-3.19 6.285-7.474 6.285-4.19 0-6.896-1.996-8.22-4.612zm-15.934.391c.709 1.257 1.353 2.32 2.903 2.32 1.482 0 2.417-.58 2.417-2.834V22.04h4.51v15.398c0 4.67-2.738 6.796-6.735 6.796-3.612 0-5.703-1.869-6.767-4.12z"})));const ForwardRef=forwardRef(IconTechJavascript);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-json.js b/src/components/Icon/glyphs/icon-tech-json.js new file mode 100644 index 0000000000..c827d37f63 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-json.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechJson=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("clipPath",{id:"clip0_4810_1924"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_4810_1924)"},React.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeWidth:3,d:"M36 2h3a4 4 0 0 1 4 4v12.092a4 4 0 0 0 1.056 2.708l1.761 1.915a2 2 0 0 1 .068 2.63l-1.965 2.37a4 4 0 0 0-.92 2.553V42a4 4 0 0 1-4 4h-3M12 2H9a4 4 0 0 0-4 4v12.092A4 4 0 0 1 3.944 20.8l-1.761 1.915a2 2 0 0 0-.068 2.63l1.965 2.37A4 4 0 0 1 5 30.269V42a4 4 0 0 0 4 4h3"}),React.createElement("path",{fill:"currentColor",d:"M26 41V25.67h3.213l4.263 12.39-.147-1.575a79 79 0 0 1-.105-1.87 39 39 0 0 1-.042-1.68V25.67h2.394V41h-3.213l-4.221-12.39.105 1.47q.063.861.105 1.764t.042 1.659V41zM17.038 41.21q-1.47 0-2.562-.546a4.1 4.1 0 0 1-1.659-1.575q-.567-1.029-.567-2.415v-6.678q0-1.386.567-2.394a3.9 3.9 0 0 1 1.659-1.575q1.092-.567 2.562-.567 1.491 0 2.562.567 1.071.546 1.638 1.575.588 1.008.588 2.373v6.699q0 1.386-.588 2.415a3.95 3.95 0 0 1-1.638 1.575q-1.071.546-2.562.546m0-2.31q1.071 0 1.617-.567.546-.588.546-1.659v-6.678q0-1.092-.546-1.659t-1.617-.567-1.617.567-.546 1.659v6.678q0 1.071.546 1.659.567.567 1.617.567M30.895 22.21q-1.575 0-2.73-.525t-1.785-1.491q-.609-.987-.63-2.31h2.625q0 .945.672 1.491.693.525 1.869.525 1.134 0 1.764-.525.651-.525.651-1.449 0-.777-.462-1.344-.441-.588-1.281-.798l-1.764-.483q-1.806-.462-2.793-1.659-.966-1.197-.966-2.898 0-1.302.588-2.268a3.9 3.9 0 0 1 1.659-1.491q1.092-.525 2.562-.525 2.226 0 3.528 1.155 1.302 1.134 1.323 3.066H33.1q0-.903-.588-1.407-.588-.525-1.659-.525-1.029 0-1.596.483t-.567 1.365q0 .798.42 1.365.441.546 1.26.777l1.827.504q1.827.462 2.793 1.659.966 1.176.966 2.919 0 1.302-.63 2.31-.63.987-1.764 1.533t-2.667.546M17.04 22.23q-2.331 0-3.696-1.28Q12 19.648 12 17.464h2.625q0 1.155.651 1.806.651.65 1.764.65t1.764-.63q.651-.65.651-1.805v-8.38h-3.57V6.67h6.195v10.815q0 2.205-1.365 3.486-1.344 1.26-3.675 1.26"})));const ForwardRef=forwardRef(IconTechJson);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-kotlin.js b/src/components/Icon/glyphs/icon-tech-kotlin.js new file mode 100644 index 0000000000..c5ba99b455 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-kotlin.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechKotlin=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_1031_1914",x1:14.252,x2:58.919,y1:66.464,y2:21.797,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{offset:.108,stopColor:"#C757BC"}),React.createElement("stop",{offset:.173,stopColor:"#CD5CA9"}),React.createElement("stop",{offset:.492,stopColor:"#E8744F"}),React.createElement("stop",{offset:.716,stopColor:"#F88316"}),React.createElement("stop",{offset:.823,stopColor:"#FF8900"})),React.createElement("linearGradient",{id:"paint1_linear_1031_1914",x1:36.994,x2:43.459,y1:62.127,y2:36.495,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{offset:.296,stopColor:"#00AFFF"}),React.createElement("stop",{offset:.694,stopColor:"#5282FF"}),React.createElement("stop",{offset:1,stopColor:"#945DFF"})),React.createElement("linearGradient",{id:"paint2_linear_1031_1914",x1:6.685,x2:22.686,y1:21.279,y2:6.811,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{offset:.296,stopColor:"#00AFFF"}),React.createElement("stop",{offset:.694,stopColor:"#5282FF"}),React.createElement("stop",{offset:1,stopColor:"#945DFF"})),React.createElement("clipPath",{id:"clip0_1031_1914"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_1031_1914)"},React.createElement("path",{fill:"url(#paint0_linear_1031_1914)",d:"M24.1 0 0 25.344V48l24.065-24.107L48 0z"}),React.createElement("path",{fill:"url(#paint1_linear_1031_1914)",d:"m0 48 24.065-24.107L48 48z"}),React.createElement("path",{fill:"url(#paint2_linear_1031_1914)",d:"M0 0h24.1L0 25.344z"})));const ForwardRef=forwardRef(IconTechKotlin);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-laravel-broadcast.js b/src/components/Icon/glyphs/icon-tech-laravel-broadcast.js new file mode 100644 index 0000000000..746af298a3 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-laravel-broadcast.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechLaravelBroadcast=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#FF2D20",fillRule:"evenodd",d:"M47.6 10.859q.027.097.027.196v10.303a.75.75 0 0 1-.378.652l-8.647 4.979v9.868a.75.75 0 0 1-.376.652L20.176 47.9c-.042.023-.087.038-.132.054-.017.006-.033.016-.051.021a.76.76 0 0 1-.385 0c-.02-.006-.04-.017-.06-.024-.04-.015-.084-.029-.123-.051L1.377 37.509A.75.75 0 0 1 1 36.857V5.947q.001-.101.026-.196c.006-.022.02-.042.027-.063.014-.04.027-.08.047-.117.015-.024.035-.044.052-.067.022-.03.041-.06.067-.087.021-.021.05-.037.074-.056q.038-.036.083-.065L10.402.1a.75.75 0 0 1 .75 0l9.025 5.196h.002q.043.03.082.064.04.027.074.056c.026.027.045.058.067.088.016.023.038.043.051.067.022.038.034.077.049.117.007.021.02.04.026.063a.8.8 0 0 1 .026.197v19.305l7.52-4.33v-9.869q0-.1.027-.195c.006-.023.018-.042.026-.064.015-.04.028-.08.049-.116.014-.025.034-.045.05-.067.023-.03.042-.061.068-.087.022-.022.049-.038.073-.057.028-.022.053-.047.083-.065l9.026-5.195a.75.75 0 0 1 .751 0l9.024 5.195c.032.02.056.043.085.064.023.02.05.036.072.057.026.027.045.058.068.088.017.022.037.042.05.067.022.036.034.077.05.116.008.022.02.041.025.064m-1.478 10.064v-8.567l-3.158 1.818-4.362 2.512v8.567l7.521-4.33m-9.024 15.5v-8.574l-4.291 2.45-12.255 6.995v8.654zM2.504 7.247v29.174l16.544 9.525v-8.653l-8.643-4.89-.002-.003-.004-.002c-.03-.017-.054-.041-.081-.062-.024-.018-.05-.033-.071-.054l-.002-.003c-.025-.023-.041-.053-.062-.079-.019-.025-.041-.047-.056-.073l-.002-.003q-.022-.044-.039-.094c-.012-.028-.028-.054-.036-.084v-.001c-.009-.036-.01-.074-.015-.11-.003-.028-.01-.056-.01-.085V11.58L5.661 9.064 2.504 7.25m8.273-5.628L3.258 5.948l7.517 4.328 7.518-4.33-7.518-4.326zm3.91 27.01 4.362-2.51V7.247l-3.158 1.818-4.363 2.512V30.45zM37.851 6.727l-7.518 4.328 7.517 4.328 7.517-4.329zm-.753 9.959-4.362-2.512-3.158-1.818v8.567l4.361 2.511 3.16 1.82zM19.8 35.993l11.028-6.295 5.512-3.146-7.513-4.325-8.65 4.98-7.882 4.538z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconTechLaravelBroadcast);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-nextjs.js b/src/components/Icon/glyphs/icon-tech-nextjs.js new file mode 100644 index 0000000000..c3f7334fbc --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-nextjs.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechNextjs=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_2276_25",x1:29.067,x2:38.533,y1:31.067,y2:42.8,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{offset:1,stopColor:"#fff",stopOpacity:0})),React.createElement("linearGradient",{id:"paint1_linear_2276_25",x1:32.267,x2:32.213,y1:14.4,y2:28.5,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{offset:1,stopColor:"#fff",stopOpacity:0})),React.createElement("clipPath",{id:"clip0_2276_25"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"})),React.createElement("clipPath",{id:"clip1_2276_25"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_2276_25)"},React.createElement("g",{clipPath:"url(#clip1_2276_25)"},React.createElement("mask",{id:"mask0_2276_25",width:48,height:48,x:0,y:0,maskUnits:"userSpaceOnUse",style:{maskType:"alpha"}},React.createElement("path",{fill:"#000",d:"M24 48c13.255 0 24-10.745 24-24S37.255 0 24 0 0 10.745 0 24s10.745 24 24 24"})),React.createElement("g",{mask:"url(#mask0_2276_25)"},React.createElement("path",{fill:"#000",d:"M24 48c13.255 0 24-10.745 24-24S37.255 0 24 0 0 10.745 0 24s10.745 24 24 24"}),React.createElement("path",{fill:"url(#paint0_linear_2276_25)",d:"M39.869 42.005 18.438 14.4H14.4v19.192h3.23v-15.09L37.333 43.96a24 24 0 0 0 2.536-1.954"}),React.createElement("path",{fill:"url(#paint1_linear_2276_25)",d:"M33.867 14.4h-3.2v19.2h3.2z"})))));const ForwardRef=forwardRef(IconTechNextjs);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-nodejs.js b/src/components/Icon/glyphs/icon-tech-nodejs.js new file mode 100644 index 0000000000..0f5face3e3 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-nodejs.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechNodejs=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_1031_1900",x1:24.084,x2:-23.516,y1:-23.765,y2:18.053,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#3E863D"}),React.createElement("stop",{offset:1,stopColor:"#5AAD45"})),React.createElement("linearGradient",{id:"paint1_linear_1031_1900",x1:18.428,x2:-6.405,y1:-5.756,y2:27.603,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#76AC64"}),React.createElement("stop",{offset:.526,stopColor:"#3E863D"}),React.createElement("stop",{offset:1,stopColor:"#3E863D"})),React.createElement("linearGradient",{id:"paint2_linear_1031_1900",x1:3.048,x2:45.13,y1:48.88,y2:48.88,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#6BBF46"}),React.createElement("stop",{offset:1,stopColor:"#3E863D"})),React.createElement("clipPath",{id:"clip0_1031_1900"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{fillRule:"evenodd",clipPath:"url(#clip0_1031_1900)",clipRule:"evenodd"},React.createElement("path",{fill:"url(#paint0_linear_1031_1900)",d:"M22.944.539h.005L4.137 11.397A2.27 2.27 0 0 0 3 13.365v21.732a2.27 2.27 0 0 0 1.137 1.967L22.95 47.932c.703.405 1.57.405 2.273 0l18.81-10.868a2.28 2.28 0 0 0 1.135-1.967V13.365a2.27 2.27 0 0 0-1.139-1.968L25.222.54a2.29 2.29 0 0 0-2.278 0"}),React.createElement("path",{fill:"url(#paint1_linear_1031_1900)",d:"M3.466 36.477c.18.234.405.436.67.588l16.138 9.322 2.688 1.544a2.28 2.28 0 0 0 1.757.216L44.56 11.816a2.3 2.3 0 0 0-.529-.42L31.713 4.283 25.202.536a2.4 2.4 0 0 0-.59-.236z"}),React.createElement("path",{fill:"url(#paint2_linear_1031_1900)",d:"M23.87.248h-.012c-.315.031-.624.13-.908.29L4.19 11.367 24.42 48.21c.281-.04.558-.134.808-.278L44.04 37.064a2.28 2.28 0 0 0 1.09-1.51v-.056L24.517.285a2.4 2.4 0 0 0-.648-.037"})));const ForwardRef=forwardRef(IconTechNodejs);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-objectivec.js b/src/components/Icon/glyphs/icon-tech-objectivec.js new file mode 100644 index 0000000000..bbdfccd1e5 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-objectivec.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechObjectivec=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("clipPath",{id:"clip0_1031_1913"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{fill:"#03020D",clipPath:"url(#clip0_1031_1913)"},React.createElement("path",{d:"M48 45v3H38v-3zM10 45v3H0v-3zM33.068 31.276c-1.728 1.26-3.888 2.088-6.48 2.088-5.508 0-9.54-3.816-9.54-9.144 0-5.4 3.924-9.252 9.252-9.252l7.74 2.376v-5.076c-1.98-.72-4.788-1.476-7.884-1.476-7.74 0-13.752 5.436-13.752 13.428s6.012 13.428 13.752 13.428c3.348 0 6.48-.972 8.532-2.268zM0 3h3v42H0zM48 0v3H38V0zM45 3h3v42h-3zM10 0v3H0V0z"})));const ForwardRef=forwardRef(IconTechObjectivec);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-openai.js b/src/components/Icon/glyphs/icon-tech-openai.js new file mode 100644 index 0000000000..06853e203f --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-openai.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechOpenai=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("g",{clipPath:"url(#a)"},React.createElement("path",{fill:"currentColor",d:"M44.24 19.645a11.96 11.96 0 0 0-1.027-9.821 12.09 12.09 0 0 0-13.027-5.802A11.96 11.96 0 0 0 21.168 0 12.095 12.095 0 0 0 9.631 8.373a11.96 11.96 0 0 0-7.997 5.801 12.1 12.1 0 0 0 1.488 14.181 11.96 11.96 0 0 0 1.027 9.822 12.095 12.095 0 0 0 13.026 5.803 11.96 11.96 0 0 0 9.02 4.02 12.09 12.09 0 0 0 11.54-8.378 11.96 11.96 0 0 0 7.997-5.8 12.1 12.1 0 0 0-1.492-14.177M26.2 44.865a8.97 8.97 0 0 1-5.76-2.082c.074-.04.202-.11.285-.16l9.557-5.52a1.55 1.55 0 0 0 .786-1.362V22.266l4.04 2.333a.14.14 0 0 1 .079.11v11.16a9.007 9.007 0 0 1-8.989 8.995zM6.87 36.61a8.97 8.97 0 0 1-1.073-6.028c.072.043.194.119.284.17l9.557 5.52a1.56 1.56 0 0 0 1.57 0l11.67-6.738V34.2a.14.14 0 0 1-.058.124l-9.662 5.579A9.005 9.005 0 0 1 6.87 36.61M4.357 15.745A8.96 8.96 0 0 1 9.04 11.8c0 .082-.004.228-.004.33v11.04a1.55 1.55 0 0 0 .784 1.36l11.668 6.736L17.45 33.6a.14.14 0 0 1-.137.012L7.648 28.03a9.005 9.005 0 0 1-3.291-12.284Zm33.19 7.725-11.669-6.74 4.04-2.331a.15.15 0 0 1 .136-.012l9.663 5.577a8.998 8.998 0 0 1-1.39 16.235V24.828a1.55 1.55 0 0 0-.78-1.358m4.02-6.052a9 9 0 0 0-.283-.17l-9.558-5.52a1.56 1.56 0 0 0-1.57 0l-11.668 6.737V13.8a.14.14 0 0 1 .058-.124l9.66-5.574a8.996 8.996 0 0 1 13.362 9.316Zm-25.276 8.315-4.04-2.333a.14.14 0 0 1-.079-.11V12.13a8.996 8.996 0 0 1 14.753-6.907q-.143.078-.284.16l-9.558 5.52a1.55 1.55 0 0 0-.785 1.36zM18.486 21l5.196-3 5.199 3v6l-5.198 3-5.197-3v-5.999Z"})));const ForwardRef=forwardRef(IconTechOpenai);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-php.js b/src/components/Icon/glyphs/icon-tech-php.js new file mode 100644 index 0000000000..53a320ad6a --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-php.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechPhp=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#000",fillRule:"evenodd",d:"M10.917 17.073H3.798L0 36.118h3.692l1.01-5.047h3.187q1.62 0 3.134-.372 1.515-.372 2.842-1.753a8.2 8.2 0 0 0 1.7-2.443q.638-1.355.824-2.763.479-3.081-.93-4.86-1.408-1.781-4.542-1.807m-4.016 3.055.005-.026-.026.026zm0 0L5.312 28.07q.16.027.32.027h.371q2.55.027 4.25-.505 1.7-.558 2.284-3.878.479-2.789-.956-3.214-1.408-.425-3.533-.398a8 8 0 0 1-.61.026z",clipRule:"evenodd"}),React.createElement("path",{fill:"#000",d:"M20.595 12h3.665l-1.036 5.073h3.294q2.71.054 4.037 1.116 1.355 1.062.797 4.037l-1.78 8.846h-3.718l1.7-8.447q.266-1.328-.16-1.886-.425-.558-1.832-.558l-2.949-.027-2.178 10.918H16.77z"}),React.createElement("path",{fill:"#000",fillRule:"evenodd",d:"M42.407 17.073h-7.119L31.49 36.118h3.692l1.01-5.047h3.187q1.62 0 3.134-.372 1.515-.372 2.842-1.753a8.2 8.2 0 0 0 1.7-2.443q.638-1.355.824-2.763.479-3.081-.93-4.86-1.409-1.781-4.542-1.807m-4.016 3.055.005-.026-.026.026zm0 0-1.588 7.942q.159.027.318.027h.372q2.55.027 4.25-.505 1.7-.558 2.284-3.878.479-2.789-.956-3.214-1.408-.425-3.533-.398a8 8 0 0 1-.61.026z",clipRule:"evenodd"}));const ForwardRef=forwardRef(IconTechPhp);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-postgres.js b/src/components/Icon/glyphs/icon-tech-postgres.js new file mode 100644 index 0000000000..85f37a15bc --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-postgres.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechPostgres=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#000",d:"M34.799 34.936c.304-2.544.217-2.899 2.109-2.508l.485.044c1.53.07 3.057-.177 4.487-.725 2.406-1.123 3.841-2.993 1.45-2.5-5.43 1.123-5.799-.725-5.799-.725 5.726-8.495 8.118-19.287 6.052-21.926-5.639-7.204-15.395-3.798-15.554-3.71h-.058a19 19 0 0 0-3.624-.377 8.9 8.9 0 0 0-5.726 1.71S1.226-2.949 2.023 13.236c.167 3.45 4.936 26.093 10.618 19.236 2.073-2.5 4.08-4.61 4.08-4.61 1.013.68 2.23.99 3.444.877l.101-.087c-.034.326-.022.656.036.979-1.45 1.63-1.036 1.92-3.964 2.522s-1.218 1.696-.08 1.979c1.377.348 4.559.833 6.712-2.175l-.087.348c.58.457.536 3.298.616 5.335-.015 1.712.2 3.418.638 5.073.413 1.117.898 3.994 4.747 3.168 3.211-.689 5.668-1.682 5.893-10.872"}),React.createElement("path",{fill:"#000",d:"M27.63 47.998a5.07 5.07 0 0 1-3.588-1.334 6.8 6.8 0 0 1-1.696-2.994 5 5 0 0 1-.101-.304 20 20 0 0 1-.761-5.66c0-.363 0-.726-.036-1.146v-.812a8.7 8.7 0 0 1-5.661.334 5.7 5.7 0 0 1-1.617-.681 3.6 3.6 0 0 1-.862-.725 4.1 4.1 0 0 1-4.08.862c-1.537-.514-2.9-1.942-4.19-4.349a38.5 38.5 0 0 1-2.595-6.624A62 62 0 0 1 .03 13.345C-.224 8.27 1.153 4.596 4.125 2.473 6.582.74 9.923.204 14.105.893c1.447.242 2.872.598 4.262 1.065a11.3 11.3 0 0 1 6.038-1.45c1.161.018 2.32.124 3.464.32A20.2 20.2 0 0 1 33.791 0c4.755.05 8.698 1.906 11.394 5.356 1.022 1.305 1.899 4.19.26 10.517a46.6 46.6 0 0 1-4.92 11.691q1.217-.05 2.406-.311a4.4 4.4 0 0 1 1.565-.087 2.36 2.36 0 0 1 1.98 1.572c.818 2.472-2.654 4.306-3.734 4.806a13.05 13.05 0 0 1-5.428.942H36.9v.247c0 .08 0 .246-.043.384-.123 4.697-.797 7.683-2.175 9.661-1.45 2.095-3.508 2.682-5.298 3.066a8 8 0 0 1-1.754.153m-3.486-17.28c1.152.92 1.254 2.595 1.34 5.733v1.088c.007 1.49.177 2.975.508 4.428q.085.219.16.45c.413 1.282.587 1.812 2.275 1.45 1.494-.32 2.262-.602 2.849-1.45.877-1.254 1.37-3.791 1.45-7.546l2.014.051-2-.24c0-.166.036-.318.058-.463.174-1.5.312-2.689 1.384-3.457a3 3 0 0 1 2.095-.486 2.84 2.84 0 0 1-.674-1.37l-.167-.833.471-.725a42.2 42.2 0 0 0 5.661-12.51c1.232-4.755.689-6.77.479-7.053-4.48-5.726-12.279-3.384-13.141-3.102l-.152.08-.725.138-.384-.08a17 17 0 0 0-3.24-.333 6.9 6.9 0 0 0-4.48 1.304l-.992.754-1.066-.442c-2.087-.848-8.22-2.566-11.423-.297-1.768 1.254-2.573 3.725-2.392 7.357a59.5 59.5 0 0 0 2.32 10.487c1.928 6.227 3.624 7.973 4.117 8.096.243-.132.454-.318.616-.543a101 101 0 0 1 4.175-4.711l1.166-1.225 1.407.935a3.34 3.34 0 0 0 1.565.55l3.356-2.855-.493 4.972a2 2 0 0 0 0 .471l.145.942-.638.725 1.566.725z"}),React.createElement("path",{fill:"#336791",d:"M43.344 29.218c-5.429 1.123-5.799-.725-5.799-.725 5.727-8.495 8.118-19.287 6.053-21.925-5.64-7.205-15.395-3.798-15.555-3.711h-.058a20.3 20.3 0 0 0-3.624-.377 8.94 8.94 0 0 0-5.726 1.717S1.24-2.97 2.037 13.214c.167 3.45 4.936 26.093 10.619 19.236 2.073-2.5 4.08-4.61 4.08-4.61a5.28 5.28 0 0 0 3.443.878l.094-.087c-.027.326-.013.655.044.978-1.45 1.63-1.037 1.92-3.965 2.523-2.928.601-1.218 1.696-.08 1.978 1.378.348 4.56.834 6.712-2.174l-.087.348c.573.456.979 2.986.914 5.276-.066 2.29-.116 3.87.333 5.074.45 1.203.906 3.993 4.755 3.167 3.21-.688 4.877-2.471 5.11-5.45.166-2.117.536-1.805.558-3.697l.304-.899c.34-2.899.05-3.798 2.03-3.363l.485.044c1.53.07 3.057-.177 4.487-.725 2.406-1.123 3.841-2.993 1.45-2.5z"}),React.createElement("path",{fill:"#fff",d:"M27.84 46.751c-3.232 0-4.262-2.566-4.682-3.624-.609-1.537-.725-7.248-.609-12.322a.674.674 0 1 1 1.348 0c-.159 5.581.066 10.633.515 11.757.689 1.732 1.747 3.254 4.719 2.616 2.971-.637 3.892-1.652 4.348-4.428.37-2.117 1.095-8.154 1.182-9.343a.675.675 0 1 1 1.34.094c-.094 1.247-.811 7.248-1.195 9.48-.566 3.291-2.037 4.784-5.415 5.51a7.5 7.5 0 0 1-1.55.26M10.46 34.342a2.7 2.7 0 0 1-.841-.138c-2.102-.725-4.095-4.102-5.93-10.147a60 60 0 0 1-2.355-10.872c-.232-4.61.957-7.872 3.53-9.698 2.174-1.53 5.19-1.993 8.995-1.384 1.712.277 3.39.731 5.008 1.355a.674.674 0 0 1-.507 1.247c-.08 0-8.234-3.298-12.72-.124-2.175 1.544-3.175 4.414-2.965 8.531A60.6 60.6 0 0 0 4.973 23.68c1.63 5.335 3.479 8.698 5.073 9.234.247.08.993.333 2.066-.964 2.073-2.486 3.972-4.472 3.987-4.494a.674.674 0 1 1 .97.928s-1.884 1.979-3.92 4.429a3.5 3.5 0 0 1-2.69 1.529M37.502 29.11a.7.7 0 0 1-.37-.117.667.667 0 0 1-.18-.935c5.798-8.574 7.813-18.924 6.08-21.135-2.174-2.79-5.255-4.24-9.147-4.349a17.2 17.2 0 0 0-5.559.797l-.102.044a.677.677 0 1 1-.594-1.218 17.1 17.1 0 0 1 6.06-.978c4.348.043 7.936 1.718 10.379 4.841.609.776.898 2.044.877 3.77a25 25 0 0 1-.863 5.58 45 45 0 0 1-6.045 13.373.68.68 0 0 1-.536.326"}),React.createElement("path",{fill:"#fff",d:"M37.778 33.08c-.836.05-1.67-.109-2.429-.463a1.28 1.28 0 0 1-.688-1.044c-.145-2.58 1.232-3.087 2-3.312a11 11 0 0 0-.362-.464 9.2 9.2 0 0 1-1.42-2.406c-.073-.167-.298-.566-.552-1.03-1.398-2.507-4.312-7.726-2.428-10.422.863-1.232 2.573-1.732 5.074-1.45a15 15 0 0 0-2.718-4.653 12.97 12.97 0 0 0-9.915-4.74A8.07 8.07 0 0 0 18.208 5.4c-3.48 3.508-3.349 9.864-3.349 9.93a.674.674 0 0 1-1.34 0c0-.29-.139-7.002 3.732-10.916a9.37 9.37 0 0 1 7.103-2.696 14.3 14.3 0 0 1 10.872 5.124c2.175 2.493 3.197 5.248 3.276 6.168a.725.725 0 0 1-.558.892h-.246c-3.066-.493-4.24.159-4.682.797-1.4 2 1.333 6.9 2.5 8.995.297.536.515.927.616 1.166a8.1 8.1 0 0 0 1.283 2.052c.344.4.596.87.74 1.377.057.094.804 1.109 5.015.24 1.102-.233 1.768 0 1.979.6.398 1.204-1.805 2.595-3.03 3.16a11.2 11.2 0 0 1-4.341.79m-1.776-1.637a4.16 4.16 0 0 0 2.218.282 9.7 9.7 0 0 0 3.312-.688 7.4 7.4 0 0 0 1.913-1.232c-2.761.565-4.718.493-5.798-.232l-.188-.138-.283.087c-.645.218-1.232.34-1.153 1.92zM18.208 34.988a8.7 8.7 0 0 1-2.138-.276c-.218-.05-2.088-.558-2.059-1.754.03-1.196 1.58-1.45 2.175-1.536 2.072-.428 2.174-.595 2.848-1.414.188-.239.428-.543.725-.898a1.986 1.986 0 0 1 2.522-.61 2.73 2.73 0 0 1 1.45 1.776 2.27 2.27 0 0 1-.283 1.994 6.25 6.25 0 0 1-5.24 2.718m-2.711-1.965c.277.175.58.305.899.385 1.79.449 4.29.478 6.001-1.921a1 1 0 0 0 .087-.84 1.4 1.4 0 0 0-.725-.9c-.362-.152-.565-.239-1 .254-.297.333-.515.602-.725.84-.826 1.044-1.203 1.392-3.624 1.9a4 4 0 0 0-.906.282z"}),React.createElement("path",{fill:"#fff",d:"M20.186 30.196a.67.67 0 0 1-.66-.572 1.6 1.6 0 0 1 0-.268 6.16 6.16 0 0 1-4.261-1.928 6.99 6.99 0 0 1-1.841-5.864c.231-1.834.277-3.687.138-5.53 0-.312-.037-.537 0-.725.036-.188 0-.725 1.776-1.653a9.9 9.9 0 0 1 3.24-1.007c2.275-.24 3.776.783 4.232 2.9 1.196 5.558.094 8.015-.725 9.813-.152.333-.29.652-.405.95l-.095.275a6.6 6.6 0 0 0-.681 2.834.675.675 0 0 1-.565.768zm-5.334-14.655v.413a29 29 0 0 1-.152 5.799 5.67 5.67 0 0 0 1.486 4.74 4.76 4.76 0 0 0 3.435 1.522 17 17 0 0 1 .63-1.9l.095-.267c.13-.348.283-.682.442-1.037.776-1.718 1.732-3.849.623-8.98a2.02 2.02 0 0 0-1.573-1.754c-1.826-.428-4.428.964-4.986 1.464"}),React.createElement("path",{fill:"#fff",d:"M18.548 15.244c0 .217.406.804.972.884a1.02 1.02 0 0 0 1.072-.602c0-.217-.398-.463-.964-.536-.565-.072-1.05 0-1.08.254"}),React.createElement("path",{fill:"#fff",d:"M19.657 16.367h-.166a1.51 1.51 0 0 1-.957-.601.82.82 0 0 1-.196-.537.38.38 0 0 1 .174-.282c.352-.193.76-.252 1.153-.167.275.035.543.118.79.246.362.203.384.428.37.544a.97.97 0 0 1-.436.572 1.33 1.33 0 0 1-.732.225m-.877-1.073a1.09 1.09 0 0 0 .776.617.89.89 0 0 0 .819-.392 1.28 1.28 0 0 0-.768-.304 1.3 1.3 0 0 0-.834.08zM35.74 14.801c.037.218-.398.805-.963.885a1.04 1.04 0 0 1-1.08-.602c0-.217.406-.457.964-.536s1.05 0 1.08.253"}),React.createElement("path",{fill:"#fff",d:"M34.646 15.809a1.15 1.15 0 0 1-1.058-.725c-.05-.34.5-.595 1.058-.674s1.16 0 1.21.355a.86.86 0 0 1-.29.587 1.37 1.37 0 0 1-.775.42zm.37-1.175h-.334c-.594.08-.891.32-.876.413a.97.97 0 0 0 .956.508c.246-.045.47-.17.638-.355a.73.73 0 0 0 .232-.406c-.029-.058-.232-.16-.616-.16M36.56 27.471a.66.66 0 0 1-.377-.116.67.67 0 0 1-.181-.928c1.311-1.964 1.072-3.971.833-5.914a17.4 17.4 0 0 1-.174-2.522 18 18 0 0 1 .24-2.225c.152-.808.215-1.63.188-2.45a.676.676 0 0 1 1.348-.073c.028.914-.042 1.827-.21 2.726q-.172 1.026-.225 2.065 0 1.167.174 2.32c.246 2.08.53 4.443-1.058 6.82a.68.68 0 0 1-.558.297"}));const ForwardRef=forwardRef(IconTechPostgres);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-python.js b/src/components/Icon/glyphs/icon-tech-python.js new file mode 100644 index 0000000000..9c41c0f5f2 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-python.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechPython=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("radialGradient",{id:"paint0_radial_1031_1963",cx:0,cy:0,r:1,gradientTransform:"matrix(0 -2.48291 56.2936 0 23.895 45.735)",gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#C6C6C6"}),React.createElement("stop",{offset:1,stopColor:"#fff"})),React.createElement("radialGradient",{id:"paint1_radial_1031_1963",cx:0,cy:0,r:1,gradientTransform:"matrix(0 -2.48291 56.2936 0 23.895 45.735)",gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#C6C6C6"}),React.createElement("stop",{offset:1,stopColor:"#fff"})),React.createElement("linearGradient",{id:"paint2_linear_1031_1963",x1:38.828,x2:30.863,y1:27.877,y2:16.841,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FFD33A"}),React.createElement("stop",{offset:1,stopColor:"#FFE873"})),React.createElement("clipPath",{id:"clip0_1031_1963"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{fillRule:"evenodd",clipPath:"url(#clip0_1031_1963)",clipRule:"evenodd"},React.createElement("path",{fill:"url(#paint0_radial_1031_1963)",d:"M23.895 48.218c-7.073 0-12.807-1.112-12.807-2.483s5.734-2.483 12.807-2.483 12.807 1.112 12.807 2.483-5.734 2.483-12.807 2.483"}),React.createElement("path",{fill:"url(#paint1_radial_1031_1963)",d:"M23.895 48.218c-7.073 0-12.807-1.112-12.807-2.483s5.734-2.483 12.807-2.483 12.807 1.112 12.807 2.483-5.734 2.483-12.807 2.483"}),React.createElement("path",{fill:"url(#paint2_linear_1031_1963)",d:"M29.156 33.479c.99 0 1.793.811 1.793 1.815 0 1.007-.803 1.827-1.794 1.827-.987 0-1.793-.82-1.793-1.827 0-1.004.806-1.815 1.794-1.815m5.368-23.046v4.244c0 3.29-2.79 6.059-5.97 6.059h-9.546c-2.614 0-4.778 2.238-4.778 4.857v9.1c0 2.59 2.252 4.113 4.778 4.856 3.025.89 5.926 1.05 9.546 0 2.406-.696 4.778-2.099 4.778-4.856V31.05h-9.546v-1.215H38.11c2.778 0 3.813-1.937 4.78-4.845.997-2.993.954-5.872 0-9.713-.687-2.765-1.998-4.845-4.78-4.845z"}),React.createElement("path",{fill:"#4786CF",d:"M4 20.149v-.052c.003-1.506.232-2.99.673-4.819.76-3.176 3.193-4.845 5.97-4.845h13.143V9.22H14.23V5.577c0-2.758.734-4.254 4.778-4.968a27.3 27.3 0 0 1 4.567-.39 30 30 0 0 1 4.979.39c2.593.432 4.778 2.378 4.778 4.968v9.1c0 2.669-2.121 4.856-4.778 4.856h-9.546c-3.24 0-5.97 2.782-5.97 5.937v4.366H9.752c-2.778 0-4.4-2.015-5.08-4.845-.45-1.869-.67-3.368-.672-4.842M18.407 3.148c-.991 0-1.794.82-1.794 1.827 0 1.003.803 1.815 1.794 1.815.987 0 1.793-.812 1.793-1.815 0-1.008-.806-1.827-1.793-1.827"})));const ForwardRef=forwardRef(IconTechPython);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-react.js b/src/components/Icon/glyphs/icon-tech-react.js new file mode 100644 index 0000000000..da402dc60e --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-react.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechReact=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("clipPath",{id:"clip0_1031_1960"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{fill:"#61DAFB",clipPath:"url(#clip0_1031_1960)"},React.createElement("path",{d:"M48 24.378c0-3.18-3.982-6.193-10.087-8.062 1.409-6.223.783-11.173-1.977-12.758-.636-.372-1.38-.548-2.191-.548v2.182c.45 0 .812.088 1.115.254 1.33.763 1.908 3.669 1.458 7.406-.108.92-.284 1.889-.499 2.877a47 47 0 0 0-6.213-1.066 48 48 0 0 0-4.07-4.893c3.19-2.964 6.183-4.588 8.219-4.588V3c-2.691 0-6.213 1.918-9.775 5.244-3.56-3.307-7.083-5.205-9.774-5.205v2.182c2.026 0 5.03 1.614 8.219 4.56a46 46 0 0 0-4.041 4.881 46 46 0 0 0-6.223 1.077 30 30 0 0 1-.508-2.838c-.46-3.737.107-6.643 1.428-7.416.294-.176.675-.254 1.125-.254V3.049c-.822 0-1.565.176-2.21.548-2.75 1.585-3.367 6.526-1.948 12.729C3.962 18.204 0 21.208 0 24.378c0 3.18 3.982 6.193 10.087 8.062-1.409 6.223-.782 11.173 1.977 12.758.636.372 1.38.548 2.201.548 2.69 0 6.213-1.918 9.774-5.244 3.561 3.307 7.084 5.205 9.774 5.205.822 0 1.566-.176 2.212-.548 2.749-1.585 3.365-6.526 1.947-12.729C44.038 30.561 48 27.548 48 24.378m-12.739-6.526a44 44 0 0 1-1.32 3.865 46 46 0 0 0-1.282-2.348 53 53 0 0 0-1.41-2.29c1.39.206 2.73.46 4.012.773m-4.48 10.42a52 52 0 0 1-2.359 3.737 51 51 0 0 1-8.825.01 54 54 0 0 1-2.367-3.718 51 51 0 0 1-2.036-3.894 51 51 0 0 1 2.026-3.904 52 52 0 0 1 2.358-3.737 51 51 0 0 1 8.825-.01 54 54 0 0 1 2.367 3.718 51 51 0 0 1 2.036 3.894 55 55 0 0 1-2.026 3.904M33.94 27a42 42 0 0 1 1.35 3.894 44 44 0 0 1-4.031.783c.48-.754.958-1.527 1.409-2.32.45-.782.87-1.574 1.272-2.357M24.02 37.44c-.91-.94-1.82-1.987-2.72-3.131.88.039 1.78.068 2.69.068.92 0 1.83-.02 2.72-.068a38 38 0 0 1-2.69 3.13m-7.28-5.763c-1.389-.206-2.73-.46-4.011-.773a44 44 0 0 1 1.32-3.865c.402.783.823 1.566 1.282 2.348q.69 1.174 1.41 2.29m7.23-20.36c.91.939 1.82 1.986 2.72 3.13-.88-.039-1.78-.068-2.69-.068-.92 0-1.83.02-2.72.068a38 38 0 0 1 2.69-3.13m-7.24 5.762a54 54 0 0 0-2.68 4.667 42 42 0 0 1-1.35-3.894 48 48 0 0 1 4.03-.773M7.877 29.33c-3.463-1.478-5.704-3.415-5.704-4.951s2.24-3.483 5.704-4.95a30 30 0 0 1 2.71-.989 47 47 0 0 0 2.202 5.959 46 46 0 0 0-2.172 5.929 30 30 0 0 1-2.74-.998m5.264 13.98c-1.33-.762-1.908-3.668-1.458-7.406.108-.92.284-1.888.499-2.876 1.918.47 4.011.831 6.213 1.066a48 48 0 0 0 4.07 4.892c-3.19 2.965-6.183 4.589-8.219 4.589-.44-.01-.812-.098-1.105-.264m23.207-7.454c.46 3.737-.107 6.643-1.428 7.416-.294.176-.675.254-1.125.254-2.026 0-5.03-1.614-8.219-4.56a46 46 0 0 0 4.041-4.881 46 46 0 0 0 6.223-1.077c.225.989.4 1.938.508 2.848m3.767-6.526a30 30 0 0 1-2.71.988 47 47 0 0 0-2.201-5.959 46 46 0 0 0 2.172-5.929c.968.304 1.888.636 2.749.998 3.463 1.478 5.704 3.415 5.704 4.95-.01 1.537-2.25 3.484-5.714 4.952"}),React.createElement("path",{d:"M23.99 28.85a4.471 4.471 0 1 0 0-8.943 4.471 4.471 0 0 0 0 8.942"})));const ForwardRef=forwardRef(IconTechReact);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-reactnative.js b/src/components/Icon/glyphs/icon-tech-reactnative.js new file mode 100644 index 0000000000..6b4adaa17b --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-reactnative.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechReactnative=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#61DAFB",d:"M48 24.378c0-3.18-3.982-6.193-10.087-8.062 1.409-6.223.783-11.173-1.977-12.758-.636-.372-1.38-.548-2.191-.548v2.182c.45 0 .812.088 1.115.254 1.33.763 1.908 3.669 1.458 7.406-.108.92-.284 1.889-.499 2.877a47 47 0 0 0-6.213-1.066 48 48 0 0 0-4.07-4.893c3.19-2.964 6.183-4.588 8.219-4.588V3c-2.691 0-6.213 1.918-9.775 5.244-3.56-3.307-7.083-5.205-9.774-5.205v2.182c2.026 0 5.03 1.614 8.219 4.56a46 46 0 0 0-4.041 4.881 46 46 0 0 0-6.223 1.077 30 30 0 0 1-.508-2.838c-.46-3.737.107-6.643 1.428-7.416.294-.176.675-.254 1.125-.254V3.049c-.822 0-1.565.176-2.21.548-2.75 1.585-3.367 6.526-1.948 12.729C3.962 18.204 0 21.208 0 24.378c0 3.18 3.982 6.193 10.087 8.062-1.409 6.223-.782 11.173 1.977 12.758.636.372 1.38.548 2.201.548 2.69 0 6.213-1.918 9.774-5.244 3.561 3.307 7.084 5.205 9.774 5.205.822 0 1.566-.176 2.212-.548 2.749-1.585 3.365-6.526 1.947-12.729C44.038 30.561 48 27.548 48 24.378m-12.739-6.526a44 44 0 0 1-1.32 3.865 46 46 0 0 0-1.282-2.348 53 53 0 0 0-1.41-2.29c1.39.206 2.73.46 4.012.773m-4.48 10.42a52 52 0 0 1-2.359 3.737 51 51 0 0 1-8.825.01 54 54 0 0 1-2.367-3.718 51 51 0 0 1-2.036-3.894 51 51 0 0 1 2.026-3.904 52 52 0 0 1 2.358-3.737 51 51 0 0 1 8.825-.01 54 54 0 0 1 2.367 3.718 51 51 0 0 1 2.036 3.894 55 55 0 0 1-2.026 3.904M33.94 27a42 42 0 0 1 1.35 3.894 44 44 0 0 1-4.031.783c.48-.754.958-1.527 1.409-2.32.45-.782.87-1.574 1.272-2.357M24.02 37.44c-.91-.94-1.82-1.987-2.72-3.131.88.039 1.78.068 2.69.068.92 0 1.83-.02 2.72-.068a38 38 0 0 1-2.69 3.13m-7.28-5.763c-1.389-.206-2.73-.46-4.011-.773a44 44 0 0 1 1.32-3.865c.402.783.823 1.566 1.282 2.348q.69 1.174 1.41 2.29m7.23-20.36c.91.939 1.82 1.986 2.72 3.13-.88-.039-1.78-.068-2.69-.068-.92 0-1.83.02-2.72.068a38 38 0 0 1 2.69-3.13m-7.24 5.762a54 54 0 0 0-2.68 4.667 42 42 0 0 1-1.35-3.894 48 48 0 0 1 4.03-.773M7.877 29.33c-3.463-1.478-5.704-3.415-5.704-4.951s2.24-3.483 5.704-4.95a30 30 0 0 1 2.71-.989 47 47 0 0 0 2.202 5.959 46 46 0 0 0-2.172 5.929 30 30 0 0 1-2.74-.998m5.264 13.98c-1.33-.762-1.908-3.668-1.458-7.406.108-.92.284-1.888.499-2.876 1.918.47 4.011.831 6.213 1.066a48 48 0 0 0 4.07 4.892c-3.19 2.965-6.183 4.589-8.219 4.589-.44-.01-.812-.098-1.105-.264m23.207-7.454c.46 3.737-.107 6.643-1.428 7.416-.294.176-.675.254-1.125.254-2.026 0-5.03-1.614-8.219-4.56a46 46 0 0 0 4.041-4.881 46 46 0 0 0 6.223-1.077c.225.989.4 1.938.508 2.848m3.767-6.526a30 30 0 0 1-2.71.988 47 47 0 0 0-2.201-5.959 46 46 0 0 0 2.172-5.929c.968.304 1.888.636 2.749.998 3.463 1.478 5.704 3.415 5.704 4.95-.01 1.537-2.25 3.484-5.714 4.952"}),React.createElement("path",{fill:"#61DAFB",d:"M23.99 28.85a4.471 4.471 0 1 0 0-8.943 4.471 4.471 0 0 0 0 8.942"}));const ForwardRef=forwardRef(IconTechReactnative);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-ruby.js b/src/components/Icon/glyphs/icon-tech-ruby.js new file mode 100644 index 0000000000..c85e480b1a --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-ruby.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechRuby=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_1031_2037",x1:51.112,x2:41.001,y1:47.178,y2:29.274,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FB7655"}),React.createElement("stop",{stopColor:"#FB7655"}),React.createElement("stop",{offset:.41,stopColor:"#E42B1E"}),React.createElement("stop",{offset:.99,stopColor:"#900"}),React.createElement("stop",{offset:1,stopColor:"#900"})),React.createElement("linearGradient",{id:"paint1_linear_1031_2037",x1:67.988,x2:22.822,y1:-4.343,y2:-6.911,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#871101"}),React.createElement("stop",{stopColor:"#871101"}),React.createElement("stop",{offset:.99,stopColor:"#911209"}),React.createElement("stop",{offset:1,stopColor:"#911209"})),React.createElement("linearGradient",{id:"paint2_linear_1031_2037",x1:2787.28,x2:2787.11,y1:559.152,y2:-83.088,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#871101"}),React.createElement("stop",{stopColor:"#871101"}),React.createElement("stop",{offset:.99,stopColor:"#911209"}),React.createElement("stop",{offset:1,stopColor:"#911209"})),React.createElement("linearGradient",{id:"paint3_linear_1031_2037",x1:4.959,x2:12.911,y1:34.187,y2:44.708,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{offset:.23,stopColor:"#E57252"}),React.createElement("stop",{offset:.46,stopColor:"#DE3B20"}),React.createElement("stop",{offset:.99,stopColor:"#A60003"}),React.createElement("stop",{offset:1,stopColor:"#A60003"})),React.createElement("linearGradient",{id:"paint4_linear_1031_2037",x1:-11.51,x2:1.418,y1:52.752,y2:53.259,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{offset:.31,stopColor:"#DE4024"}),React.createElement("stop",{offset:.99,stopColor:"#BF190B"}),React.createElement("stop",{offset:1,stopColor:"#BF190B"})),React.createElement("linearGradient",{id:"paint5_linear_1031_2037",x1:16.647,x2:17.436,y1:18.967,y2:32.38,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{offset:.23,stopColor:"#E4714E"}),React.createElement("stop",{offset:.56,stopColor:"#BE1A0D"}),React.createElement("stop",{offset:.99,stopColor:"#A80D00"}),React.createElement("stop",{offset:1,stopColor:"#A80D00"})),React.createElement("linearGradient",{id:"paint6_linear_1031_2037",x1:21.859,x2:22.969,y1:2.592,y2:22.043,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{offset:.18,stopColor:"#E46342"}),React.createElement("stop",{offset:.4,stopColor:"#C82410"}),React.createElement("stop",{offset:.99,stopColor:"#A80D00"}),React.createElement("stop",{offset:1,stopColor:"#A80D00"})),React.createElement("linearGradient",{id:"paint7_linear_1031_2037",x1:88.161,x2:89.301,y1:7.302,y2:-33.694,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{stopColor:"#fff"}),React.createElement("stop",{offset:.54,stopColor:"#C81F11"}),React.createElement("stop",{offset:.99,stopColor:"#BF0905"}),React.createElement("stop",{offset:1,stopColor:"#BF0905"})),React.createElement("linearGradient",{id:"paint8_linear_1031_2037",x1:14.71,x2:51.846,y1:56.193,y2:17.788,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#BD0012"}),React.createElement("stop",{stopColor:"#BD0012"}),React.createElement("stop",{offset:.07,stopColor:"#fff"}),React.createElement("stop",{offset:.17,stopColor:"#fff"}),React.createElement("stop",{offset:.27,stopColor:"#C82F1C"}),React.createElement("stop",{offset:.33,stopColor:"#820C01"}),React.createElement("stop",{offset:.46,stopColor:"#A31601"}),React.createElement("stop",{offset:.72,stopColor:"#B31301"}),React.createElement("stop",{offset:.99,stopColor:"#E82609"}),React.createElement("stop",{offset:1,stopColor:"#E82609"})),React.createElement("linearGradient",{id:"paint9_linear_1031_2037",x1:27.062,x2:17.422,y1:39.083,y2:32.193,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#8C0C01"}),React.createElement("stop",{stopColor:"#8C0C01"}),React.createElement("stop",{offset:.54,stopColor:"#990C00"}),React.createElement("stop",{offset:.99,stopColor:"#A80D0E"}),React.createElement("stop",{offset:1,stopColor:"#A80D0E"})),React.createElement("linearGradient",{id:"paint10_linear_1031_2037",x1:11.381,x2:-7.149,y1:31.099,y2:27.336,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#8B2114"}),React.createElement("stop",{stopColor:"#8B2114"}),React.createElement("stop",{offset:.43,stopColor:"#9E100A"}),React.createElement("stop",{offset:.99,stopColor:"#B3100C"}),React.createElement("stop",{offset:1,stopColor:"#B3100C"})),React.createElement("linearGradient",{id:"paint11_linear_1031_2037",x1:48.563,x2:35.215,y1:20.922,y2:13.408,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#7E110B"}),React.createElement("stop",{stopColor:"#7E110B"}),React.createElement("stop",{offset:.99,stopColor:"#9E0C00"}),React.createElement("stop",{offset:1,stopColor:"#9E0C00"})),React.createElement("linearGradient",{id:"paint13_linear_1031_2037",x1:49.814,x2:44.46,y1:8.931,y2:3.77,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#79130D"}),React.createElement("stop",{stopColor:"#79130D"}),React.createElement("stop",{offset:.99,stopColor:"#9E120B"}),React.createElement("stop",{offset:1,stopColor:"#9E120B"})),React.createElement("linearGradient",{id:"paint15_linear_1031_2037",x1:10.024,x2:11.64,y1:.08,y2:23.505,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#B31000"}),React.createElement("stop",{stopColor:"#B31000"}),React.createElement("stop",{offset:.44,stopColor:"#910F08"}),React.createElement("stop",{offset:.99,stopColor:"#791C12"}),React.createElement("stop",{offset:1,stopColor:"#791C12"})),React.createElement("radialGradient",{id:"paint12_radial_1031_2037",cx:0,cy:0,r:1,gradientTransform:"matrix(16.212 0 0 12.6203 17.943 35.297)",gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#A30C00"}),React.createElement("stop",{stopColor:"#A30C00"}),React.createElement("stop",{offset:.99,stopColor:"#800E08"}),React.createElement("stop",{offset:1,stopColor:"#800E08"})),React.createElement("radialGradient",{id:"paint14_radial_1031_2037",cx:0,cy:0,r:1,gradientTransform:"matrix(12.1954 0 0 6.37254 34.832 19.226)",gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#A80D00"}),React.createElement("stop",{stopColor:"#A80D00"}),React.createElement("stop",{offset:.99,stopColor:"#7E0E08"}),React.createElement("stop",{offset:1,stopColor:"#7E0E08"})),React.createElement("clipPath",{id:"clip0_1031_2037"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_1031_2037)"},React.createElement("path",{fill:"url(#paint0_linear_1031_2037)",d:"M37.174 31.582 9.779 47.849l35.471-2.407 2.732-35.767z"}),React.createElement("path",{fill:"url(#paint1_linear_1031_2037)",d:"M45.308 45.417 42.26 24.375l-8.305 10.966z"}),React.createElement("path",{fill:"url(#paint2_linear_1031_2037)",d:"m45.35 45.417-22.336-1.753-13.116 4.139z"}),React.createElement("path",{fill:"url(#paint3_linear_1031_2037)",d:"m9.93 47.808 5.58-18.28-12.28 2.626z"}),React.createElement("path",{fill:"url(#paint4_linear_1031_2037)",d:"m0 38.285 3.422-6.24-2.768-7.436z"}),React.createElement("path",{fill:"url(#paint5_linear_1031_2037)",d:"m33.953 35.401-5.134-20.11-14.693 13.773z"}),React.createElement("path",{fill:"url(#paint6_linear_1031_2037)",d:"M46.817 15.574 32.93 4.23 29.06 16.734z"}),React.createElement("path",{fill:"url(#paint7_linear_1031_2037)",d:"m40.322.186-8.168 4.515L27 .126z"}),React.createElement("path",{fill:"#fff",d:"m.47 24.375 2.785 7.9 12.102-2.715 13.815-12.84 3.9-12.385L26.931 0 16.494 3.906c-3.288 3.059-9.67 9.11-9.9 9.225-.227.116-4.213 7.65-6.124 11.244"}),React.createElement("path",{fill:"url(#paint8_linear_1031_2037)",d:"M10.249 10.183C17.376 3.117 26.564-1.059 30.09 2.5c3.524 3.558-.213 12.203-7.34 19.268-7.128 7.064-16.202 11.469-19.725 7.911-3.526-3.555.097-12.43 7.224-19.495"}),React.createElement("path",{fill:"url(#paint9_linear_1031_2037)",d:"m9.93 47.8 5.535-18.337 18.386 5.907C27.203 41.603 19.81 46.873 9.93 47.8"}),React.createElement("path",{fill:"#9E1209",d:"M0 38.204c.262 9.408 7.05 9.548 9.941 9.631L3.262 32.236z"}),React.createElement("path",{fill:"url(#paint10_linear_1031_2037)",d:"M3.233 32.255 2.185 44.742c1.978 2.702 4.7 2.937 7.555 2.727-2.065-5.142-6.192-15.422-6.507-15.214"}),React.createElement("path",{fill:"url(#paint11_linear_1031_2037)",d:"m29.197 16.683 4.72 18.696C39.47 29.54 44.453 23.263 46.894 15.5z"}),React.createElement("path",{fill:"url(#paint12_radial_1031_2037)",d:"m15.458 29.463 7.4 14.279c4.377-2.374 7.804-5.265 10.942-8.363z"}),React.createElement("path",{fill:"url(#paint13_linear_1031_2037)",d:"M46.846 15.594c1.89-5.701 2.325-13.88-6.582-15.398l-7.31 4.037z"}),React.createElement("path",{fill:"url(#paint14_radial_1031_2037)",d:"M29.223 16.713c4.267 2.622 12.867 7.89 13.041 7.987.271.152 3.708-5.796 4.488-9.157z"}),React.createElement("path",{fill:"url(#paint15_linear_1031_2037)",d:"m32.912 4.265 14.702 2.063C46.83 3.003 44.42.858 40.312.187z"})));const ForwardRef=forwardRef(IconTechRuby);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-swift.js b/src/components/Icon/glyphs/icon-tech-swift.js new file mode 100644 index 0000000000..82f18212fc --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-swift.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechSwift=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("linearGradient",{id:"paint0_linear_1031_1912",x1:2.813,x2:-6.259,y1:-6.259,y2:45.187,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#FAAE41"}),React.createElement("stop",{offset:1,stopColor:"#EF3E31"})),React.createElement("linearGradient",{id:"paint1_linear_1031_1912",x1:4.876,x2:-1.23,y1:-2.365,y2:35.34,gradientUnits:"userSpaceOnUse"},React.createElement("stop",{stopColor:"#E29F3A"}),React.createElement("stop",{offset:1,stopColor:"#D43929"})),React.createElement("clipPath",{id:"clip0_1031_1912"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{fillRule:"evenodd",clipPath:"url(#clip0_1031_1912)",clipRule:"evenodd"},React.createElement("path",{fill:"url(#paint0_linear_1031_1912)",d:"M10.633 0C4.76 0 0 4.76 0 10.633v26.734C0 43.24 4.76 48 10.633 48h26.734C43.24 48 48 43.24 48 37.367V10.633C48 4.76 43.24 0 37.367 0z"}),React.createElement("path",{fill:"url(#paint1_linear_1031_1912)",d:"M10.624.038C4.756.038 0 4.795 0 10.662v12.092l4.925 5.301c.05.088 6.417 11.228 19.817 11.228 6.054 0 7.813-3.104 10.814-3.104 3.104 0 4.967 3.104 4.967 3.104 1.811-4.45-2.69-9.52-2.69-9.52s5.122-11.848-10.71-22.61l-.001-.001L20.107.038z"}),React.createElement("path",{fill:"#FEFEFE",d:"M27.16 7.155c15.833 10.763 10.71 22.612 10.71 22.612s4.502 5.07 2.691 9.52c0 0-1.863-3.105-4.967-3.105-3.001 0-4.76 3.105-10.814 3.105-13.453 0-19.817-11.228-19.817-11.228 12.113 7.982 20.386 2.328 20.386 2.328-5.465-3.174-17.075-18.316-17.075-18.316 10.12 8.608 14.488 10.865 14.488 10.865-2.615-2.152-9.935-12.676-9.935-12.676 5.857 5.926 17.49 14.177 17.49 14.177 3.317-9.12-3.157-17.282-3.157-17.282"})));const ForwardRef=forwardRef(IconTechSwift);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-typescript.js b/src/components/Icon/glyphs/icon-tech-typescript.js new file mode 100644 index 0000000000..7753401893 --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-typescript.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechTypescript=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("defs",null,React.createElement("clipPath",{id:"clip0_1031_1977"},React.createElement("path",{fill:"#fff",d:"M0 0h48v48H0z"}))),React.createElement("g",{clipPath:"url(#clip0_1031_1977)"},React.createElement("path",{fill:"#007ACC",d:"M0 24V0h48v48H0"}),React.createElement("path",{fill:"#fff",fillRule:"evenodd",d:"M41.568 23.796c-.84-.876-1.776-1.428-3-1.716l.024-.024c-.828-.216-2.808-.288-3.648-.12-2.592.48-4.404 2.16-4.92 4.56-.168.684-.108 2.388.072 3.084.24.804.756 1.776 1.32 2.4.984 1.032 2.04 1.704 4.524 2.76 2.16.96 2.928 1.392 3.312 1.92.276.42.36.672.36 1.224 0 .6-.192 1.032-.636 1.44-1.032.936-3.12 1.044-4.68.24-.516-.288-1.404-1.128-1.8-1.752l-.312-.42-1.356.792-1.8 1.044-.456.288c-.048.084.804 1.368 1.248 1.848 1.128 1.236 2.964 2.196 4.884 2.556.9.156 2.82.18 3.66.036 2.676-.444 4.548-1.8 5.316-3.804.684-1.836.456-4.284-.564-5.844-.9-1.392-2.388-2.364-5.82-3.84-1.86-.816-2.46-1.212-2.784-1.872-.144-.312-.216-.528-.216-.912 0-1.26.96-2.016 2.4-1.92.996.072 1.632.456 2.256 1.344.192.312.384.516.432.48 1.26-.78 3.336-2.184 3.336-2.256-.048-.216-.708-1.056-1.152-1.536M10.524 26.04v-1.956l.036.012v-1.968l8.4-.036c4.62 0 8.424.012 8.424.048.048.024.048.9.048 1.98v1.92h-6.24V43.8h-4.428V26.04z",clipRule:"evenodd"})));const ForwardRef=forwardRef(IconTechTypescript);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-vercel.js b/src/components/Icon/glyphs/icon-tech-vercel.js new file mode 100644 index 0000000000..b55de12acd --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-vercel.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechVercel=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#000",d:"m23.856 6 20.785 36H3.07z"}));const ForwardRef=forwardRef(IconTechVercel);export default ForwardRef; diff --git a/src/components/Icon/glyphs/icon-tech-web.js b/src/components/Icon/glyphs/icon-tech-web.js new file mode 100644 index 0000000000..b15cb1b75a --- /dev/null +++ b/src/components/Icon/glyphs/icon-tech-web.js @@ -0,0 +1 @@ +import*as React from"react";import{forwardRef}from"react";const IconTechWeb=({title,titleId,...props},ref)=>React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:48,height:48,fill:"none",viewBox:"0 0 48 48",ref:ref,"aria-labelledby":titleId,...props},title?React.createElement("title",{id:titleId},title):null,React.createElement("path",{fill:"#03020D",fillRule:"evenodd",d:"M4 2h40a2 2 0 0 1 2 2v6H2V4a2 2 0 0 1 2-2m-4 8V4a4 4 0 0 1 4-4h40a4 4 0 0 1 4 4v40a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V10m2 2v32a2 2 0 0 0 2 2h40a2 2 0 0 0 2-2V12z",clipRule:"evenodd"}),React.createElement("path",{fill:"#03020D",d:"m14.816 32.52 1.584-6.036 1.596 6.036h1.488L21.812 24h-1.584l-1.488 6.12L17.144 24h-1.476l-1.608 6.12L12.584 24H11l2.328 8.52zM28.025 32.52v-1.404h-3.6v-2.172h3.336V27.54h-3.336v-2.136h3.54V24h-5.04v8.52zM33.073 32.52c1.692 0 2.64-1.02 2.64-2.364 0-.996-.54-1.812-1.524-2.172.612-.396.936-1.032.936-1.776 0-1.248-.9-2.208-2.52-2.208h-3.024v8.52zm-.216-3.576c.72 0 1.272.336 1.272 1.08s-.552 1.092-1.272 1.092H31.13v-2.172zm-.432-3.54c.624 0 1.128.324 1.128 1.044 0 .768-.504 1.092-1.128 1.092H31.13v-2.136zM14 6c0-1.09.91-2 2-2s2 .91 2 2-.91 2-2 2-2-.91-2-2M9 6c0-1.09.91-2 2-2s2 .91 2 2-.91 2-2 2-2-.91-2-2M4 6c0-1.09.91-2 2-2s2 .91 2 2-.91 2-2 2-2-.91-2-2"}));const ForwardRef=forwardRef(IconTechWeb);export default ForwardRef; diff --git a/src/components/Icon/glyphs/index.ts b/src/components/Icon/glyphs/index.ts new file mode 100644 index 0000000000..382c218c8e --- /dev/null +++ b/src/components/Icon/glyphs/index.ts @@ -0,0 +1,126 @@ +import type { ComponentType, SVGProps } from "react"; +import IconGuiAblyBadge from "./icon-gui-ably-badge"; +import IconGuiProdAiTransportOutline from "./icon-gui-prod-ai-transport-outline"; +import IconGuiProdAiTransportSolid from "./icon-gui-prod-ai-transport-solid"; +import IconGuiProdChatOutline from "./icon-gui-prod-chat-outline"; +import IconGuiProdChatSolid from "./icon-gui-prod-chat-solid"; +import IconGuiProdLiveobjectsOutline from "./icon-gui-prod-liveobjects-outline"; +import IconGuiProdLiveobjectsSolid from "./icon-gui-prod-liveobjects-solid"; +import IconGuiProdLivesyncOutline from "./icon-gui-prod-livesync-outline"; +import IconGuiProdLivesyncSolid from "./icon-gui-prod-livesync-solid"; +import IconGuiProdPubsubOutline from "./icon-gui-prod-pubsub-outline"; +import IconGuiProdPubsubSolid from "./icon-gui-prod-pubsub-solid"; +import IconGuiProdSpacesOutline from "./icon-gui-prod-spaces-outline"; +import IconGuiProdSpacesSolid from "./icon-gui-prod-spaces-solid"; +import IconGuiResources from "./icon-gui-resources"; +import IconProductAiTransportMono from "./icon-product-ai-transport-mono"; +import IconProductAiTransport from "./icon-product-ai-transport"; +import IconProductChatMono from "./icon-product-chat-mono"; +import IconProductChat from "./icon-product-chat"; +import IconProductLiveobjectsMono from "./icon-product-liveobjects-mono"; +import IconProductLiveobjects from "./icon-product-liveobjects"; +import IconProductLivesyncMono from "./icon-product-livesync-mono"; +import IconProductLivesync from "./icon-product-livesync"; +import IconProductPlatformMono from "./icon-product-platform-mono"; +import IconProductPlatform from "./icon-product-platform"; +import IconProductPubsubMono from "./icon-product-pubsub-mono"; +import IconProductPubsub from "./icon-product-pubsub"; +import IconProductSpacesMono from "./icon-product-spaces-mono"; +import IconProductSpaces from "./icon-product-spaces"; +import IconSocialDiscordMono from "./icon-social-discord-mono"; +import IconSocialGithubMono from "./icon-social-github-mono"; +import IconSocialGithub from "./icon-social-github"; +import IconSocialLinkedinMono from "./icon-social-linkedin-mono"; +import IconSocialStackoverflowMono from "./icon-social-stackoverflow-mono"; +import IconSocialXMono from "./icon-social-x-mono"; +import IconSocialYoutubeMono from "./icon-social-youtube-mono"; +import IconTechAndroidFull from "./icon-tech-android-full"; +import IconTechClaudeMono from "./icon-tech-claude-mono"; +import IconTechCsharp from "./icon-tech-csharp"; +import IconTechFlutter from "./icon-tech-flutter"; +import IconTechGo from "./icon-tech-go"; +import IconTechJava from "./icon-tech-java"; +import IconTechJavascript from "./icon-tech-javascript"; +import IconTechJson from "./icon-tech-json"; +import IconTechKotlin from "./icon-tech-kotlin"; +import IconTechLaravelBroadcast from "./icon-tech-laravel-broadcast"; +import IconTechNextjs from "./icon-tech-nextjs"; +import IconTechNodejs from "./icon-tech-nodejs"; +import IconTechObjectivec from "./icon-tech-objectivec"; +import IconTechOpenai from "./icon-tech-openai"; +import IconTechPhp from "./icon-tech-php"; +import IconTechPostgres from "./icon-tech-postgres"; +import IconTechPython from "./icon-tech-python"; +import IconTechReact from "./icon-tech-react"; +import IconTechReactnative from "./icon-tech-reactnative"; +import IconTechRuby from "./icon-tech-ruby"; +import IconTechSwift from "./icon-tech-swift"; +import IconTechTypescript from "./icon-tech-typescript"; +import IconTechVercel from "./icon-tech-vercel"; +import IconTechWeb from "./icon-tech-web"; + +export type GlyphProps = SVGProps & { title?: string; titleId?: string }; + +// Vendored from @ably/ui core/Icon/components (DX-1128). One self-contained SVG +// component per icon docs actually uses; keyed by the legacy icon name. +export const glyphs: Record> = { + "icon-gui-ably-badge": IconGuiAblyBadge, + "icon-gui-prod-ai-transport-outline": IconGuiProdAiTransportOutline, + "icon-gui-prod-ai-transport-solid": IconGuiProdAiTransportSolid, + "icon-gui-prod-chat-outline": IconGuiProdChatOutline, + "icon-gui-prod-chat-solid": IconGuiProdChatSolid, + "icon-gui-prod-liveobjects-outline": IconGuiProdLiveobjectsOutline, + "icon-gui-prod-liveobjects-solid": IconGuiProdLiveobjectsSolid, + "icon-gui-prod-livesync-outline": IconGuiProdLivesyncOutline, + "icon-gui-prod-livesync-solid": IconGuiProdLivesyncSolid, + "icon-gui-prod-pubsub-outline": IconGuiProdPubsubOutline, + "icon-gui-prod-pubsub-solid": IconGuiProdPubsubSolid, + "icon-gui-prod-spaces-outline": IconGuiProdSpacesOutline, + "icon-gui-prod-spaces-solid": IconGuiProdSpacesSolid, + "icon-gui-resources": IconGuiResources, + "icon-product-ai-transport-mono": IconProductAiTransportMono, + "icon-product-ai-transport": IconProductAiTransport, + "icon-product-chat-mono": IconProductChatMono, + "icon-product-chat": IconProductChat, + "icon-product-liveobjects-mono": IconProductLiveobjectsMono, + "icon-product-liveobjects": IconProductLiveobjects, + "icon-product-livesync-mono": IconProductLivesyncMono, + "icon-product-livesync": IconProductLivesync, + "icon-product-platform-mono": IconProductPlatformMono, + "icon-product-platform": IconProductPlatform, + "icon-product-pubsub-mono": IconProductPubsubMono, + "icon-product-pubsub": IconProductPubsub, + "icon-product-spaces-mono": IconProductSpacesMono, + "icon-product-spaces": IconProductSpaces, + "icon-social-discord-mono": IconSocialDiscordMono, + "icon-social-github-mono": IconSocialGithubMono, + "icon-social-github": IconSocialGithub, + "icon-social-linkedin-mono": IconSocialLinkedinMono, + "icon-social-stackoverflow-mono": IconSocialStackoverflowMono, + "icon-social-x-mono": IconSocialXMono, + "icon-social-youtube-mono": IconSocialYoutubeMono, + "icon-tech-android-full": IconTechAndroidFull, + "icon-tech-claude-mono": IconTechClaudeMono, + "icon-tech-csharp": IconTechCsharp, + "icon-tech-flutter": IconTechFlutter, + "icon-tech-go": IconTechGo, + "icon-tech-java": IconTechJava, + "icon-tech-javascript": IconTechJavascript, + "icon-tech-json": IconTechJson, + "icon-tech-kotlin": IconTechKotlin, + "icon-tech-laravel-broadcast": IconTechLaravelBroadcast, + "icon-tech-nextjs": IconTechNextjs, + "icon-tech-nodejs": IconTechNodejs, + "icon-tech-objectivec": IconTechObjectivec, + "icon-tech-openai": IconTechOpenai, + "icon-tech-php": IconTechPhp, + "icon-tech-postgres": IconTechPostgres, + "icon-tech-python": IconTechPython, + "icon-tech-react": IconTechReact, + "icon-tech-reactnative": IconTechReactnative, + "icon-tech-ruby": IconTechRuby, + "icon-tech-swift": IconTechSwift, + "icon-tech-typescript": IconTechTypescript, + "icon-tech-vercel": IconTechVercel, + "icon-tech-web": IconTechWeb, +}; diff --git a/src/components/Icon/heroicon.ts b/src/components/Icon/heroicon.ts new file mode 100644 index 0000000000..d1195166e8 --- /dev/null +++ b/src/components/Icon/heroicon.ts @@ -0,0 +1,34 @@ +import * as outline from '@heroicons/react/24/outline'; +import * as solid from '@heroicons/react/24/solid'; +import * as mini from '@heroicons/react/20/solid'; +import * as micro from '@heroicons/react/16/solid'; +import type { ComponentType } from 'react'; + +import type { GlyphProps } from './glyphs'; + +type HeroiconModule = Record>; + +// mini -> 20/solid, micro -> 16/solid (Heroicons size conventions). +const modules: Record = { + outline: outline as unknown as HeroiconModule, + solid: solid as unknown as HeroiconModule, + mini: mini as unknown as HeroiconModule, + micro: micro as unknown as HeroiconModule, +}; + +const toPascalCase = (value: string): string => + value + .split('-') + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(''); + +// `icon-gui--` resolves to a Heroicon — this is exactly what +// @ably/ui's Icon did internally, so the glyphs are identical. +export const resolveHeroicon = (name: string): ComponentType | null => { + const match = name.match(/^icon-gui-(.+)-(outline|solid|mini|micro)$/); + if (!match) { + return null; + } + const [, base, variant] = match; + return modules[variant]?.[`${toPascalCase(base)}Icon`] ?? null; +}; diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx new file mode 100644 index 0000000000..f0ad8cc9a2 --- /dev/null +++ b/src/components/Icon/index.tsx @@ -0,0 +1,60 @@ +import React, { useCallback, useId } from 'react'; + +import cn from 'src/utilities/cn'; + +import { glyphs, type GlyphProps } from './glyphs'; +import { resolveHeroicon } from './heroicon'; +import { setUniqueIds } from './set-unique-ids'; +import type { IconName } from './types'; + +export type { IconName }; + +export interface IconProps extends Omit, 'color'> { + name: IconName; + /** CSS width & height, e.g. "1rem" or "20px". */ + size?: string; + /** Tailwind text-colour class — applied via `currentColor`. */ + color?: string; + additionalCSS?: string; +} + +type SvgComponent = React.ForwardRefExoticComponent>; + +// +// docs-local Icon (DX-1128), replacing @ably/ui/core/Icon. `icon-gui-*` names +// resolve to Heroicons; everything else (custom gui, product, social and tech +// logos) comes from the vendored glyph registry. Unknown names render nothing, +// matching @ably/ui's behaviour for an unrecognised icon. +// +const Icon = ({ + name, + size = '0.75rem', + color, + additionalCSS = '', + className, + style, + ...rest +}: IconProps): React.ReactElement | null => { + const Component = (glyphs[name] ?? resolveHeroicon(name)) as SvgComponent | null; + + // Per-instance id rewriting so a gradient/mask-bearing glyph rendered more than + // once on a page doesn't collide on duplicate DOM ids (ported from @ably/ui). + const uniqueId = useId(); + const ref = useCallback((el: SVGSVGElement | null) => setUniqueIds(el, uniqueId), [uniqueId]); + + if (!Component) { + return null; + } + + return ( + + ); +}; + +export default Icon; diff --git a/src/components/Icon/set-unique-ids.ts b/src/components/Icon/set-unique-ids.ts new file mode 100644 index 0000000000..ee23995983 --- /dev/null +++ b/src/components/Icon/set-unique-ids.ts @@ -0,0 +1,34 @@ +// Ported from @ably/ui core/Icon/utils.ts (DX-1128). Rewrites in-SVG element ids +// (and the url(#…) references to them) to be unique per Icon instance, so multiple +// copies of a gradient/mask-bearing glyph on one page don't collide on duplicate +// DOM ids. +export const setUniqueIds = (el: SVGSVGElement | null, uniqueId: string): void => { + if (!el) { + return; + } + + const defsElements = el.querySelectorAll('defs [id]'); + const elementsWithUrls = el.querySelectorAll( + '[fill*="url("], [stroke*="url("], [filter*="url("], [clip-path*="url("], [mask*="url("]', + ); + + defsElements.forEach((def) => { + const oldId = def.id; + if (oldId.includes(uniqueId)) { + return; + } + + const newId = `${oldId}-${uniqueId}`; + def.id = newId; + + const regex = new RegExp(`url\\(#${oldId}\\)`, 'g'); + elementsWithUrls.forEach((element) => { + ['fill', 'stroke', 'filter', 'clip-path', 'mask'].forEach((attr) => { + const value = element.getAttribute(attr); + if (value && value.includes(`url(#${oldId})`)) { + element.setAttribute(attr, value.replace(regex, `url(#${newId})`)); + } + }); + }); + }); +}; diff --git a/src/components/Icon/types.ts b/src/components/Icon/types.ts new file mode 100644 index 0000000000..135e75ef27 --- /dev/null +++ b/src/components/Icon/types.ts @@ -0,0 +1,6 @@ +// Local icon name (DX-1128). Permissive template-literal type: covers the +// static `icon-gui-*` / `icon-social-*` / `icon-product-*` names and the +// dynamic `icon-tech-${language}` usages, while keeping the icon-* convention. +export type IconName = `icon-${string}`; + +export type IconSize = `${number}px` | `${number}em` | `${number}rem` | `calc(${string})`; diff --git a/src/components/Layout/Breadcrumbs.tsx b/src/components/Layout/Breadcrumbs.tsx index f0c080adab..240c8f3647 100644 --- a/src/components/Layout/Breadcrumbs.tsx +++ b/src/components/Layout/Breadcrumbs.tsx @@ -1,8 +1,8 @@ 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 Icon from 'src/components/Icon'; +import cn from 'src/utilities/cn'; import { hierarchicalKey } from './utils/nav'; const linkStyles = diff --git a/src/components/Layout/CopyForLLM.test.tsx b/src/components/Layout/CopyForLLM.test.tsx index 03d0de30f2..0812ed06cd 100644 --- a/src/components/Layout/CopyForLLM.test.tsx +++ b/src/components/Layout/CopyForLLM.test.tsx @@ -24,7 +24,7 @@ jest.mock('@reach/router', () => ({ useLocation: () => ({ pathname: '/docs/test-page' }), })); -jest.mock('@ably/ui/core/Icon', () => ({ +jest.mock('src/components/Icon', () => ({ __esModule: true, default: ({ name }: { name: string }) => {name}, })); diff --git a/src/components/Layout/CopyForLLM.tsx b/src/components/Layout/CopyForLLM.tsx index c176feaa44..5d8133b7bb 100644 --- a/src/components/Layout/CopyForLLM.tsx +++ b/src/components/Layout/CopyForLLM.tsx @@ -1,9 +1,9 @@ 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 Icon from '@ably/ui/core/Icon'; -import { IconName } from '@ably/ui/core/Icon/types'; +import cn from 'src/utilities/cn'; +import Icon from 'src/components/Icon'; +import { IconName } from 'src/components/Icon/types'; import { track } from '@ably/ui/core/insights'; import { productData } from 'src/data'; import { languageInfo } from 'src/data/languages'; diff --git a/src/components/Layout/Footer.tsx b/src/components/Layout/Footer.tsx index 79c8761358..b4118175f5 100644 --- a/src/components/Layout/Footer.tsx +++ b/src/components/Layout/Footer.tsx @@ -1,12 +1,12 @@ import React, { useEffect, useMemo, useState } from 'react'; 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 Icon from 'src/components/Icon'; +import { IconName } from 'src/components/Icon/types'; +import Status, { StatusUrl } from 'src/components/ui/Status'; +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'; +import Button from 'src/components/ui/Button'; const ENABLE_FEEDBACK = false; diff --git a/src/components/Layout/Header.test.tsx b/src/components/Layout/Header.test.tsx index 66f3f2ea13..b022a18249 100644 --- a/src/components/Layout/Header.test.tsx +++ b/src/components/Layout/Header.test.tsx @@ -17,13 +17,13 @@ jest.mock('src/contexts/layout-context', () => ({ }), })); -jest.mock('@ably/ui/core/Icon', () => { +jest.mock('src/components/Icon', () => { const MockIcon: React.FC<{ name: string }> = ({ name }) =>
{name}
; MockIcon.displayName = 'MockIcon'; return MockIcon; }); -jest.mock('@ably/ui/core/LinkButton', () => { +jest.mock('src/components/ui/LinkButton', () => { const MockButton: React.FC<{ children: React.ReactNode }> = ({ children }) => ; MockButton.displayName = 'MockButton'; return MockButton; diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index 6d8aa70d4d..af0c6e1b05 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -4,13 +4,13 @@ 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 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 cn from 'src/utilities/cn'; +import Icon from 'src/components/Icon'; +import TabMenu from 'src/components/ui/TabMenu'; +import Logo from 'src/images/ably-logo.svg'; import { track } from '@ably/ui/core/insights'; -import { componentMaxHeight, HEADER_BOTTOM_MARGIN, HEADER_HEIGHT } from '@ably/ui/core/utils/heights'; -import { IconName } from '@ably/ui/core/Icon/types'; +import { componentMaxHeight, HEADER_BOTTOM_MARGIN, HEADER_HEIGHT } from 'src/utilities/heights'; +import { IconName } from 'src/components/Icon/types'; import LeftSidebar from './LeftSidebar'; import ProductBar from './ProductBar'; import UserContext from 'src/contexts/user-context'; diff --git a/src/components/Layout/LanguageSelector.test.tsx b/src/components/Layout/LanguageSelector.test.tsx index 46fca66280..af1e136602 100644 --- a/src/components/Layout/LanguageSelector.test.tsx +++ b/src/components/Layout/LanguageSelector.test.tsx @@ -9,12 +9,12 @@ jest.mock('src/contexts/layout-context', () => ({ useLayoutContext: jest.fn(), })); -jest.mock('@ably/ui/core/Icon', () => ({ +jest.mock('src/components/Icon', () => ({ __esModule: true, default: ({ name }: { name: string }) =>
{name}
, })); -jest.mock('@ably/ui/core/Badge', () => ({ +jest.mock('src/components/ui/Badge', () => ({ __esModule: true, default: ({ children }: { children: React.ReactNode }) =>
{children}
, })); diff --git a/src/components/Layout/LanguageSelector.tsx b/src/components/Layout/LanguageSelector.tsx index d698114cd2..d166d21d13 100644 --- a/src/components/Layout/LanguageSelector.tsx +++ b/src/components/Layout/LanguageSelector.tsx @@ -1,10 +1,10 @@ import { useEffect, useMemo, useState } from 'react'; 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 Badge from 'src/components/ui/Badge'; +import Icon from 'src/components/Icon'; +import { IconName } from 'src/components/Icon/types'; +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..f5220684de 100644 --- a/src/components/Layout/LeftSidebar.tsx +++ b/src/components/Layout/LeftSidebar.tsx @@ -2,9 +2,9 @@ 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 Icon from '@ably/ui/core/Icon'; +import cn from 'src/utilities/cn'; +import { HEADER_HEIGHT } from 'src/utilities/heights'; +import Icon from 'src/components/Icon'; import { productData } from 'src/data'; import { ProductKey } from 'src/data/types'; diff --git a/src/components/Layout/MDXWrapper.test.tsx b/src/components/Layout/MDXWrapper.test.tsx index 600e58e645..8b947d6947 100644 --- a/src/components/Layout/MDXWrapper.test.tsx +++ b/src/components/Layout/MDXWrapper.test.tsx @@ -3,7 +3,7 @@ import { WindowLocation } from '@reach/router'; import { render, screen, waitFor } from '@testing-library/react'; import { Helmet } from 'react-helmet'; import If from './mdx/If'; -import CodeSnippet from '@ably/ui/core/CodeSnippet'; +import CodeSnippet from 'src/components/ui/CodeSnippet'; import UserContext from 'src/contexts/user-context'; import MDXWrapper from './MDXWrapper'; @@ -38,7 +38,7 @@ jest.mock('src/hooks/use-site-metadata', () => ({ })); // We need to mock minimal implementation of other dependencies that CodeSnippet might use -jest.mock('@ably/ui/core/Icon', () => { +jest.mock('src/components/Icon', () => { return { __esModule: true, default: ({ name, size, additionalCSS, color }: any) => ( @@ -54,7 +54,7 @@ jest.mock('@ably/ui/core/Icon', () => { }); // Mock Code component used by CodeSnippet -jest.mock('@ably/ui/core/Code', () => { +jest.mock('src/components/ui/Code', () => { return { __esModule: true, default: ({ language, snippet }: any) => ( diff --git a/src/components/Layout/MDXWrapper.tsx b/src/components/Layout/MDXWrapper.tsx index 4122b2c135..72bd5a5c6b 100644 --- a/src/components/Layout/MDXWrapper.tsx +++ b/src/components/Layout/MDXWrapper.tsx @@ -9,9 +9,9 @@ import React, { ReactElement, } from '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 CodeSnippet from 'src/components/ui/CodeSnippet'; +import type { CodeSnippetProps, SDKType } from 'src/components/ui/CodeSnippet'; +import cn from 'src/utilities/cn'; import { getRandomChannelName } from '../../utilities/get-random-channel-name'; diff --git a/src/components/Layout/ProductBar.test.tsx b/src/components/Layout/ProductBar.test.tsx index 37a5a4feaa..55444cffd2 100644 --- a/src/components/Layout/ProductBar.test.tsx +++ b/src/components/Layout/ProductBar.test.tsx @@ -7,7 +7,7 @@ jest.mock('src/contexts/layout-context', () => ({ useLayoutContext: jest.fn(), })); -jest.mock('@ably/ui/core/Icon', () => { +jest.mock('src/components/Icon', () => { const MockIcon: React.FC<{ name: string }> = ({ name }) =>
; MockIcon.displayName = 'MockIcon'; return MockIcon; diff --git a/src/components/Layout/ProductBar.tsx b/src/components/Layout/ProductBar.tsx index cdf38cbe3c..c25caa0bdd 100644 --- a/src/components/Layout/ProductBar.tsx +++ b/src/components/Layout/ProductBar.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; -import cn from '@ably/ui/core/utils/cn'; -import Icon from '@ably/ui/core/Icon'; -import { IconName } from '@ably/ui/core/Icon/types'; +import cn from 'src/utilities/cn'; +import Icon from 'src/components/Icon'; +import { IconName } from 'src/components/Icon/types'; import { productData } from 'src/data'; import { ProductKey } from 'src/data/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/RequiredBadge.tsx b/src/components/Layout/mdx/RequiredBadge.tsx index 3770452d9f..7a28fab4db 100644 --- a/src/components/Layout/mdx/RequiredBadge.tsx +++ b/src/components/Layout/mdx/RequiredBadge.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import Badge from '@ably/ui/core/Badge'; +import Badge from 'src/components/ui/Badge'; // The wrapping div with data-toc-exclude prevents this badge's text from // appearing in the "On this page" sidebar when rendered inside a heading. 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/mdx/tiles.tsx b/src/components/Layout/mdx/tiles.tsx index 47616d0887..68f20f3b7e 100644 --- a/src/components/Layout/mdx/tiles.tsx +++ b/src/components/Layout/mdx/tiles.tsx @@ -1,6 +1,6 @@ import Link from 'src/components/Link'; -import Icon from '@ably/ui/core/Icon'; -import { IconName } from '@ably/ui/core/Icon/types'; +import Icon from 'src/components/Icon'; +import { IconName } from 'src/components/Icon/types'; type TileProps = { title?: 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/Markdown/CodeBlock.test.tsx b/src/components/Markdown/CodeBlock.test.tsx index 0920f23383..fe2b547700 100644 --- a/src/components/Markdown/CodeBlock.test.tsx +++ b/src/components/Markdown/CodeBlock.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { CodeBlock } from './CodeBlock'; -jest.mock('@ably/ui/core/Icon', () => { +jest.mock('src/components/Icon', () => { return function MockIcon() { return ; }; @@ -24,7 +24,7 @@ const mockHighlightSnippet = jest.fn(); const mockParseLineHighlights = jest.fn(); const mockSplitHtmlLines = jest.fn(); -jest.mock('@ably/ui/core/utils/syntax-highlighter', () => ({ +jest.mock('src/utilities/syntax-highlighter', () => ({ highlightSnippet: (...args: any[]) => mockHighlightSnippet(...args), LINE_HIGHLIGHT_CLASSES: { addition: 'code-line-addition', @@ -36,7 +36,7 @@ jest.mock('@ably/ui/core/utils/syntax-highlighter', () => ({ registerDefaultLanguages: jest.fn(), })); -jest.mock('@ably/ui/core/utils/syntax-highlighter-registry', () => ({ +jest.mock('src/utilities/syntax-highlighter-registry', () => ({ __esModule: true, default: [], })); diff --git a/src/components/Markdown/CodeBlock.tsx b/src/components/Markdown/CodeBlock.tsx index 55613b5ded..1a17e9fe11 100644 --- a/src/components/Markdown/CodeBlock.tsx +++ b/src/components/Markdown/CodeBlock.tsx @@ -1,14 +1,14 @@ import React, { FC, useMemo } from 'react'; import DOMPurify from 'dompurify'; -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/Icon'; import { highlightSnippet, LINE_HIGHLIGHT_CLASSES, registerDefaultLanguages, parseLineHighlights, splitHtmlLines, -} from '@ably/ui/core/utils/syntax-highlighter'; -import languagesRegistry from '@ably/ui/core/utils/syntax-highlighter-registry'; +} from 'src/utilities/syntax-highlighter'; +import languagesRegistry from 'src/utilities/syntax-highlighter-registry'; registerDefaultLanguages(languagesRegistry); 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/Redoc/GoTopButton.tsx b/src/components/Redoc/GoTopButton.tsx index f4749684d2..d067bdbe74 100644 --- a/src/components/Redoc/GoTopButton.tsx +++ b/src/components/Redoc/GoTopButton.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/Icon'; export const GoTopButton = () => { const [showGoTop, setShowGoTop] = useState(false); diff --git a/src/components/SDKsPage/Card/index.tsx b/src/components/SDKsPage/Card/index.tsx index d022f0eceb..b279d96faa 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 Icon from 'src/components/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/blocks/dividers/Aside.tsx b/src/components/blocks/dividers/Aside.tsx index 151a323bad..531292ecd8 100644 --- a/src/components/blocks/dividers/Aside.tsx +++ b/src/components/blocks/dividers/Aside.tsx @@ -1,4 +1,4 @@ -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/Icon'; import { inlineGridParagraph, inlineContentContainer, diff --git a/src/components/blocks/software/Code/ApiKeyIndicator.tsx b/src/components/blocks/software/Code/ApiKeyIndicator.tsx index f57ae7c564..35684abe6a 100644 --- a/src/components/blocks/software/Code/ApiKeyIndicator.tsx +++ b/src/components/blocks/software/Code/ApiKeyIndicator.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useState } from 'react'; -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/Icon'; type ApiKeyIndicatorProps = { tooltip: string }; diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx new file mode 100644 index 0000000000..70ab0cd5c9 --- /dev/null +++ b/src/components/ui/Badge.tsx @@ -0,0 +1,164 @@ +import React, { PropsWithChildren, useMemo } from 'react'; +import { IconName, IconSize } from 'src/components/Icon/types'; +import Icon from 'src/components/Icon'; +import cn from 'src/utilities/cn'; +import { ColorClassColorGroups } from './colors'; + +/** + * Props for the Badge component. + */ +export interface BadgeProps { + /** + * The size of the badge. Can be one of "xs", "sm", "md", or "lg". + */ + size?: 'xs' | 'sm' | 'md' | 'lg'; + + /** + * The color of the badge. Can be a value from ColorClassColorGroups or "red". + */ + color?: ColorClassColorGroups | 'red'; + + /** + * The name of the icon to be displayed before the children in the badge. + */ + iconBefore?: IconName; + + /** + * The name of the icon to be displayed after the children in the badge. + */ + iconAfter?: IconName; + + /** + * Additional CSS class names to apply to the badge. + */ + className?: string; + + /** + * Whether the badge is disabled. Defaults to false. + */ + disabled?: boolean; + + /** + * Whether the badge is focusable. Defaults to false. + */ + focusable?: boolean; + + /** + * Whether the badge is hoverable. Defaults to false. + */ + hoverable?: boolean; + + /** + * The size of the icons in the badge. If not provided, it will be derived from the badge size. + */ + iconSize?: IconSize; + + /** + * Accessible label for the badge when interactive + */ + ariaLabel?: string; + + /** + * Additional CSS class names to apply to the children of the badge. + */ + childClassName?: string; +} + +const defaultIconSizeByBadgeSize: Record, IconSize> = { + lg: '16px', + md: '15px', + sm: '14px', + xs: '13px', +}; + +const Badge: React.FC> = ({ + size = 'md', + color = 'neutral', + iconBefore, + iconAfter, + className, + childClassName, + children, + disabled = false, + focusable = false, + hoverable = false, + iconSize, + ariaLabel, +}) => { + const sizeClass = useMemo(() => { + switch (size) { + case 'xs': + return 'px-2 py-0 text-[10px] leading-tight'; + case 'sm': + return 'px-2 py-0.5 text-[10px] leading-tight'; + case 'md': + return 'px-2.5 py-0.5 text-[11px] leading-normal'; + case 'lg': + return 'px-3 py-[0.1875rem] text-[12px] leading-normal'; + } + }, [size]); + + const childClass = useMemo(() => { + switch (size) { + case 'xs': + case 'sm': + return 'leading-[18px]'; + case 'md': + case 'lg': + return 'leading-[20px]'; + } + }, [size]); + + const colorClass = useMemo(() => { + switch (color) { + case 'neutral': + return 'text-neutral-900 dark:text-neutral-400'; + case 'violet': + return 'text-violet-400'; + case 'orange': + return 'text-orange-600'; + case 'yellow': + return 'text-yellow-600'; + case 'green': + return 'text-green-600'; + case 'blue': + return 'text-blue-600'; + case 'pink': + return 'text-pink-600'; + case 'red': + return 'text-orange-700'; + } + }, [color]); + + const computedIconSize = iconSize ?? defaultIconSizeByBadgeSize[size]; + + return ( +
+ {iconBefore ? : null} + + {children} + + {iconAfter ? : null} +
+ ); +}; + +export default Badge; diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 0000000000..b6cdaf9433 --- /dev/null +++ b/src/components/ui/Button.tsx @@ -0,0 +1,113 @@ +import React, { PropsWithChildren } from 'react'; +import { IconName } from 'src/components/Icon/types'; +import Icon from 'src/components/Icon'; +import cn from 'src/utilities/cn'; +import { ColorClass, ColorThemeSet } from './colors'; + +export type ButtonType = 'priority' | 'primary' | 'secondary'; + +type ButtonSize = 'lg' | 'md' | 'sm' | 'xs'; + +export type ButtonPropsBase = { + /** + * The type of button: priority, primary, or secondary. + */ + variant?: ButtonType; + /** + * The button size: lg, sm, or xs. Leave empty for md. + */ + size?: ButtonSize; + /** + * An icon to render on the left side of the button label. + */ + leftIcon?: IconName; + /** + * An icon to render on the right side of the button label. + */ + rightIcon?: IconName; + /** + * Optional classes to add to the button element. + */ + className?: string; + /** + * Optional color to apply to the icon on either left and/or right side of the button. + */ + iconColor?: ColorClass | ColorThemeSet; +}; + +type ButtonProps = ButtonPropsBase & React.ButtonHTMLAttributes; + +// got to go the long way round because of ol' mate Taily Waily +const buttonClasses: Record> = { + priority: { + lg: 'ui-button-priority-lg', + md: 'ui-button-priority', + sm: 'ui-button-priority-sm', + xs: 'ui-button-priority-xs', + }, + primary: { + lg: 'ui-button-primary-lg', + md: 'ui-button-primary', + sm: 'ui-button-primary-sm', + xs: 'ui-button-primary-xs', + }, + secondary: { + lg: 'ui-button-secondary-lg', + md: 'ui-button-secondary', + sm: 'ui-button-secondary-sm', + xs: 'ui-button-secondary-xs', + }, +}; + +export const iconModifierClasses: Record = { + lg: { left: 'ui-button-lg-left-icon', right: 'ui-button-lg-right-icon' }, + md: { left: 'ui-button-left-icon', right: 'ui-button-right-icon' }, + sm: { left: 'ui-button-sm-left-icon', right: 'ui-button-sm-right-icon' }, + xs: { left: '', right: '' }, +}; + +export const commonButtonProps = (props: ButtonPropsBase) => { + const { variant = 'primary', size, leftIcon, rightIcon, className } = props; + + return { + className: cn( + buttonClasses[variant][size ?? 'md'], + { [iconModifierClasses[size ?? 'md'].left]: leftIcon }, + { [iconModifierClasses[size ?? 'md'].right]: rightIcon }, + className, + ), + }; +}; + +export const commonButtonInterior = (props: PropsWithChildren) => { + const { leftIcon, rightIcon, iconColor, children } = props; + return ( + <> + {leftIcon ? : null} + {children} + {rightIcon ? : null} + + ); +}; + +const Button: React.FC> = ({ + variant = 'primary', + size, + leftIcon, + rightIcon, + children, + className, + iconColor, + ...rest +}) => { + return ( + + ); +}; + +export default Button; 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/Code.tsx b/src/components/ui/Code.tsx new file mode 100644 index 0000000000..66244118a8 --- /dev/null +++ b/src/components/ui/Code.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import { + highlightSnippet, + LINE_HIGHLIGHT_CLASSES, + registerDefaultLanguages, + splitHtmlLines, +} from 'src/utilities/syntax-highlighter'; +import languagesRegistry from 'src/utilities/syntax-highlighter-registry'; +import cn from 'src/utilities/cn'; + +registerDefaultLanguages(languagesRegistry); + +export type LineHighlightType = 'addition' | 'removal' | 'highlight'; + +type CodeProps = { + language: string; + snippet: string; + textSize?: string; + padding?: string; + additionalCSS?: string; + showLines?: boolean; + lineCSS?: string; + wrap?: boolean; + lineHighlights?: Record; +}; + +const Code = ({ + language, + snippet, + textSize = 'ui-text-code', + padding = 'p-8', + additionalCSS = '', + showLines, + lineCSS, + wrap = false, + lineHighlights, +}: CodeProps) => { + // Trim the snippet and remove trailing empty lines + const trimmedSnippet = snippet.trimEnd(); + const HTMLraw = highlightSnippet(language, trimmedSnippet) ?? ''; + const className = `language-${language} ${textSize}`; + + // Calculate line count after removing trailing empty lines + const lines = trimmedSnippet.split(/\r\n|\r|\n/); + const lineCount = lines.length; + + const hasHighlights = lineHighlights && Object.keys(lineHighlights).length > 0; + + // Per-line rendering when highlights are present + if (hasHighlights) { + const htmlLines = splitHtmlLines(HTMLraw); + + return ( +
+
+          
+            {htmlLines.map((lineHtml, i) => {
+              const lineNum = i + 1;
+              const highlightType = lineHighlights[lineNum];
+              const highlightClass = highlightType ? LINE_HIGHLIGHT_CLASSES[highlightType] : undefined;
+
+              return (
+                
+                  {showLines && (
+                    
+                      {lineNum}
+                    
+                  )}
+                  
+                
+              );
+            })}
+          
+        
+
+ ); + } + + // Default: single-block rendering (no highlights) + return ( +
+ {showLines ? ( +
+ {[...Array(lineCount)].map((_, i) => ( +

+ {i + 1} +

+ ))} +
+ ) : null} +
+        
+      
+
+ ); +}; + +export default Code; diff --git a/src/components/ui/CodeSnippet.tsx b/src/components/ui/CodeSnippet.tsx new file mode 100644 index 0000000000..5ae3714999 --- /dev/null +++ b/src/components/ui/CodeSnippet.tsx @@ -0,0 +1,597 @@ +import React, { useState, useEffect, Children, isValidElement, useRef, useCallback, useMemo } from 'react'; +import Code from 'src/components/ui/Code'; +import type { LineHighlightType } from 'src/components/ui/Code'; +import cn from 'src/utilities/cn'; +import { parseLineHighlights } from 'src/utilities/syntax-highlighter'; +import Icon from 'src/components/Icon'; +import { getLanguageInfo, stripSdkType, SDK_PREFIXES, SDKType } from './CodeSnippet/languages'; +import LanguageSelector from './CodeSnippet/LanguageSelector'; +import ApiKeySelector from './CodeSnippet/ApiKeySelector'; +import PlainCodeView from './CodeSnippet/PlainCodeView'; +import CopyButton from './CodeSnippet/CopyButton'; +import SegmentedControl from 'src/components/ui/SegmentedControl'; + +// Re-export SDKType for consumers +export type { SDKType }; + +// Define API key types +export type ApiKeysItem = { + app: string; + keys: { name: string; key: string }[]; +}; + +export type CodeSnippetProps = { + /** + * If true, hides the language selector row completely + */ + fixed?: boolean; + /** + * If true, renders a macOS-style window header with buttons and title + */ + headerRow?: boolean; + /** + * Title to display in the header row (when headerRow is true) + */ + title?: string; + /** + * Children elements with lang attribute + */ + children: React.ReactNode; + /** + * Additional CSS classes + */ + className?: string; + /** + * Default language to display. If not found in available languages, first available is used. + * If found in languages but no matching snippet exists, a message is displayed. + */ + lang: string | null; + /** + * Callback fired when the active language changes + */ + onChange?: (language: string, sdk?: SDKType) => void; + /** + * List of API keys to display in a dropdown + */ + apiKeys?: ApiKeysItem[]; + /** + * Default SDK type to use for the code snippet + */ + sdk?: SDKType; + /** + * Whether to show line numbers in code snippets + */ + showCodeLines?: boolean; + /** + * Defines the order in which languages should be displayed. + * Languages not in this array will be shown after those that are included. + */ + languageOrdering?: string[]; + /** + * Whether to wrap code content instead of scrolling + */ + wrapCode?: boolean; +}; + +// Substitution function for API key placeholders +const substituteApiKey = (content: string, apiKey: string, mask = true): string => { + return content.replace(/\{\{API_KEY\}\}/g, mask ? `${apiKey.split(':')[0]}:*****` : apiKey); +}; + +/** + * CodeSnippet component that displays code with language switching capability + */ +const CodeSnippet: React.FC = ({ + fixed = false, + headerRow = false, + title = 'Code', + children, + className, + lang, + onChange, + apiKeys, + sdk, + showCodeLines = true, + languageOrdering, + wrapCode = false, +}) => { + const codeRef = useRef(null); + + const [selectedApiKey, setSelectedApiKey] = useState(() => apiKeys?.[0]?.keys?.[0]?.key ?? ''); + const [prevApiKeys, setPrevApiKeys] = useState(apiKeys); + if (prevApiKeys !== apiKeys) { + setPrevApiKeys(apiKeys); + if (!selectedApiKey && apiKeys && apiKeys.length > 0) { + setSelectedApiKey(apiKeys[0].keys?.[0]?.key ?? ''); + } + } + + useEffect(() => { + const element = codeRef.current; + if (!element) { + return; + } + + // Detects the key masking via substituteApiKey (i.e. "abcde:*****") and replaces it with the actual API key + const unmaskRenderedApiKey = (content: string, apiKey: string): string => { + return content.replace(/(['"]?)([^:'"]+):\*{5}\1/g, `$1${apiKey}$1`); + }; + + const handleCopy = (event: ClipboardEvent) => { + const selection = window.getSelection(); + if (!selection || selection.rangeCount === 0) { + return; + } + + const selectedText = selection.toString(); + if (!selectedText) { + return; + } + + // Check if the selection is within our element + const range = selection.getRangeAt(0); + if (!element.contains(range.commonAncestorContainer)) { + return; + } + + const modifiedText = unmaskRenderedApiKey(selectedText, selectedApiKey); + + event.clipboardData?.setData('text/plain', modifiedText); + event.preventDefault(); + }; + + document.addEventListener('copy', handleCopy); + + return () => { + document.removeEventListener('copy', handleCopy); + }; + }, [selectedApiKey]); + + const extractLanguageFromCode = useCallback((codeElement: React.ReactElement | null): string | null => { + if (!codeElement || !codeElement.props.className) { + return null; + } + + const classNames = codeElement.props.className.split(' '); + const langClass = classNames.find((cls: string) => cls.startsWith('language-')); + if (!langClass) { + return null; + } + + return langClass.substring(9); // Remove "language-" prefix + }, []); + + // Helper to find the code element within pre's children (handles both single element and array) + const findCodeElement = useCallback((preChildren: React.ReactNode): React.ReactElement | null => { + if (isValidElement(preChildren)) { + return preChildren; + } + if (Array.isArray(preChildren)) { + const codeEl = preChildren.find((c) => isValidElement(c)); + return codeEl && isValidElement(codeEl) ? codeEl : null; + } + return null; + }, []); + + const { codeData, languages, sdkTypes, isSinglePlainCommand } = useMemo(() => { + const childrenArray = Children.toArray(children); + const languages: string[] = []; + const sdkTypes = new Set(); + const codeData: { + language: string; + content: string; + lineHighlights: Record; + }[] = []; + + const isSinglePlainCommand = + childrenArray.length === 1 && + ['language-shell', 'language-text'].some((lang) => { + if (!isValidElement(childrenArray[0])) { + return false; + } + const codeEl = findCodeElement(childrenArray[0].props.children); + return codeEl?.props.className?.includes(lang); + }); + + childrenArray.forEach((child) => { + if (!isValidElement(child)) { + return; + } + + const preElement = child; + const codeElement = findCodeElement(preElement.props.children); + + if (!codeElement) { + return; + } + + const rawLanguage = extractLanguageFromCode(codeElement); + + if (!rawLanguage) { + return; + } + + const meta: string | undefined = codeElement.props?.['data-meta']; + const { lang: codeLanguage, highlights: lineHighlights } = parseLineHighlights(rawLanguage, meta); + + for (const prefix of SDK_PREFIXES) { + if (codeLanguage.startsWith(`${prefix}_`)) { + sdkTypes.add(prefix); + break; + } + } + + if (!languages.includes(codeLanguage)) { + languages.push(codeLanguage); + } + + const codeContent = codeElement.props.children; + codeData.push({ + language: codeLanguage, + content: codeContent, + lineHighlights, + }); + }); + + return { + codeData, + languages, + sdkTypes, + isSinglePlainCommand, + }; + }, [children, extractLanguageFromCode, findCodeElement]); + + // Resolve which SDK type to filter by. If the snippet only contains one SDK type + // and it doesn't match the current selector, fall back to the only available type. + // Returns undefined when no SDK prop is provided (e.g. plain code blocks without SDK prefixes). + const resolvedSdk: SDKType | undefined = useMemo(() => { + if (sdkTypes.size === 1 && sdk && !sdkTypes.has(sdk)) { + return Array.from(sdkTypes)[0]; + } + return sdk; + }, [sdk, sdkTypes]); + + // Only show SDK selector for realtime/rest types, not for client/agent (which are controlled by page-level selector) + const showSDKSelector = sdkTypes.has('realtime') || sdkTypes.has('rest'); + + const filteredLanguages = useMemo(() => { + const filtered = + !resolvedSdk || !showSDKSelector + ? [...languages] + : languages.filter((lang) => lang.startsWith(`${resolvedSdk}_`)); + + // Apply custom ordering if provided + if (languageOrdering && languageOrdering.length > 0) { + filtered.sort((a, b) => { + const aBase = stripSdkType(a); + const bBase = stripSdkType(b); + + const aIndex = languageOrdering.indexOf(aBase); + const bIndex = languageOrdering.indexOf(bBase); + + if (aIndex !== -1 && bIndex !== -1) { + return aIndex - bIndex; + } + if (aIndex !== -1) { + return -1; + } + if (bIndex !== -1) { + return 1; + } + return 0; + }); + } + + return filtered; + }, [resolvedSdk, showSDKSelector, languages, languageOrdering]); + + const activeLanguage = useMemo(() => { + // For client/agent SDK types (controlled by page-level selector), construct the full language + if (resolvedSdk === 'client' || resolvedSdk === 'agent') { + const fullLang = `${resolvedSdk}_${lang}`; + // Verify this language exists in available languages + if (languages.includes(fullLang)) { + return fullLang; + } + // Fall back to first language with this prefix + const prefixMatch = languages.find((l) => l.startsWith(`${resolvedSdk}_`)); + if (prefixMatch) { + return prefixMatch; + } + } + + // For realtime/rest SDK types + if (resolvedSdk && sdkTypes.has(resolvedSdk)) { + return `${resolvedSdk}_${lang}`; + } + + if (lang) { + return lang; + } + + if (filteredLanguages.length > 0) { + return filteredLanguages[0]; + } + + return languages[0]; + }, [resolvedSdk, sdkTypes, lang, filteredLanguages, languages]); + + const requiresApiKeySubstitution = useMemo(() => { + const containsPlaceholder = codeData.some((code) => code?.content.includes('{{API_KEY}}')); + + return containsPlaceholder && !!apiKeys && apiKeys.length > 0 && !!selectedApiKey; + }, [codeData, apiKeys, selectedApiKey]); + + const [isHovering, setIsHovering] = useState(false); + + const hasOnlyJsonSnippet = useMemo(() => languages.length === 1 && languages[0] === 'json', [languages]); + + const processedChildren = useMemo(() => { + if (!activeLanguage) { + return []; + } + + const targetLanguage = hasOnlyJsonSnippet ? 'json' : activeLanguage; + + return codeData + .filter((code) => { + return code?.language === targetLanguage; + }) + .map((code) => { + if (!code) { + return null; + } + + const cleanLang = hasOnlyJsonSnippet ? 'json' : code.language; + const langInfo = getLanguageInfo(cleanLang ?? ''); + + if (typeof code.content === 'string' || typeof code.content === 'number' || typeof code.content === 'boolean') { + // Apply API key substitution if apiKeys are provided + let processedContent = String(code.content); + if (requiresApiKeySubstitution) { + processedContent = substituteApiKey(processedContent, selectedApiKey); + } + + if (!langInfo.syntaxHighlighterKey || !cleanLang) { + return null; + } + + return ( + 0 ? code.lineHighlights : undefined} + /> + ); + } + + return null; + }); + }, [ + activeLanguage, + hasOnlyJsonSnippet, + codeData, + requiresApiKeySubstitution, + showCodeLines, + wrapCode, + selectedApiKey, + ]); + + const hasSnippetForActiveLanguage = useMemo(() => { + if (!activeLanguage) { + return false; + } + if (hasOnlyJsonSnippet) { + return true; + } + + return codeData.some((code) => { + return code?.language === activeLanguage; + }); + }, [activeLanguage, hasOnlyJsonSnippet, codeData]); + + const handleSDKTypeChange = useCallback( + (type: SDKType) => { + const nextLang = stripSdkType( + languages.find((l) => l === `${type}_${stripSdkType(activeLanguage)}`) ?? + languages.find((l) => l.startsWith(`${type}_`)) ?? + activeLanguage, + ); + + if (onChange && nextLang) { + onChange(stripSdkType(activeLanguage), type); + } + }, + [activeLanguage, languages, onChange], + ); + + const handleLanguageChange = useCallback( + (language: string) => { + if (onChange) { + onChange(stripSdkType(language), resolvedSdk); + } + }, + [onChange, resolvedSdk], + ); + + const noSnippetMessage = useMemo(() => { + if (!activeLanguage) { + return null; + } + + const activeLanguageInfo = getLanguageInfo(activeLanguage); + + return ( +
+ +

+ You're currently viewing the {activeLanguageInfo.label} docs. There either isn't a{' '} + {activeLanguageInfo.label} code sample for this example, or this feature isn't supported in{' '} + {activeLanguageInfo.label}. Switch language to view this example in a different language, or check which SDKs + support this feature. +

+
+ ); + }, [activeLanguage]); + + const showLanguageSelector = !fixed && filteredLanguages.length > 0; + const showFullSelector = filteredLanguages.length > 1; + // Show a read-only language label when fixed (controlled by external selector) + const showFixedLanguageLabel = fixed && activeLanguage; + + const renderLanguageLabel = (langKey: string, onClick?: () => void) => ( +
+
+ + + {getLanguageInfo(langKey).label} + +
+
+ ); + + const renderContent = useMemo(() => { + if (!activeLanguage) { + return null; + } + + if (hasSnippetForActiveLanguage) { + return processedChildren; + } + + return noSnippetMessage; + }, [activeLanguage, hasSnippetForActiveLanguage, processedChildren, noSnippetMessage]); + + // Render special case for plain commands (shell or text) + if (isSinglePlainCommand) { + const plainChild = codeData[0]; + if (plainChild) { + const codeContent = plainChild.content; + const language = plainChild.language; + + if (!language || !codeContent) { + return null; + } + + // Apply API key substitution if apiKeys are provided + let processedContent = String(codeContent); + if (requiresApiKeySubstitution) { + processedContent = substituteApiKey(processedContent, selectedApiKey); + } + + return ( + + ); + } + } + + return ( +
+ {headerRow && ( +
+
+
+
+
+
+ +
{title}
+ +
+
+ )} + + {showSDKSelector && ( +
+
+ {['realtime', 'rest'].map( + (type) => + sdkTypes.has(type as SDKType) && ( + handleSDKTypeChange(type as SDKType)} + size="xs" + active={resolvedSdk === type} + className={cn( + 'text-[11px] font-semibold px-2 py-1 h-auto', + sdkTypes.size === 1 && + 'pointer-events-none bg-neutral-100 dark:bg-neutral-1200 !text-neutral-800 !dark:text-neutral-500', + sdkTypes.size > 1 && + resolvedSdk !== type && + 'bg-neutral-100 dark:bg-neutral-1200 hover:bg-neutral-200 dark:hover:bg-neutral-1100 active:bg-neutral-400 dark:active:bg-neutral-900', + sdkTypes.size > 1 && resolvedSdk === type && 'bg-neutral-000 dark:bg-neutral-1100', + )} + > + {type === 'realtime' ? 'Realtime' : 'REST'} + + ), + )} +
+
+ )} + + {showFixedLanguageLabel && renderLanguageLabel(activeLanguage)} + + {showLanguageSelector && + (showFullSelector ? ( + + ) : ( + renderLanguageLabel(filteredLanguages[0], () => handleLanguageChange(filteredLanguages[0])) + ))} +
setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + onFocus={() => setIsHovering(true)} + onBlur={() => setIsHovering(false)} + > + {renderContent} + {isHovering && activeLanguage && hasSnippetForActiveLanguage && ( + { + const text = codeData.find((code) => code.language === activeLanguage)?.content; + if (text) { + navigator.clipboard.writeText(substituteApiKey(text, selectedApiKey, false)); + } + }} + /> + )} +
+ {requiresApiKeySubstitution && ( + + )} +
+ ); +}; + +export default CodeSnippet; diff --git a/src/components/ui/CodeSnippet/ApiKeySelector.tsx b/src/components/ui/CodeSnippet/ApiKeySelector.tsx new file mode 100644 index 0000000000..50bba0a8ca --- /dev/null +++ b/src/components/ui/CodeSnippet/ApiKeySelector.tsx @@ -0,0 +1,125 @@ +import React, { useMemo } from 'react'; +import * as Select from '@radix-ui/react-select'; +import Badge from 'src/components/ui/Badge'; +import Icon from 'src/components/Icon'; +import Tooltip from 'src/components/ui/Tooltip'; +import type { ApiKeysItem } from '../CodeSnippet'; + +type ApiKeySelectorProps = { + apiKeys?: ApiKeysItem[]; + selectedApiKey: string; + onApiKeyChange: (apiKey: string) => void; +}; + +const ApiKeySelector = ({ apiKeys, selectedApiKey, onApiKeyChange }: ApiKeySelectorProps) => { + const isDemoMode = useMemo(() => apiKeys?.length === 1 && apiKeys[0].app === 'demo', [apiKeys]); + + const renderDemoMode = useMemo( + () => ( +
+ DEMO ONLY + + + +
+ } + > + This code example uses a temporary key that is rate limited and expires in 4 hrs. Sign in to Ably to use your + API keys instead. + +
+ ), + [], + ); + + const renderApiKeyDropdown = useMemo(() => { + if (isDemoMode) { + return renderDemoMode; + } + + if (!apiKeys?.length) { + return null; + } + + return ( + + + + + + + + + + + + + + + + {apiKeys.map((apiKeyItem) => ( + + {apiKeys.length > 1 && ( + + {apiKeyItem.app} + + )} + {apiKeyItem.keys.map(({ name, key }) => ( + + + {key.length > 10 ? `${key.substring(0, 10)}...` : key} + {name && ` - ${name}`} + + + + + + ))} + + ))} + + + + + + + + + ); + }, [apiKeys, isDemoMode, selectedApiKey, onApiKeyChange, renderDemoMode]); + + return ( +
+ API key: + {renderApiKeyDropdown} +
+ ); +}; + +export default ApiKeySelector; diff --git a/src/components/ui/CodeSnippet/CopyButton.tsx b/src/components/ui/CodeSnippet/CopyButton.tsx new file mode 100644 index 0000000000..0e33645a06 --- /dev/null +++ b/src/components/ui/CodeSnippet/CopyButton.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react'; +import Icon from 'src/components/Icon'; +import TooltipButton from './TooltipButton'; + +type CopyButtonProps = { + onCopy: () => void; + tooltip?: string; +}; + +const CopyButton = ({ onCopy, tooltip = 'Copy' }: CopyButtonProps) => { + const [isCopied, setIsCopied] = useState(false); + const [isHovering, setIsHovering] = useState(false); + + return ( +
setIsHovering(true)} + onMouseLeave={() => { + setIsHovering(false); + + setTimeout(() => { + setIsCopied(false); + }, 250); + }} + > + { + onCopy(); + setIsCopied(true); + }} + tooltipRootProps={{ + open: isHovering, + }} + variant="icon-button" + > + + +
+ ); +}; + +export default CopyButton; diff --git a/src/components/ui/CodeSnippet/LanguageSelector.tsx b/src/components/ui/CodeSnippet/LanguageSelector.tsx new file mode 100644 index 0000000000..4a89d0f455 --- /dev/null +++ b/src/components/ui/CodeSnippet/LanguageSelector.tsx @@ -0,0 +1,108 @@ +import React, { useMemo } from 'react'; +import * as Select from '@radix-ui/react-select'; +import Icon from 'src/components/Icon'; +import TooltipButton from './TooltipButton'; +import { getLanguageInfo } from './languages'; + +type LanguageSelectorProps = { + languages: string[]; + activeLanguage: string; + onLanguageChange: (language: string) => void; +}; + +const LanguageSelector = ({ languages, activeLanguage, onLanguageChange }: LanguageSelectorProps) => { + const desktopLanguageElements = useMemo( + () => + languages.map((lang) => { + const active = activeLanguage === lang; + const displayName = getLanguageInfo(lang).label; + + return ( + onLanguageChange(lang)} + icon={getLanguageInfo(lang).icon} + variant="segmented" + size="xs" + > + {displayName} + + ); + }), + [languages, activeLanguage, onLanguageChange], + ); + + const mobileLanguageElements = useMemo( + () => + languages.map((lang) => ( + + +
+ + {getLanguageInfo(lang).label} +
+
+ + + +
+ )), + [languages], + ); + + const mobileSelectValue = useMemo( + () => + activeLanguage ? ( +
+ + {getLanguageInfo(activeLanguage).label} +
+ ) : null, + [activeLanguage], + ); + + return ( +
+
{desktopLanguageElements}
+ +
+ + + {mobileSelectValue} + + + + + + + + + + + + {mobileLanguageElements} + + + + + + + +
+
+ ); +}; + +export default LanguageSelector; diff --git a/src/components/ui/CodeSnippet/PlainCodeView.tsx b/src/components/ui/CodeSnippet/PlainCodeView.tsx new file mode 100644 index 0000000000..147dc966b8 --- /dev/null +++ b/src/components/ui/CodeSnippet/PlainCodeView.tsx @@ -0,0 +1,58 @@ +import React, { useRef, useState } from 'react'; +import Icon from 'src/components/Icon'; +import Code from 'src/components/ui/Code'; +import cn from 'src/utilities/cn'; +import CopyButton from './CopyButton'; +import { IconName } from 'src/components/Icon/types'; + +type PlainCodeViewProps = { + content: string; + language: string; + icon: IconName | null; + className?: string; +}; + +const PlainCodeView: React.FC = ({ content, className, language, icon }) => { + const codeRef = useRef(null); + const [isHovering, setIsHovering] = useState(false); + + return ( +
setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + onFocus={() => setIsHovering(true)} + onBlur={() => setIsHovering(false)} + tabIndex={0} + role="button" + aria-label="Focusable code view area" + ref={codeRef} + > + {icon && ( +
+
+ +
+
+ )} + + + + {isHovering && navigator.clipboard.writeText(content)} />} +
+ ); +}; + +export default PlainCodeView; diff --git a/src/components/ui/CodeSnippet/TooltipButton.tsx b/src/components/ui/CodeSnippet/TooltipButton.tsx new file mode 100644 index 0000000000..8170d7b522 --- /dev/null +++ b/src/components/ui/CodeSnippet/TooltipButton.tsx @@ -0,0 +1,99 @@ +import React, { useMemo } from 'react'; +import Tooltip from 'src/components/ui/Tooltip'; +import SegmentedControl, { SegmentedControlSize } from 'src/components/ui/SegmentedControl'; +import cn from 'src/utilities/cn'; +import { IconName } from 'src/components/Icon/types'; +import type { TooltipProps } from '@radix-ui/react-tooltip'; + +type TooltipButtonProps = { + tooltip: string | React.ReactNode; + active?: boolean; + onClick: () => void; + icon?: IconName; + className?: string; + children?: React.ReactNode; + variant?: 'segmented' | 'icon-button'; + size?: SegmentedControlSize; + alwaysShowLabel?: boolean; + tooltipRootProps?: TooltipProps; +}; + +const TooltipButton = ({ + tooltip, + active = false, + onClick, + icon, + className, + children, + variant = 'segmented', + size = 'sm', + alwaysShowLabel = false, + tooltipRootProps, +}: TooltipButtonProps) => { + const showTooltip = (variant === 'segmented' && !active) || variant === 'icon-button'; + + const showChildren = active || alwaysShowLabel; + + // Create the button element based on variant + const buttonElement = useMemo(() => { + if (variant === 'segmented') { + return ( + + {showChildren ? children : null} + + ); + } + + return ( +
{ + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick?.(); + } + }} + tabIndex={0} + > + {children} +
+ ); + }, [variant, size, active, onClick, icon, className, showChildren, children]); + + if (showTooltip) { + return ( + + {tooltip} + + ); + } + + return buttonElement; +}; + +export default TooltipButton; diff --git a/src/components/ui/CodeSnippet/languages.test.ts b/src/components/ui/CodeSnippet/languages.test.ts new file mode 100644 index 0000000000..58906d8528 --- /dev/null +++ b/src/components/ui/CodeSnippet/languages.test.ts @@ -0,0 +1,106 @@ +import { stripSdkType, getLanguageInfo, SDK_PREFIXES, SDKType } from './languages'; + +describe('SDK_PREFIXES', () => { + it('contains the expected SDK types', () => { + expect(SDK_PREFIXES).toEqual(['realtime', 'rest', 'client', 'agent']); + }); + + it('derives SDKType correctly', () => { + // Verify the type system works by assigning each prefix + const types: SDKType[] = [...SDK_PREFIXES]; + expect(types).toHaveLength(4); + }); +}); + +describe('stripSdkType', () => { + it('strips realtime_ prefix', () => { + expect(stripSdkType('realtime_javascript')).toBe('javascript'); + }); + + it('strips rest_ prefix', () => { + expect(stripSdkType('rest_python')).toBe('python'); + }); + + it('strips client_ prefix', () => { + expect(stripSdkType('client_javascript')).toBe('javascript'); + }); + + it('strips agent_ prefix', () => { + expect(stripSdkType('agent_python')).toBe('python'); + }); + + it('returns the language unchanged when no prefix', () => { + expect(stripSdkType('javascript')).toBe('javascript'); + }); + + it('handles languages with underscores after the prefix', () => { + expect(stripSdkType('client_objective_c')).toBe('objective_c'); + }); + + it('does not strip unknown prefixes', () => { + expect(stripSdkType('unknown_javascript')).toBe('unknown_javascript'); + }); + + it('handles empty string', () => { + expect(stripSdkType('')).toBe(''); + }); + + it('does not strip prefix without underscore', () => { + expect(stripSdkType('realtimejavascript')).toBe('realtimejavascript'); + }); +}); + +describe('getLanguageInfo', () => { + it('returns info for known languages', () => { + const info = getLanguageInfo('javascript'); + expect(info.label).toBe('JavaScript'); + expect(info.icon).toBe('icon-tech-javascript'); + expect(info.syntaxHighlighterKey).toBe('javascript'); + }); + + it('returns info for prefixed languages by stripping prefix first', () => { + const info = getLanguageInfo('realtime_javascript'); + expect(info.label).toBe('JavaScript'); + expect(info.icon).toBe('icon-tech-javascript'); + }); + + it('returns info for client_ prefixed languages', () => { + const info = getLanguageInfo('client_python'); + expect(info.label).toBe('Python'); + expect(info.icon).toBe('icon-tech-python'); + }); + + it('returns info for agent_ prefixed languages', () => { + const info = getLanguageInfo('agent_nodejs'); + expect(info.label).toBe('Node.js'); + expect(info.icon).toBe('icon-tech-nodejs'); + }); + + it('handles case-insensitive lookup', () => { + const info = getLanguageInfo('JavaScript'); + expect(info.label).toBe('JavaScript'); + }); + + it('returns fallback for unknown languages', () => { + const info = getLanguageInfo('brainfuck'); + expect(info.label).toBe('brainfuck'); + expect(info.icon).toBe('icon-tech-web'); + expect(info.syntaxHighlighterKey).toBe('brainfuck'); + }); + + it('returns correct info for new language entries', () => { + expect(getLanguageInfo('cpp').label).toBe('C++'); + expect(getLanguageInfo('dart').label).toBe('Dart'); + expect(getLanguageInfo('objc').label).toBe('Objective-C'); + expect(getLanguageInfo('android').label).toBe('Android'); + expect(getLanguageInfo('flutter').label).toBe('Flutter'); + }); + + it('maps languages to correct syntax highlighter keys', () => { + expect(getLanguageInfo('nodejs').syntaxHighlighterKey).toBe('javascript'); + expect(getLanguageInfo('react').syntaxHighlighterKey).toBe('javascript'); + expect(getLanguageInfo('flutter').syntaxHighlighterKey).toBe('dart'); + expect(getLanguageInfo('android').syntaxHighlighterKey).toBe('kotlin'); + expect(getLanguageInfo('laravel').syntaxHighlighterKey).toBe('php'); + }); +}); diff --git a/src/components/ui/CodeSnippet/languages.ts b/src/components/ui/CodeSnippet/languages.ts new file mode 100644 index 0000000000..b432438ef5 --- /dev/null +++ b/src/components/ui/CodeSnippet/languages.ts @@ -0,0 +1,159 @@ +import { IconName } from 'src/components/Icon/types'; + +export interface LanguageInfo { + label: string; + icon: IconName; + syntaxHighlighterKey?: string; +} + +export type LanguageMap = Record; + +export const SDK_PREFIXES = ['realtime', 'rest', 'client', 'agent'] as const; +export type SDKType = (typeof SDK_PREFIXES)[number]; + +const languages: LanguageMap = { + javascript: { + label: 'JavaScript', + icon: 'icon-tech-javascript', + syntaxHighlighterKey: 'javascript', + }, + typescript: { + label: 'TypeScript', + icon: 'icon-tech-typescript', + syntaxHighlighterKey: 'typescript', + }, + java: { + label: 'Java', + icon: 'icon-tech-java', + syntaxHighlighterKey: 'java', + }, + kotlin: { + label: 'Kotlin', + icon: 'icon-tech-kotlin', + syntaxHighlighterKey: 'kotlin', + }, + python: { + label: 'Python', + icon: 'icon-tech-python', + syntaxHighlighterKey: 'python', + }, + csharp: { + label: 'C#', + icon: 'icon-tech-csharp', + syntaxHighlighterKey: 'csharp', + }, + go: { + label: 'Go', + icon: 'icon-tech-go', + syntaxHighlighterKey: 'go', + }, + ruby: { + label: 'Ruby', + icon: 'icon-tech-ruby', + syntaxHighlighterKey: 'ruby', + }, + php: { + label: 'PHP', + icon: 'icon-tech-php', + syntaxHighlighterKey: 'php', + }, + nodejs: { + label: 'Node.js', + icon: 'icon-tech-nodejs', + syntaxHighlighterKey: 'javascript', + }, + react: { + label: 'React', + icon: 'icon-tech-react', + syntaxHighlighterKey: 'javascript', + }, + html: { + label: 'HTML', + icon: 'icon-tech-web', + syntaxHighlighterKey: 'xml', + }, + shell: { + label: 'Shell', + icon: 'icon-tech-web', + syntaxHighlighterKey: 'bash', + }, + json: { + label: 'JSON', + icon: 'icon-tech-json', + syntaxHighlighterKey: 'json', + }, + laravel: { + label: 'Laravel', + icon: 'icon-tech-laravel-broadcast', + syntaxHighlighterKey: 'php', + }, + xml: { + label: 'XML', + icon: 'icon-tech-web', + syntaxHighlighterKey: 'xml', + }, + sql: { + label: 'SQL', + icon: 'icon-tech-postgres', + syntaxHighlighterKey: 'sql', + }, + swift: { + label: 'Swift', + icon: 'icon-tech-swift', + syntaxHighlighterKey: 'swift', + }, + // New entries from languageInfo.ts + cpp: { + label: 'C++', + icon: 'icon-tech-web', + syntaxHighlighterKey: 'cpp', + }, + dart: { + label: 'Dart', + icon: 'icon-tech-web', + syntaxHighlighterKey: 'dart', + }, + objc: { + label: 'Objective-C', + icon: 'icon-tech-objectivec', + syntaxHighlighterKey: 'objc', + }, + android: { + label: 'Android', + icon: 'icon-tech-android-full', + syntaxHighlighterKey: 'kotlin', + }, + flutter: { + label: 'Flutter', + icon: 'icon-tech-flutter', + syntaxHighlighterKey: 'dart', + }, +}; + +export const stripSdkType = (lang: string) => { + for (const prefix of SDK_PREFIXES) { + const withUnderscore = `${prefix}_`; + if (lang.startsWith(withUnderscore)) { + return lang.slice(withUnderscore.length); + } + } + return lang; +}; + +// Fallback function to handle languages not in the map +export const getLanguageInfo = (langKey: string): LanguageInfo => { + const key = stripSdkType(langKey).toLowerCase(); + + if (languages[key]) { + return languages[key]; + } + + // Fallback for unknown languages + return { + label: langKey, + icon: 'icon-tech-web', + syntaxHighlighterKey: langKey, + }; +}; + +export default languages; diff --git a/src/components/ui/FeaturedLink.tsx b/src/components/ui/FeaturedLink.tsx new file mode 100644 index 0000000000..7f27aa588e --- /dev/null +++ b/src/components/ui/FeaturedLink.tsx @@ -0,0 +1,130 @@ +import React, { CSSProperties, ReactNode } from 'react'; + +import Icon from 'src/components/Icon'; +import { ColorClass, ColorThemeSet } from './colors'; +import cn from 'src/utilities/cn'; + +type FeaturedLinkProps = { + url: string; + children: ReactNode; + textSize?: string; + iconColor?: ColorClass | ColorThemeSet; + flush?: boolean; + reverse?: boolean; + additionalCSS?: string; + newWindow?: boolean; + onClick?: () => void; + disabled?: boolean; + /** + * Optional class name for the icon. + */ + iconClassName?: string; +}; + +type TargetProps = { target?: string; rel?: string }; + +// When generating links with target=_blank, we only add `noreferrer` to +// links that don't start with `/`, so we can continue tracking referrers +// across our own domains +const buildTargetAndRel = (url: string, newWindow: boolean) => { + const props: TargetProps = {}; + + if (newWindow) { + props.target = '_blank'; + + if (url.startsWith('/') && !url.startsWith('//')) { + props.rel = 'noopener'; + } else { + props.rel = 'noopenner noreferrer'; + } + } + + return props; +}; + +const FeaturedLink = ({ + url, + textSize = 'text-p2', + iconColor, + flush = false, + reverse = false, + additionalCSS = '', + newWindow = false, + onClick = undefined, + children, + disabled = false, + iconClassName = '', +}: FeaturedLinkProps) => { + const targetAndRel = buildTargetAndRel(url, newWindow); + + return ( + { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick(); + } + } + : undefined + } + role={onClick && !url ? 'button' : undefined} + > + {reverse ? ( + <> + + {children} + + ) : ( + <> + {children} + + + )} + + ); +}; + +export default FeaturedLink; 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/LinkButton.tsx b/src/components/ui/LinkButton.tsx new file mode 100644 index 0000000000..12983b788f --- /dev/null +++ b/src/components/ui/LinkButton.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { ButtonPropsBase, commonButtonInterior, commonButtonProps } from './Button'; +import cn from 'src/utilities/cn'; +import { ColorClass, ColorThemeSet } from './colors'; + +export type LinkButtonProps = ButtonPropsBase & { + disabled?: boolean; + onClick?: (event: React.MouseEvent) => void; + iconColor?: ColorClass | ColorThemeSet; +} & React.AnchorHTMLAttributes; + +const LinkButton: React.FC = ({ + variant = 'primary', + size, + leftIcon, + rightIcon, + children, + className, + disabled, + onClick, + iconColor, + ...rest +}) => { + const handleClick = (e: React.MouseEvent) => { + if (disabled) { + e.preventDefault(); + return; + } + onClick?.(e); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.repeat) { + return; + } + // Space: prevent page scroll on keydown; activate on keyup. + if (e.key === ' ' || e.key === 'Spacebar') { + e.preventDefault(); + return; + } + // Enter activates on keydown. + if (e.key === 'Enter') { + e.preventDefault(); + if (!disabled) { + e.currentTarget.click(); + } + } + }; + + const handleKeyUp = (e: React.KeyboardEvent) => { + if (e.key === ' ' || e.key === 'Spacebar') { + e.preventDefault(); + if (!disabled) { + e.currentTarget.click(); + } + } + }; + + return ( + )} + > + {commonButtonInterior({ leftIcon, rightIcon, iconColor, children })} + + ); +}; + +export default LinkButton; diff --git a/src/components/ui/ProductTile.tsx b/src/components/ui/ProductTile.tsx new file mode 100644 index 0000000000..712262a331 --- /dev/null +++ b/src/components/ui/ProductTile.tsx @@ -0,0 +1,179 @@ +import React from 'react'; +import cn from 'src/utilities/cn'; +import { IconSize } from 'src/components/Icon/types'; +import LinkButton from './LinkButton'; +import { ProductName, products } from './ProductTile/data'; +import ProductIcon from './ProductTile/ProductIcon'; +import ProductLabel from './ProductTile/ProductLabel'; +import ProductDescription from './ProductTile/ProductDescription'; + +/** + * Props for the ProductTile component. + */ +export type ProductTileProps = { + /** + * The name of the product. + */ + name: ProductName; + + /** + * Indicates if the product tile is selected. If `undefined`, the product tile is not selectable. + * @default false + */ + selected?: boolean; + + /** + * Indicates if the product tile is on the "current" page. Changes CTA copy. + * @default false + */ + currentPage?: boolean; + + /** + * Additional CSS class names to apply to the product tile outer container. + */ + className?: string; + + /** + * Additional CSS class names to apply to the product description container. + */ + descriptionClassName?: string; + + /** + * Additional CSS class names to apply to the product name / label container. + */ + labelClassName?: string; + + /** + * Additional CSS class names to apply to the product icon. + */ + iconClassName?: string; + + /** + * Callback function to handle click events on the product tile. + */ + + onClick?: () => void; + + /** + * Indicates if the product description should be shown. + * @default true + */ + showDescription?: boolean; + + /** + * Indicates if the product label should be shown. + * @default true + */ + showLabel?: boolean; + + /** + * The size of the product icon. + * @default "40px" + */ + size?: IconSize; + + /** + * Indicates if the product icons should be animated. + * @default false + */ + animateIcons?: boolean; +}; + +const CONTAINER_GAP_RATIO = 3; + +const ProductTile = ({ + name, + selected, + currentPage, + className, + onClick, + showDescription = true, + showLabel = true, + size = '40px', + animateIcons = false, + descriptionClassName, + labelClassName, + iconClassName, +}: ProductTileProps) => { + const { icon, hoverIcon, label, description, link, unavailable } = products[name] ?? {}; + const numericalSize = parseInt(size, 10); + const containerPresent = showDescription || showLabel; + + return ( +
{ + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick(); + } + } + : undefined + } + role={onClick ? 'button' : undefined} + tabIndex={onClick ? 0 : undefined} + > +
+ + +
+ + {selected && link ? ( + + {currentPage ? 'View docs' : 'Explore'} + + ) : null} +
+ ); +}; + +export default ProductTile; diff --git a/src/components/ui/ProductTile/ProductDescription.tsx b/src/components/ui/ProductTile/ProductDescription.tsx new file mode 100644 index 0000000000..d915d778c0 --- /dev/null +++ b/src/components/ui/ProductTile/ProductDescription.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import cn from 'src/utilities/cn'; + +type ProductDescriptionProps = { + description: string; + selected?: boolean; + unavailable: boolean; + showDescription?: boolean; + className?: string; +}; + +const ProductDescription = ({ + description, + selected, + unavailable, + showDescription = true, + className, +}: ProductDescriptionProps) => { + if (!description || !showDescription) { + return null; + } + + return ( + + {description} + + ); +}; + +export default ProductDescription; diff --git a/src/components/ui/ProductTile/ProductIcon.tsx b/src/components/ui/ProductTile/ProductIcon.tsx new file mode 100644 index 0000000000..49caecc66a --- /dev/null +++ b/src/components/ui/ProductTile/ProductIcon.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import Icon from 'src/components/Icon'; +import { IconName } from 'src/components/Icon/types'; +import cn from 'src/utilities/cn'; + +type ProductIconProps = { + name?: IconName; + hoverName?: IconName; + selected?: boolean; + size: number; + unavailable: boolean; + className?: string; +}; + +const ProductIcon = ({ name, hoverName, selected, size, unavailable, className }: ProductIconProps) => { + if (!name) { + return null; + } + + // Padding around the icon is 1/4 the icon's size, so the icon is 4 of 6 parts + const iconSize = (size / 6) * 4; + + return ( + // Outer container, contains the gradient stroke (since we can't natively have CSS gradient strokes) + + + {/* The product icons themselves */} + {hoverName ? ( + + ) : null} + + + ); +}; + +export default ProductIcon; diff --git a/src/components/ui/ProductTile/ProductLabel.tsx b/src/components/ui/ProductTile/ProductLabel.tsx new file mode 100644 index 0000000000..b326474310 --- /dev/null +++ b/src/components/ui/ProductTile/ProductLabel.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import cn from 'src/utilities/cn'; + +type ProductLabelProps = { + label: string; + unavailable: boolean; + selected?: boolean; + numericalSize: number; + showLabel?: boolean; + showAblyText?: boolean; + className?: string; +}; + +const LABEL_FONT_SIZE_RATIO = 4; +const DESCRIPTION_FONT_SIZE_RATIO = 2.6; + +const ProductLabel = ({ + label, + unavailable, + selected, + numericalSize, + showLabel, + showAblyText = true, + className, +}: ProductLabelProps) => { + if (!label || !showLabel) { + return null; + } + + const dynamicFontSize = numericalSize / LABEL_FONT_SIZE_RATIO; + + return ( + + {unavailable ? ( + + + COMING SOON + + + ) : ( + showAblyText && ( + + Ably + + ) + )} + + {label} + + + ); +}; + +export default ProductLabel; diff --git a/src/components/ui/ProductTile/data.ts b/src/components/ui/ProductTile/data.ts new file mode 100644 index 0000000000..18a7e2cf17 --- /dev/null +++ b/src/components/ui/ProductTile/data.ts @@ -0,0 +1,61 @@ +import { IconName } from 'src/components/Icon/types'; + +export const productNames = ['pubsub', 'chat', 'aiTransport', 'liveObjects', 'spaces', 'liveSync'] as const; + +export type ProductName = (typeof productNames)[number]; + +type Products = Record< + ProductName, + { + label: string; + description: string; + link?: string; + icon?: IconName; + hoverIcon?: IconName; + unavailable?: boolean; + } +>; + +export const products: Products = { + pubsub: { + label: 'Pub/Sub', + description: 'Low-level APIs to build any realtime experience.', + icon: 'icon-product-pubsub-mono', + hoverIcon: 'icon-product-pubsub', + link: '/docs/channels', + }, + chat: { + label: 'Chat', + description: 'Rapidly build chat features and roll-out at scale.', + icon: 'icon-product-chat-mono', + hoverIcon: 'icon-product-chat', + link: '/docs/chat', + }, + aiTransport: { + label: 'AI Transport', + description: 'Drop-in realtime continuity for Gen-2 AI experiences.', + icon: 'icon-product-ai-transport-mono', + hoverIcon: 'icon-product-ai-transport', + }, + liveObjects: { + label: 'LiveObjects', + description: 'Sync application state across multiple devices.', + icon: 'icon-product-liveobjects-mono', + hoverIcon: 'icon-product-liveobjects', + link: '/docs/liveobjects', + }, + spaces: { + label: 'Spaces', + description: 'Create collaborative environments in a few lines of code.', + icon: 'icon-product-spaces-mono', + hoverIcon: 'icon-product-spaces', + link: '/docs/spaces', + }, + liveSync: { + label: 'LiveSync', + description: 'Sync database changes with frontend clients.', + icon: 'icon-product-livesync-mono', + hoverIcon: 'icon-product-livesync', + link: '/docs/livesync', + }, +}; diff --git a/src/components/ui/SegmentedControl.tsx b/src/components/ui/SegmentedControl.tsx new file mode 100644 index 0000000000..06fe35514e --- /dev/null +++ b/src/components/ui/SegmentedControl.tsx @@ -0,0 +1,143 @@ +import React, { PropsWithChildren } from 'react'; +import cn from 'src/utilities/cn'; +import Icon from 'src/components/Icon'; +import type { IconName, IconSize } from 'src/components/Icon/types'; +import { ColorClass } from './colors'; + +export type SegmentedControlSize = 'md' | 'sm' | 'xs'; + +export type SegmentedControlProps = { + className?: string; + rounded?: boolean; + leftIcon?: IconName; + rightIcon?: IconName; + active?: boolean; + variant?: 'default' | 'subtle' | 'strong'; + size?: SegmentedControlSize; + onClick?: () => void; + disabled?: boolean; +}; + +const SegmentedControl: React.FC> = ({ + className, + rounded = false, + leftIcon, + rightIcon, + active = false, + variant = 'default', + size = 'md', + children, + onClick, + disabled, +}) => { + const colorStyles = { + default: { + active: 'bg-neutral-200 dark:bg-neutral-1100', + inactive: + 'bg-neutral-000 dark:bg-neutral-1300 hover:bg-neutral-100 dark:hover:bg-neutral-1200 active:bg-neutral-100 dark:active:bg-neutral-1200', + }, + subtle: { + active: 'bg-neutral-000 dark:bg-neutral-1000', + inactive: + 'bg-neutral-100 dark:bg-neutral-1200 hover:bg-neutral-200 dark:hover:bg-neutral-1100 active:bg-neutral-200 dark:active:bg-neutral-1100', + }, + strong: { + active: 'bg-neutral-1000 dark:bg-neutral-300', + inactive: + 'bg-neutral-100 dark:bg-neutral-1200 hover:bg-neutral-200 dark:hover:bg-neutral-1100 active:bg-neutral-200 dark:active:bg-neutral-1100', + }, + }; + + const contentColorStyles = { + default: { + active: 'text-neutral-1300 dark:text-neutral-000', + inactive: 'text-neutral-1000 dark:text-neutral-300 hover:text-neutral-1300 dark:hover:text-neutral-000', + }, + subtle: { + active: 'text-neutral-1300 dark:text-neutral-000', + inactive: 'text-neutral-1000 dark:text-neutral-300 hover:text-neutral-1300 dark:hover:text-neutral-000', + }, + strong: { + active: 'text-neutral-000 dark:text-neutral-1300', + inactive: 'text-neutral-1000 dark:text-neutral-300 hover:text-neutral-1300 dark:hover:text-neutral-000', + }, + }; + + const sizeStyles = { + md: cn('h-12 p-3 gap-2.5', rounded && 'px-[1.125rem]'), + sm: cn('h-10 p-[0.5625rem] gap-[0.5625rem]', rounded && 'px-3.5'), + xs: cn('h-9 p-2 gap-2', rounded && 'px-3'), + }; + + const textStyles = { + md: 'ui-text-label2', + sm: 'ui-text-label3', + xs: 'ui-text-label4', + }; + + const iconSizes: Record = { + md: '23px', + sm: '22px', + xs: '20px', + }; + + const activeKey = active ? 'active' : 'inactive'; + + return ( +
{ + if ((e.key === 'Enter' || e.key === ' ') && !disabled && onClick) { + e.preventDefault(); + onClick(); + } + }} + className={cn( + 'focus-base flex items-center justify-center cursor-pointer select-none transition-colors', + colorStyles[variant][activeKey], + contentColorStyles[variant][activeKey], + sizeStyles[size], + textStyles[size], + disabled && + 'cursor-not-allowed hover:bg-inherit dark:hover:bg-inherit active:bg-inherit dark:active:bg-inherit', + rounded ? 'rounded-full' : 'rounded-lg', + className, + )} + tabIndex={disabled ? -1 : 0} + role="button" + aria-pressed={active} + aria-disabled={disabled} + > + {leftIcon && ( +
+ ); +}; + +export default SegmentedControl; 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/components/ui/Status.tsx b/src/components/ui/Status.tsx new file mode 100644 index 0000000000..fd8870b430 --- /dev/null +++ b/src/components/ui/Status.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import useSWR from 'swr'; +import cn from 'src/utilities/cn'; +import Icon from 'src/components/Icon'; + +type StatusProps = { + statusUrl: string; + additionalCSS?: string; + refreshInterval?: number; + showDescription?: boolean; +}; + +export const statusTypes = ['none', 'operational', 'minor', 'major', 'critical', 'unknown'] as const; + +export type StatusType = (typeof statusTypes)[number]; + +export const StatusUrl = 'https://ntqy1wz94gjv.statuspage.io/api/v2/status.json'; + +// Our SWR fetcher function +const fetcher = (url: string) => fetch(url).then((res) => res.json()); + +const indicatorClass = (indicator?: StatusType) => { + switch (indicator) { + case 'none': + case 'operational': + return 'bg-gui-success-green'; + case 'minor': + return 'bg-yellow-500'; + case 'major': + return 'bg-orange-500'; + case 'critical': + return 'bg-gui-error-red'; + default: + return 'bg-neutral-500'; + } +}; + +export const StatusIcon = ({ statusUrl, refreshInterval = 1000 * 60 }: StatusProps) => { + const { data, error, isLoading } = useSWR(statusUrl, fetcher, { + refreshInterval, + }); + + return ( + + ); +}; + +const Status = ({ + statusUrl = StatusUrl, + additionalCSS, + refreshInterval = 1000 * 60, + showDescription = false, +}: StatusProps) => { + const { data } = useSWR(statusUrl, fetcher, { + refreshInterval, + }); + + return ( + + + {showDescription && data?.status?.description && ( +
+ + {data.status.description.charAt(0).toUpperCase() + data.status.description.slice(1).toLowerCase()} + + +
+ )} +
+ ); +}; + +export default Status; diff --git a/src/components/ui/TabMenu.tsx b/src/components/ui/TabMenu.tsx new file mode 100644 index 0000000000..f57f5de86e --- /dev/null +++ b/src/components/ui/TabMenu.tsx @@ -0,0 +1,204 @@ +import React, { ReactNode, useEffect } from 'react'; +import * as Tabs from '@radix-ui/react-tabs'; +import { throttle } from 'es-toolkit/compat'; +import cn from 'src/utilities/cn'; + +type TabTriggerContent = string | { label: string; disabled?: boolean } | ReactNode; + +/** + * Props for the TabMenu component. + */ + +export type TabMenuProps = { + /** + * An array of tabs, which can be either a string or an object with a label and an optional disabled state. + */ + tabs: TabTriggerContent[]; + + /** + * An optional array of React nodes representing the content for each tab. + */ + contents?: ReactNode[]; + + /** + * An optional callback function that is called when a tab is clicked, receiving the index of the clicked tab. + */ + tabOnClick?: (index: number) => void; + + /** + * An optional class name to apply to each tab. + */ + tabClassName?: string; + + /** + * An optional class name to apply to the Tabs.Root element. + */ + rootClassName?: string; + + /** + * An optional class name to apply to the Tabs.Content element. + */ + contentClassName?: string; + + /** + * Optional configuration options for the TabMenu. + */ + options?: { + /** + * The index of the tab that should be selected by default. + */ + defaultTabIndex?: number; + + /** + * Whether to show an underline below the selected tab. + */ + underline?: boolean; + + /** + * Whether to animate the transition between tabs. + */ + animated?: boolean; + + /** + * Whether the tab width should be flexible. + */ + flexibleTabWidth?: boolean; + + /** + * Whether the tab height should be flexible. + */ + flexibleTabHeight?: boolean; + }; +}; + +const DEFAULT_TAILWIND_ANIMATION_DURATION = 150; + +const TabMenu: React.FC = ({ + tabs = [], + contents = [], + tabOnClick, + tabClassName, + rootClassName, + contentClassName, + options, +}) => { + const { + defaultTabIndex = 0, + underline = true, + animated: animatedOption = true, + flexibleTabWidth = false, + flexibleTabHeight = false, + } = options ?? {}; + + const listRef = React.useRef(null); + const [animated, setAnimated] = React.useState(false); + const [highlight, setHighlight] = React.useState({ offset: 0, width: 0 }); + + useEffect(() => { + if (animatedOption && highlight.width > 0) { + setTimeout(() => { + setAnimated(true); + }, DEFAULT_TAILWIND_ANIMATION_DURATION); + } + }, [animatedOption, highlight.width]); + + const updateHighlightDimensions = (element: HTMLButtonElement) => { + const { left: parentLeft } = listRef.current?.getBoundingClientRect() ?? {}; + const { left, width } = element.getBoundingClientRect() ?? {}; + + setHighlight({ + offset: (left ?? 0) - (parentLeft ?? 0), + width: width ?? 0, + }); + }; + + useEffect(() => { + const handleResize = throttle(() => { + const activeTabElement = listRef.current?.querySelector(`[data-state="active"]`); + + if (activeTabElement) { + updateHighlightDimensions(activeTabElement); + } + }, 100); + + handleResize(); + + window.addEventListener('resize', handleResize); + + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + const handleTabClick = (event: React.MouseEvent, index: number) => { + tabOnClick?.(index); + updateHighlightDimensions(event.currentTarget as HTMLButtonElement); + }; + + const tabTriggerContent = (tab: TabTriggerContent) => { + if (!tab) { + return null; + } + + if (React.isValidElement(tab) || typeof tab === 'string') { + return tab; + } + + if (typeof tab === 'object' && 'label' in tab) { + return tab.label; + } + + return null; + }; + + return ( + + + {tabs.map( + (tab, index) => + tab && ( + handleTabClick(event, index)} + disabled={typeof tab === 'object' && 'disabled' in tab ? tab.disabled : false} + > + {tabTriggerContent(tab)} + + ), + )} +
+
+ {contents.map((content, index) => ( + + {content} + + ))} +
+ ); +}; + +export default TabMenu; diff --git a/src/components/ui/Tooltip.tsx b/src/components/ui/Tooltip.tsx new file mode 100644 index 0000000000..d9df1e0e71 --- /dev/null +++ b/src/components/ui/Tooltip.tsx @@ -0,0 +1,67 @@ +import React, { ButtonHTMLAttributes, HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; +import * as RadixTooltip from '@radix-ui/react-tooltip'; +import Icon from 'src/components/Icon'; +import cn from 'src/utilities/cn'; +import { IconSize } from 'src/components/Icon/types'; + +type TooltipProps = { + triggerElement?: ReactNode; + triggerProps?: ButtonHTMLAttributes; + contentProps?: RadixTooltip.TooltipContentProps & HTMLAttributes; + rootProps?: RadixTooltip.TooltipProps; + interactive?: boolean; + iconSize?: IconSize; +} & HTMLAttributes; + +const Tooltip = ({ + children, + triggerElement, + triggerProps, + contentProps, + rootProps, + interactive = false, + iconSize = '1rem', + ...rest +}: PropsWithChildren) => { + return ( +
+ + + + + + + +
{children}
+
+
+
+
+
+ ); +}; + +export default Tooltip; diff --git a/src/components/ui/colors.ts b/src/components/ui/colors.ts new file mode 100644 index 0000000000..693bff7431 --- /dev/null +++ b/src/components/ui/colors.ts @@ -0,0 +1,182 @@ +export type ColorName = + | (typeof neutralColors)[number] + | (typeof orangeColors)[number] + | (typeof secondaryColors)[number] + | (typeof guiColors)[number] + | (typeof aliasedColors)[number]; + +export const variants = ['', 'dark:'] as const; + +type ColorClassVariants = (typeof variants)[number]; + +export const prefixes = ['text', 'bg', 'from', 'to', 'border'] as const; + +type ColorClassPrefixes = (typeof prefixes)[number]; + +export const colors = ['neutral', 'orange', 'blue', 'yellow', 'green', 'violet', 'pink'] as const; + +export type ColorClassColorGroups = (typeof colors)[number]; + +export type Theme = 'light' | 'dark'; + +export type ColorClass = `${ColorClassVariants}${ColorClassPrefixes}-${ColorName}`; + +export type ColorThemeSet = `${string} dark:${string}`; + +export const neutralColors = [ + 'neutral-000', + 'neutral-100', + 'neutral-200', + 'neutral-300', + 'neutral-400', + 'neutral-500', + 'neutral-600', + 'neutral-700', + 'neutral-800', + 'neutral-900', + 'neutral-1000', + 'neutral-1100', + 'neutral-1200', + 'neutral-1300', +] as const; + +export const orangeColors = [ + 'orange-100', + 'orange-200', + 'orange-300', + 'orange-400', + 'orange-500', + 'orange-600', + 'orange-700', + 'orange-800', + 'orange-900', + 'orange-1000', + 'orange-1100', +] as const; + +export const yellowColors = [ + 'yellow-100', + 'yellow-200', + 'yellow-300', + 'yellow-400', + 'yellow-500', + 'yellow-600', + 'yellow-700', + 'yellow-800', + 'yellow-900', +] as const; + +export const greenColors = [ + 'green-100', + 'green-200', + 'green-300', + 'green-400', + 'green-500', + 'green-600', + 'green-700', + 'green-800', + 'green-900', +] as const; + +export const blueColors = [ + 'blue-100', + 'blue-200', + 'blue-300', + 'blue-400', + 'blue-500', + 'blue-600', + 'blue-700', + 'blue-800', + 'blue-900', +] as const; + +export const violetColors = [ + 'violet-100', + 'violet-200', + 'violet-300', + 'violet-400', + 'violet-500', + 'violet-600', + 'violet-700', + 'violet-800', + 'violet-900', +] as const; + +export const pinkColors = [ + 'pink-100', + 'pink-200', + 'pink-300', + 'pink-400', + 'pink-500', + 'pink-600', + 'pink-700', + 'pink-800', + 'pink-900', +] as const; + +export const secondaryColors = [ + ...yellowColors, + ...greenColors, + ...blueColors, + ...violetColors, + ...pinkColors, +] as const; + +export const guiColors = [ + 'gui-blue-default-light', + 'gui-blue-hover-light', + 'gui-blue-active-light', + 'gui-blue-default-dark', + 'gui-blue-hover-dark', + 'gui-blue-active-dark', + 'gui-blue-focus', + 'gui-disabled-light', + 'gui-disabled-dark', + 'gui-success-green', + 'gui-error-red', + 'gui-focus', + 'gui-focus-outline', + 'gui-visited', +] as const; + +export const aliasedColors = [ + 'white', + 'extra-light-grey', + 'light-grey', + 'mid-grey', + 'dark-grey', + 'charcoal-grey', + 'cool-black', + 'active-orange', + 'bright-red', + 'red-orange', + 'electric-cyan', + 'zingy-green', + 'jazzy-pink', + 'gui-default', + 'gui-hover', + 'gui-active', + 'gui-error', + 'gui-success', + 'gui-default-dark', + 'gui-hover-dark', + 'gui-active-dark', + 'transparent', +] as const; + +export const colorRoles = { + neutral: neutralColors, + orange: orangeColors, + secondary: secondaryColors, + gui: guiColors, +}; + +export const colorGroupLengths = { + neutral: neutralColors.length, + orange: orangeColors.length, + blue: blueColors.length, + yellow: yellowColors.length, + green: greenColors.length, + violet: violetColors.length, + pink: pinkColors.length, +}; diff --git a/src/contexts/layout-context.tsx b/src/contexts/layout-context.tsx index e7ca3ab7a1..8107314649 100644 --- a/src/contexts/layout-context.tsx +++ b/src/contexts/layout-context.tsx @@ -1,6 +1,6 @@ import React, { createContext, PropsWithChildren, useContext, useMemo } from 'react'; import { useLocation } from '@reach/router'; -import { stripSdkType } from '@ably/ui/core/CodeSnippet/languages'; +import { stripSdkType } from 'src/components/ui/CodeSnippet/languages'; import { ActivePage, determineActivePage, PageTemplate } from 'src/components/Layout/utils/nav'; import { productData } from 'src/data'; import { LanguageKey } from 'src/data/languages/types'; diff --git a/src/data/content/types.ts b/src/data/content/types.ts index 500b458308..b48c8656cb 100644 --- a/src/data/content/types.ts +++ b/src/data/content/types.ts @@ -1,4 +1,4 @@ -import { ProductName } from '@ably/ui/core/ProductTile/data'; +import { ProductName } from 'src/components/ui/ProductTile/data'; export type ContentLink = { text: string; diff --git a/src/data/nav/types.ts b/src/data/nav/types.ts index bebe3cb5ff..1445df70bc 100644 --- a/src/data/nav/types.ts +++ b/src/data/nav/types.ts @@ -1,4 +1,4 @@ -import { IconName } from '@ably/ui/core/Icon/types'; +import { IconName } from 'src/components/Icon/types'; import { LanguageKey } from 'src/data/languages/types'; export type NavProduct = { diff --git a/src/images/ably-logo.svg b/src/images/ably-logo.svg new file mode 100644 index 0000000000..b8b2895f0e --- /dev/null +++ b/src/images/ably-logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/docs/api/chat-rest.tsx b/src/pages/docs/api/chat-rest.tsx index 3e2b7f0d9b..2ee8118d08 100644 --- a/src/pages/docs/api/chat-rest.tsx +++ b/src/pages/docs/api/chat-rest.tsx @@ -1,5 +1,5 @@ import { Link, withAssetPrefix } from 'gatsby'; -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/Icon'; import { useSiteMetadata } from '../../../hooks/use-site-metadata'; import { Head } from '../../../components/Head'; import { Loader } from '../../../components/Redoc'; diff --git a/src/pages/docs/api/control-api.tsx b/src/pages/docs/api/control-api.tsx index 0ea6ecacf9..f296a3cbaf 100644 --- a/src/pages/docs/api/control-api.tsx +++ b/src/pages/docs/api/control-api.tsx @@ -1,5 +1,5 @@ import { Link, withAssetPrefix } from 'gatsby'; -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/Icon'; import { useSiteMetadata } from '../../../hooks/use-site-metadata'; import { Head } from '../../../components/Head'; import { Loader } from '../../../components/Redoc'; diff --git a/src/pages/docs/api/liveobjects-rest.tsx b/src/pages/docs/api/liveobjects-rest.tsx index 5a268de78d..eea5d634a6 100644 --- a/src/pages/docs/api/liveobjects-rest.tsx +++ b/src/pages/docs/api/liveobjects-rest.tsx @@ -1,5 +1,5 @@ import { Link, withAssetPrefix } from 'gatsby'; -import Icon from '@ably/ui/core/Icon'; +import Icon from 'src/components/Icon'; import { useSiteMetadata } from '../../../hooks/use-site-metadata'; import { Head } from '../../../components/Head'; import { Loader } from '../../../components/Redoc'; diff --git a/src/styles/global.css b/src/styles/global.css index 3a407f6c3b..e11afbeb08 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -2,12 +2,10 @@ @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; -@import '@ably/ui/reset/styles.css'; -@import '@ably/ui/core/styles.css'; -@import '@ably/ui/core/Slider/component.css'; -@import '@ably/ui/core/Code/component.css'; -@import '@ably/ui/core/Flash/component.css'; -@import '@ably/ui/core/utils/syntax-highlighter.css'; +@import './ui/reset/styles.css'; +@import './ui/core/styles.css'; +@import './ui/core/Code/component.css'; +@import './ui/core/utils/syntax-highlighter.css'; :root { --top-nav-height: 64px; diff --git a/src/styles/ui/core/Code/component.css b/src/styles/ui/core/Code/component.css new file mode 100644 index 0000000000..29a84c3efa --- /dev/null +++ b/src/styles/ui/core/Code/component.css @@ -0,0 +1 @@ +@import "../utils/syntax-highlighter.css"; diff --git a/src/styles/ui/core/styles.base.css b/src/styles/ui/core/styles.base.css new file mode 100644 index 0000000000..ba4b59f3a0 --- /dev/null +++ b/src/styles/ui/core/styles.base.css @@ -0,0 +1 @@ +@import "./styles/properties.css"; diff --git a/src/styles/ui/core/styles.components.css b/src/styles/ui/core/styles.components.css new file mode 100644 index 0000000000..d1a00a160d --- /dev/null +++ b/src/styles/ui/core/styles.components.css @@ -0,0 +1,64 @@ +@import "./styles/utils.css"; +@import "./styles/buttons.css"; +@import "./styles/forms.css"; +@import "./styles/shadows.css"; +@import "./styles/text.css"; + +@layer components { + /* Basis for icon component */ + .ui-icon-cool-black { + stroke: var(--color-cool-black); + } + + .ui-icon-white { + stroke: var(--color-white); + } + + .ui-icon-dark-grey { + stroke: var(--color-dark-grey); + } + + .ui-version-tag { + @apply inline-block absolute align-top uppercase font-bold whitespace-nowrap; + + position: relative; + vertical-align: super; + margin-left: 3px; + font-size: 8px; + } + + .ui-section-spacing { + @apply my-20 sm:my-24 md:my-32; + } + + /* The *-collapse classes remove the spacing between adjacent .ui-section-spacing classes, + in a way that handles margin collapsing as well (hence not an exact opposite) */ + .ui-section-spacing-collapse, + .ui-section-spacing-collapse-full { + @apply -my-4 sm:-my-8 md:-my-16 first:-mb-8 sm:first:-mb-16 md:first:-mb-32 last:-mt-8 sm:last:-mt-16 md:last:-mt-32; + } + + .ui-section-spacing-collapse-half { + @apply -my-2 sm:-my-4 md:-my-8 first:-mb-4 sm:first:-mb-8 md:first:-mb-16 last:-mt-4 sm:last:-mt-8 md:last:-mt-16; + } + + .ui-section-spacing-collapse-quarter { + @apply -my-1 sm:-my-2 md:-my-4 first:-mb-2 sm:first:-mb-4 md:first:-mb-8 last:-mt-2 sm:last:-mt-4 md:last:-mt-8; + } + + .ui-section-spacing-collapse-eighth { + @apply -my-0.5 sm:-my-1 md:-my-2 first:-mb-1 sm:first:-mb-2 md:first:-mb-4 last:-mt-1 sm:last:-mt-2 md:last:-mt-4; + } + + .ui-content-spacing { + @apply my-10 sm:my-12 md:my-16; + } + + .ui-divider-spacing { + @apply my-5 sm:my-6 md:my-8; + } + + .ui-container-padding { + @apply px-6 py-10 sm:px-10 sm:py-12 md:p-16; + } +} diff --git a/src/styles/ui/core/styles.css b/src/styles/ui/core/styles.css new file mode 100644 index 0000000000..ca8ba904e8 --- /dev/null +++ b/src/styles/ui/core/styles.css @@ -0,0 +1,2 @@ +@import "./styles.base.css"; +@import "./styles.components.css"; diff --git a/src/styles/ui/core/styles/buttons.css b/src/styles/ui/core/styles/buttons.css new file mode 100644 index 0000000000..f755d9f38a --- /dev/null +++ b/src/styles/ui/core/styles/buttons.css @@ -0,0 +1,133 @@ +@layer components { + .ui-button-base { + @apply inline-flex rounded justify-center items-center gap-3 text-neutral-000 transition-colors focus-base; + } + + .ui-button-lg-base { + @apply ui-button-base ui-text-label1 font-bold h-14 px-7 py-[1.0625rem] gap-3 [&>svg]:!w-6 [&>svg]:!h-6; + } + + .ui-button-md-base { + @apply ui-button-base ui-text-label2 font-bold h-12 px-6 py-[0.8125rem] gap-2.5 [&>svg]:!w-6 [&>svg]:!h-6; + } + + .ui-button-sm-base { + @apply ui-button-base ui-text-label3 font-bold h-10 px-5 py-2.5 gap-2 [&>svg]:!w-5 [&>svg]:!h-5; + } + + .ui-button-xs-base { + @apply ui-button-base ui-text-label4 font-bold h-9 px-3 py-[0.5625rem] gap-1.5 [&>svg]:!w-4 [&>svg]:!h-4; + } + + .ui-button-disabled-base { + @apply disabled:cursor-not-allowed disabled:bg-gui-unavailable disabled:text-gui-unavailable-dark disabled:hover:bg-gui-unavailable; + } + + .ui-button-disabled-base-dark { + @apply disabled:cursor-not-allowed disabled:bg-gui-unavailable-dark disabled:text-gui-unavailable disabled:hover:bg-gui-unavailable-dark; + } + + .ui-button-priority-base { + @apply bg-gradient-priority-button bg-[size:200%] bg-left hover:bg-right active:bg-right text-neutral-000 active:text-neutral-000 transition-[background-position] disabled:bg-none ui-button-disabled-base; + } + + .ui-theme-dark .ui-button-priority-base { + @apply bg-gradient-priority-button bg-[size:200%] bg-left hover:bg-right active:bg-right text-neutral-000 active:text-neutral-000 transition-[background-position] disabled:bg-none ui-button-disabled-base-dark; + } + + .ui-button-primary-base { + @apply bg-neutral-1300 text-neutral-000 hover:bg-neutral-1100 active:bg-neutral-1200 active:text-neutral-300 ui-button-disabled-base; + } + + .ui-theme-dark .ui-button-primary-base { + @apply bg-neutral-000 text-neutral-1300 hover:bg-neutral-200 active:bg-neutral-100 active:text-neutral-1000 ui-button-disabled-base-dark; + } + + .ui-button-secondary-base { + @apply text-neutral-1300 active:text-neutral-1000 border border-neutral-400 hover:border-neutral-600 active:border-neutral-500 disabled:border-gui-unavailable disabled:text-gui-unavailable; + } + + .ui-theme-dark .ui-button-secondary-base { + @apply text-neutral-000 active:text-neutral-300 border-neutral-900 hover:border-neutral-700 active:border-neutral-800 disabled:border-gui-unavailable-dark disabled:text-gui-unavailable-dark; + } + + .ui-button-priority-lg { + @apply ui-button-lg-base ui-button-priority-base; + } + + .ui-button-priority { + @apply ui-button-md-base ui-button-priority-base; + } + + .ui-button-priority-sm { + @apply ui-button-sm-base ui-button-priority-base; + } + + .ui-button-priority-xs { + @apply ui-button-xs-base ui-button-priority-base; + } + + .ui-button-primary-lg { + @apply ui-button-lg-base ui-button-primary-base; + } + + .ui-button-primary { + @apply ui-button-md-base ui-button-primary-base; + } + + .ui-button-primary-sm { + @apply ui-button-sm-base ui-button-primary-base; + } + + .ui-button-primary-xs { + @apply ui-button-xs-base ui-button-primary-base; + } + + .ui-button-secondary-lg { + @apply ui-button-lg-base ui-button-secondary-base; + } + + .ui-button-secondary { + @apply ui-button-md-base ui-button-secondary-base; + } + + .ui-button-secondary-sm { + @apply ui-button-sm-base ui-button-secondary-base; + } + + .ui-button-secondary-xs { + @apply ui-button-xs-base ui-button-secondary-base; + } + + .ui-button-lg-left-icon { + @apply pl-6; + } + + .ui-button-left-icon { + @apply pl-5; + } + + .ui-button-sm-left-icon { + @apply pl-[1.125rem]; + } + + .ui-button-lg-right-icon { + @apply pr-6; + } + + .ui-button-right-icon { + @apply pr-5; + } + + .ui-button-sm-right-icon { + @apply pr-[1.125rem]; + } + + .ui-button-disabled { + @apply select-none pointer-events-none bg-gui-unavailable text-gui-unavailable-dark hover:bg-gui-unavailable; + } + + .ui-theme-dark .ui-button-disabled { + @apply select-none pointer-events-none bg-gui-unavailable-dark text-gui-unavailable hover:bg-gui-unavailable-dark; + } +} diff --git a/src/styles/ui/core/styles/forms.css b/src/styles/ui/core/styles/forms.css new file mode 100644 index 0000000000..afefbdb9d2 --- /dev/null +++ b/src/styles/ui/core/styles/forms.css @@ -0,0 +1,68 @@ +@layer components { + .ui-checkbox-p1 { + @apply flex items-start mb-4 font-sans font-medium; + } + + .ui-checkbox-p2 { + @apply flex items-start mb-3 font-sans font-medium; + } + + .ui-checkbox-input { + @apply opacity-0 absolute h-5 w-5; + } + + .ui-checkbox-styled { + @apply w-5 h-5 mr-4; + @apply bg-white flex flex-shrink-0 justify-center items-center; + @apply border border-neutral-500 rounded-md focus-within:border-active-orange; + } + + .ui-theme-dark .ui-checkbox-styled, + .ui-theme-dark .ui-checkbox-input:focus + .ui-checkbox-styled { + @apply bg-neutral-1300 border-neutral-900; + } + + .ui-checkbox-styled-tick { + @apply hidden text-white; + } + + .ui-checkbox-label-p1 { + @apply select-none; + @apply text-p1 font-medium text-neutral-1300; + } + + .ui-checkbox-label-p2 { + @apply select-none; + @apply text-p2 font-medium text-neutral-1300; + } + + .ui-theme-dark .ui-checkbox-label-p1, + .ui-theme-dark .ui-checkbox-label-p2 { + @apply text-neutral-000; + } + + .ui-checkbox-input:focus + .ui-checkbox-styled { + border-width: 0.125rem; + @apply border-gui-focus; + } + + .ui-checkbox-input:checked + .ui-checkbox-styled { + @apply bg-active-orange border-dark-grey border; + } + + .ui-checkbox-input:checked + .ui-checkbox-styled svg { + @apply block; + } + + .ui-checkbox-input:disabled + .ui-checkbox-styled { + @apply bg-neutral-300; + } + + .ui-theme-dark .ui-checkbox-input:disabled + .ui-checkbox-styled { + @apply bg-neutral-1000 border border-gui-unavailable-dark; + } + + .ui-theme-dark .ui-checkbox-input:checked + .ui-checkbox-styled { + @apply bg-active-orange border-gui-unavailable-dark; + } +} diff --git a/src/styles/ui/core/styles/properties.css b/src/styles/ui/core/styles/properties.css new file mode 100644 index 0000000000..b462ff9599 --- /dev/null +++ b/src/styles/ui/core/styles/properties.css @@ -0,0 +1,272 @@ +@layer base { + :root { + /* Neutral colors */ + --color-neutral-000: #ffffff; + --color-neutral-100: #f6f8fa; + --color-neutral-200: #eef1f6; + --color-neutral-300: #e6eaf0; + --color-neutral-400: #e2e7ef; + --color-neutral-500: #c6ced9; + --color-neutral-600: #a7b1be; + --color-neutral-700: #8992a4; + --color-neutral-800: #687288; + --color-neutral-900: #575d71; + --color-neutral-1000: #3f4555; + --color-neutral-1100: #2c3344; + --color-neutral-1200: #141924; + --color-neutral-1300: #03020d; + + /* Ably orange */ + --color-orange-100: #fff5f1; + --color-orange-200: #ffe3d8; + --color-orange-300: #ffc4ae; + --color-orange-400: #ff9c79; + --color-orange-500: #ff723f; + --color-orange-600: #ff5416; + --color-orange-700: #ff2739; + --color-orange-800: #e40000; + --color-orange-900: #b82202; + --color-orange-1000: #751500; + --color-orange-1100: #2a0b00; + + /* Secondary colours */ + --color-yellow-100: #fffbef; + --color-yellow-200: #fff0ba; + --color-yellow-300: #ffe27c; + --color-yellow-400: #ffd43d; + --color-yellow-500: #f8c100; + --color-yellow-600: #e8a700; + --color-yellow-700: #ac8600; + --color-yellow-800: #654f00; + --color-yellow-900: #291c01; + --color-green-100: #f1fff1; + --color-green-200: #b8ffbb; + --color-green-300: #80ff85; + --color-green-400: #08ff13; + --color-green-500: #00e80b; + --color-green-600: #00c008; + --color-green-700: #008e06; + --color-green-800: #005303; + --color-green-900: #002a02; + --color-blue-100: #f1fbff; + --color-blue-200: #b8eaff; + --color-blue-300: #80d9ff; + --color-blue-400: #4ad4ff; + --color-blue-500: #2cc0ff; + --color-blue-600: #00a5ec; + --color-blue-700: #0284cd; + --color-blue-800: #004b75; + --color-blue-900: #001b2a; + --color-violet-100: #f7f2fe; + --color-violet-200: #d8bcfb; + --color-violet-300: #b986f8; + --color-violet-400: #9951f5; + --color-violet-500: #7a1bf2; + --color-violet-600: #5f0bc9; + --color-violet-700: #460894; + --color-violet-800: #2d055e; + --color-violet-900: #130228; + --color-pink-100: #fff1fc; + --color-pink-200: #ffb8f1; + --color-pink-300: #ff80e6; + --color-pink-400: #ff47db; + --color-pink-500: #ff17d2; + --color-pink-600: #d400ab; + --color-pink-700: #9c007e; + --color-pink-800: #630050; + --color-pink-900: #2a0022; + + /* GUI colours */ + /* Note: The ‘Light and ‘Dark’ refers to the colour of the background on which the colour is used. */ + --color-gui-blue-default-light: #006edc; + --color-gui-blue-hover-light: #0862b9; + --color-gui-blue-active-light: #074095; + --color-gui-blue-default-dark: #4da6ff; + --color-gui-blue-hover-dark: #2894ff; + --color-gui-blue-active-dark: #0080ff; + --color-gui-blue-focus: #80b9f2; + --color-gui-unavailable: #AFB9C5; + --color-gui-unavailable-dark: #676B7A; + --color-gui-disabled-light: #AFB9C5; + --color-gui-disabled-dark: #676B7A; + --color-gui-success-green: #11cb24; + --color-gui-error-red: #fb0c0c; + --color-gui-focus: #80b9f2; + --color-gui-focus-outline: #80b9f2; + --color-gui-visited: #4887c2; + + /* Colours replaced by neutral colours */ + --color-white: var(--color-neutral-000); + --color-extra-light-grey: var(--color-neutral-100); + --color-light-grey: var(--color-neutral-200); + --color-mid-grey: var(--color-neutral-500); + --color-dark-grey: var(--color-neutral-800); + --color-charcoal-grey: var(--color-neutral-1000); + --color-cool-black: var(--color-neutral-1300); + + /* Colours replaced by orange colours */ + --color-active-orange: var(--color-orange-600); + --color-bright-red: var(--color-orange-700); + --color-red-orange: var(--color-orange-800); + + /* Colours replaced by secondary colours */ + --color-electric-cyan: var(--color-blue-400); + --color-zingy-green: var(--color-green-400); + --color-jazzy-pink: var(--color-pink-500); + + /* Colours replaced by GUI colours */ + --color-gui-default: var(--color-gui-blue-default-light); + --color-gui-hover: var(--color-gui-blue-hover-light); + --color-gui-active: var(--color-gui-blue-active-light); + --color-gui-error: var(--color-gui-error-red); + --color-gui-success: var(--color-gui-success-green); + --color-gui-default-dark: var(--color-gui-blue-default-dark); + --color-gui-hover-dark: var(--color-gui-blue-hover-dark); + --color-gui-active-dark: var(--color-gui-blue-active-dark); + + /* flat colors for social icons */ + --icon-color-linkedin: #1269bf; + --icon-color-twitter: #2caae1; + --icon-color-glassdoor: #0baa41; + --icon-color-github: #000000; + --icon-color-discord: #5865f2; + --icon-color-stackoverflow: #f48024; + --icon-color-youtube: #ff0000; + + --gradient-active-orange: linear-gradient( + 61.2deg, + var(--color-active-orange) 10.46%, + #fe5215 38.63%, + #fc4a13 54.38%, + #f73c10 67.07%, + #f1280a 78.13%, + #e90f04 88.02%, + var(--color-red-orange) 92.73% + ); + + --gradient-priority-button: linear-gradient( + 61.2deg, + var(--color-active-orange) 5%, + #fe5215 19%, + #fc4a13 27%, + #f73c10 33%, + #f1280a 39%, + #e90f04 44%, + var(--color-orange-800) 50% + ); + + /* Used for smooth transitions from gradient to non-gradient backgrounds */ + --gradient-transparent: linear-gradient( + 0deg, + rgba(255, 255, 255, 0), + rgba(255, 255, 255, 0) + ); + + --gradient-hot-pink: linear-gradient( + 80.2deg, + var(--color-orange-700) 0%, + var(--color-jazzy-pink) 100% + ); + + --fs-title-xl: 3rem; + --fs-title: 2.75rem; + --fs-title-xs: 2.5rem; + --fs-h1-xl: 2.5rem; + --fs-h1: 2.25rem; + --fs-h1-xs: 2rem; + + --fs-h2-xl: 2.125rem; + --fs-h2: 2rem; + --fs-h2-xs: 1.75rem; + + --fs-h3: 1.5rem; + --fs-h4: 1.25rem; + --fs-h5: 1rem; + + --fs-p1: 1rem; + --fs-p2: 0.938rem; + --fs-p3: 0.875rem; + --fs-p4: 0.813rem; + --fs-standfirst-xl: 2.25rem; + --fs-standfirst: 1.5rem; + --fs-sub-header: 1.125rem; + --fs-sub-header-xs: 1.063rem; + --fs-overline1: 0.875rem; + --fs-overline2: 0.75rem; + --fs-label1: 1rem; + --fs-label2: 0.938rem; + --fs-label3: 0.875rem; + --fs-label4: 0.813rem; + --fs-quote: 1.25rem; + --fs-code: 0.813rem; + --fs-code2: 0.688rem; + + --lh-none: 1; + --lh-dense: 1.125; + --lh-min-normal: 1.2; + --lh-snug: 1.4; + --lh-relaxed: 1.6; + + --ls-tighten-0_025: -0.025em; + --ls-tighten-0_02: -0.02em; + --ls-tighten-0_015: -0.015em; + --ls-tighten-0_01: -0.01em; + --ls-tighten-0_005: -0.005em; + --ls-tighten-0_0025: -0.0025em; + --ls-tighten-0_0015: -0.0015em; + --ls-widen-0_1: 0.1em; + --ls-widen-0_16: 0.16em; + --ls-widen-0_15: 0.15em; + --ls-widen-0_04: 0.04em; + + --spacing-0: 0px; + --spacing-1: 1px; + --spacing-2: 0.125rem; + --spacing-4: 0.25rem; + --spacing-6: 0.375rem; + --spacing-8: 0.5rem; + --spacing-12: 0.75rem; + --spacing-14: 0.875rem; + --spacing-16: 1rem; + --spacing-20: 1.25rem; + --spacing-24: 1.5rem; + --spacing-32: 2rem; + --spacing-36: 2.25rem; + --spacing-40: 2.5rem; + --spacing-48: 3rem; + --spacing-64: 4rem; + --spacing-72: 4.5rem; + --spacing-80: 5rem; + --spacing-88: 5.5rem; + --spacing-96: 6rem; + --spacing-128: 8rem; + --spacing-160: 10rem; + --spacing-256: 16rem; + + --spacing-btn-small: 0.875rem 1.25rem; /* 14px 16px */ + --spacing-btn-xsmall: 0.875rem 1rem; /* 14px 16px */ + --spacing-btn: 0.875rem 1.5rem; /* 14px 24px */ + --spacing-btn-large: 0.875rem 1.75rem; /* 14px 28px */ + --spacing-menu-row: 0.625rem 0.5rem 0.6875rem; /* 10px 8px 11px */ + --spacing-menu-row-snug: 0.4375rem 0.5rem 0.375rem; /* 7px 8px 6px */ + --spacing-menu-row-title: 0.6875rem 0.5rem; /* 11px 8px */ + --spacing-media: 0.5rem; /* 8px */ + --spacing-input: 0.8125rem 1rem 0.75rem; /* 13px 16px 12px */ + --spacing-overline: 0.6875rem 0; /* 11px 0 */ + + /* In components, when looking at implementing viewport margin and spacing between elements, + the values in the comments can be used as guide as they represent the grid the elements (should) sit on. + alternatively, look for ui-grid-* helpers. */ + --bp-xs: 428px; /* gutters 8px, side-margin 24px */ + --bp-sm: 768px; /* gutters 16px, side-margin 32px */ + --bp-md: 1040px; /* gutters 24px, side-margin 40px, meganav desktop */ + --bp-lg: 1280px; /* gutters 24px, side-margin 64px */ + --bp-xl: 1440px; /* gutters 32px, side-margin 64px */ + + /* Page stacking context. If components create new stacking contexts z-index values should be defined within them. */ + --stacking-context-page-meganav: 100; + + /* Expose component values for cross-component usage */ + --ui-meganav-height: 4rem; + } +} diff --git a/src/styles/ui/core/styles/shadows.css b/src/styles/ui/core/styles/shadows.css new file mode 100644 index 0000000000..f265469887 --- /dev/null +++ b/src/styles/ui/core/styles/shadows.css @@ -0,0 +1,19 @@ +@layer components { + .ui-shadow-sm-soft { + box-shadow: + 0px 32.725px 9.133px 0px rgba(3, 2, 13, 0), + 0px 21.309px 8.371px 0px rgba(3, 2, 13, 0.01), + 0px 12.177px 6.849px 0px rgba(3, 2, 13, 0.03), + 0px 5.327px 5.327px 0px rgba(3, 2, 13, 0.05), + 0px 1.522px 3.044px 0px rgba(3, 2, 13, 0.06); + } + + .ui-shadow-lg-medium { + box-shadow: + 0px 201px 56px 0px rgba(20, 25, 36, 0), + 0px 129px 51px 0px rgba(20, 25, 36, 0.02), + 0px 72px 43px 0px rgba(20, 25, 36, 0.06), + 0px 32px 32px 0px rgba(20, 25, 36, 0.1), + 0px 8px 18px 0px rgba(20, 25, 36, 0.12); + } +} diff --git a/src/styles/ui/core/styles/text.css b/src/styles/ui/core/styles/text.css new file mode 100644 index 0000000000..0682ae6c8b --- /dev/null +++ b/src/styles/ui/core/styles/text.css @@ -0,0 +1,176 @@ +@layer components { + .ui-text-title { + @apply font-sans font-bold text-cool-black; + @apply text-title-xs xs:text-title xl:text-title-xl; + @apply tracking-[-0.015em] xs:tracking-[-0.02em] xl:tracking-[-0.02em]; + } + + .ui-text-h1 { + @apply font-sans font-bold text-cool-black; + @apply text-h1-xs xs:text-h1 xl:text-h1-xl; + @apply tracking-[-0.0125em] xs:tracking-[-0.015em]; + } + + .ui-text-h2 { + @apply font-sans font-bold text-cool-black; + @apply text-h2-xs xs:text-h2 xl:text-h2-xl; + @apply tracking-[-0.015em] xs:tracking-[-0.01em]; + } + + .ui-text-h3 { + @apply font-sans font-bold text-cool-black; + @apply text-h3; + @apply tracking-[-0.005em]; + } + + .ui-text-h4 { + @apply font-sans font-bold text-cool-black; + @apply text-h4; + @apply tracking-[-0.0015em]; + } + + .ui-text-h5 { + @apply font-sans font-bold text-cool-black; + @apply text-h5; + @apply tracking-[-0.0025em]; + } + + .ui-text-p1 { + @apply font-sans font-medium text-charcoal-grey text-p1; + } + + .ui-text-p2 { + @apply font-sans font-medium text-charcoal-grey text-p2; + } + + .ui-text-p3 { + @apply font-sans font-medium text-charcoal-grey text-p3; + } + + .ui-text-p4 { + @apply font-sans font-medium text-charcoal-grey text-p4; + } + + .ui-text-standfirst { + @apply font-sans font-light text-active-orange; + @apply text-standfirst xl:text-standfirst-xl; + @apply tracking-tight xl:tracking-[-0.015em]; + } + + .ui-text-quote { + @apply font-sans font-normal text-cool-black; + @apply text-quote leading-normal; + @apply tracking-[-0.0015em]; + } + + .ui-text-sub-header { + @apply font-sans font-semibold text-neutral-800; + @apply text-sub-header-xs xs:text-sub-header leading-normal; + } + + .ui-text-overline1 { + @apply font-mono font-medium text-active-orange uppercase; + @apply text-overline1 leading-normal; + @apply tracking-[0.16em]; + } + + .ui-text-overline2 { + @apply font-mono font-medium text-active-orange uppercase; + @apply text-overline2 leading-normal; + @apply tracking-[0.16em]; + } + + .ui-text-label1 { + @apply font-sans text-neutral-900 text-label1 font-semibold; + } + + .ui-text-label2 { + @apply font-sans text-neutral-900 text-label2 font-semibold; + } + + .ui-text-label3 { + @apply font-sans text-neutral-900 text-label3 font-semibold; + } + + .ui-text-label4 { + @apply font-sans text-neutral-900 text-label4 font-semibold; + } + + .ui-theme-dark .ui-text-label1, + .ui-theme-dark .ui-text-label2, + .ui-theme-dark .ui-text-label3, + .ui-theme-dark .ui-text-label4 { + @apply text-neutral-300; + } + + .ui-text-code { + @apply font-mono font-normal text-code; + } + + .ui-text-code2 { + @apply font-mono font-normal text-code2; + } + + .ui-text-code-inline { + @apply inline-flex font-mono px-[0.1875rem] py-1 text-code text-neutral-1000 font-medium bg-neutral-200 border border-neutral-400 rounded-md; + } + + .dark .ui-text-code-inline { + @apply text-neutral-300 bg-neutral-1000 border-neutral-900; + } + + .ui-unordered-list { + @apply text-p1 font-medium text-charcoal-grey; + @apply ml-8 my-4; + @apply list-disc; + } + + .ui-ordered-list { + @apply text-p1 font-medium text-charcoal-grey; + @apply ml-8 my-4; + @apply list-decimal; + } + + .ui-ordered-list li, + .ui-unordered-list li { + @apply mb-2; + } + + .ui-unordered-list li > *:last-of-type:not(ul):not(ol), + .ui-ordered-list li > *:last-of-type:not(ul):not(ol) { + margin-bottom: 0; + } + + .ui-unordered-list ul { + @apply ml-6 my-2 list-circle; + } + + .ui-ordered-list ol { + @apply ml-6 my-4 list-decimal; + } + + .ui-unordered-list ul ul { + @apply list-square my-2; + } + + .ui-unordered-list ul ul { + @apply my-2; + } + + .ui-link { + @apply text-gui-default; + @apply hover:text-gui-hover active:text-gui-active disabled:text-gui-unavailable; + @apply focus:text-gui-default focus-base; + @apply no-underline; + } + + .ui-link-neutral { + @apply hover:text-dark-grey active:text-cool-black disabled:text-gui-unavailable; + @apply focus:text-charcoal-grey focus-base; + @apply underline; + } + + .ui-figcaption { + @apply font-mono text-p3 text-neutral-800; + } +} diff --git a/src/styles/ui/core/styles/utils.css b/src/styles/ui/core/styles/utils.css new file mode 100644 index 0000000000..e2e9df68e3 --- /dev/null +++ b/src/styles/ui/core/styles/utils.css @@ -0,0 +1,3 @@ +.focus-base { + @apply focus:outline-none focus-visible:outline-4 focus-visible:outline-offset-0 focus-visible:outline-gui-focus; +} diff --git a/src/styles/ui/core/utils/syntax-highlighter.css b/src/styles/ui/core/utils/syntax-highlighter.css new file mode 100644 index 0000000000..f8c1a05d27 --- /dev/null +++ b/src/styles/ui/core/utils/syntax-highlighter.css @@ -0,0 +1,162 @@ +/* Light theme for code blocks pulled from highlight.js directly*/ +@import "highlight.js/styles/github.min.css"; + +/* Dark theme for code blocks pulled from highlight.js and manually prefixed with ui-theme-dark */ +@layer components { + /*! + Theme: GitHub Dark + Description: Dark theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-dark + Current colors taken from GitHub's CSS +*/ + + .ui-theme-dark .hljs { + color: #c9d1d9; + background: #0d1117; + } + + .ui-theme-dark .hljs-doctag, + .ui-theme-dark .hljs-keyword, + .ui-theme-dark .hljs-meta .hljs-keyword, + .ui-theme-dark .hljs-template-tag, + .ui-theme-dark .hljs-template-variable, + .ui-theme-dark .hljs-type, + .ui-theme-dark .hljs-variable.language_ { + /* prettylights-syntax-keyword */ + color: #ff7b72; + } + + .ui-theme-dark .hljs-title, + .ui-theme-dark .hljs-title.class_, + .ui-theme-dark .hljs-title.class_.inherited__, + .ui-theme-dark .hljs-title.function_ { + /* prettylights-syntax-entity */ + color: #d2a8ff; + } + + .ui-theme-dark .hljs-attr, + .ui-theme-dark .hljs-attribute, + .ui-theme-dark .hljs-literal, + .ui-theme-dark .hljs-meta, + .ui-theme-dark .hljs-number, + .ui-theme-dark .hljs-operator, + .ui-theme-dark .hljs-variable, + .ui-theme-dark .hljs-selector-attr, + .ui-theme-dark .hljs-selector-class, + .ui-theme-dark .hljs-selector-id { + /* prettylights-syntax-constant */ + color: #79c0ff; + } + + .ui-theme-dark .hljs-regexp, + .ui-theme-dark .hljs-string, + .ui-theme-dark .hljs-meta .hljs-string { + /* prettylights-syntax-string */ + color: #a5d6ff; + } + + .ui-theme-dark .hljs-built_in, + .ui-theme-dark .hljs-symbol { + /* prettylights-syntax-variable */ + color: #ffa657; + } + + .ui-theme-dark .hljs-comment, + .ui-theme-dark .hljs-code, + .ui-theme-dark .hljs-formula { + /* prettylights-syntax-comment */ + color: #8b949e; + } + + .ui-theme-dark .hljs-name, + .ui-theme-dark .hljs-quote, + .ui-theme-dark .hljs-selector-tag, + .ui-theme-dark .hljs-selector-pseudo { + /* prettylights-syntax-entity-tag */ + color: #7ee787; + } + + .ui-theme-dark .hljs-subst { + /* prettylights-syntax-storage-modifier-import */ + color: #c9d1d9; + } + + .ui-theme-dark .hljs-section { + /* prettylights-syntax-markup-heading */ + color: #1f6feb; + font-weight: bold; + } + + .ui-theme-dark .hljs-bullet { + /* prettylights-syntax-markup-list */ + color: #f2cc60; + } + + .ui-theme-dark .hljs-emphasis { + /* prettylights-syntax-markup-italic */ + color: #c9d1d9; + font-style: italic; + } + + .ui-theme-dark .hljs-strong { + /* prettylights-syntax-markup-bold */ + color: #c9d1d9; + font-weight: bold; + } + + .ui-theme-dark .hljs-addition { + /* prettylights-syntax-markup-inserted */ + color: #aff5b4; + background-color: #033a16; + } + + .ui-theme-dark .hljs-deletion { + /* prettylights-syntax-markup-deleted */ + color: #ffdcd7; + background-color: #67060c; + } + + .ui-theme-dark .hljs-char.escape_, + .ui-theme-dark .hljs-link, + .ui-theme-dark .hljs-params, + .ui-theme-dark .hljs-property, + .ui-theme-dark .hljs-punctuation, + .ui-theme-dark .hljs-tag { + /* purposely ignored */ + } + + /* Line highlighting styles – rail + subtle tint */ + .code-line-addition { + background-color: var(--color-green-100); + box-shadow: inset 3px 0 0 var(--color-green-400); + } + + .code-line-removal { + background-color: var(--color-orange-100); + box-shadow: inset 3px 0 0 var(--color-gui-error-red); + } + + .code-line-highlight { + background-color: var(--color-yellow-100); + box-shadow: inset 3px 0 0 var(--color-yellow-400); + } + + .ui-theme-dark .code-line-addition { + background-color: var(--color-green-900); + box-shadow: inset 3px 0 0 var(--color-green-600); + } + + .ui-theme-dark .code-line-removal { + background-color: var(--color-orange-1100); + box-shadow: inset 3px 0 0 var(--color-gui-error-red); + } + + .ui-theme-dark .code-line-highlight { + background-color: var(--color-yellow-900); + box-shadow: inset 3px 0 0 var(--color-yellow-600); + } +} diff --git a/src/styles/ui/reset/styles.css b/src/styles/ui/reset/styles.css new file mode 100644 index 0000000000..7810f8c948 --- /dev/null +++ b/src/styles/ui/reset/styles.css @@ -0,0 +1,2 @@ +@import "./styles/normalize.css"; +@import "./styles/reset.css"; diff --git a/src/styles/ui/reset/styles/normalize.css b/src/styles/ui/reset/styles/normalize.css new file mode 100644 index 0000000000..c6a6478163 --- /dev/null +++ b/src/styles/ui/reset/styles/normalize.css @@ -0,0 +1,353 @@ +@layer base { + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + + /* Document + ========================================================================== */ + + /** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + + html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + } + + /* Sections + ========================================================================== */ + + /** + * Remove the margin in all browsers. + */ + + body { + margin: 0; + } + + /** + * Render the `main` element consistently in IE. + */ + + main { + display: block; + } + + /** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + + h1 { + font-size: 2em; + margin: 0.67em 0; + } + + /* Grouping content + ========================================================================== */ + + /** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + + hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ + } + + /** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + + pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ + } + + /* Text-level semantics + ========================================================================== */ + + /** + * Remove the gray background on active links in IE 10. + */ + + a { + background-color: transparent; + } + + /** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + + abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ + } + + /** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + + b, + strong { + font-weight: bolder; + } + + /** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + + code, + kbd, + samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ + } + + /** + * Add the correct font size in all browsers. + */ + + small { + font-size: 80%; + } + + /** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + + sub, + sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + } + + sub { + bottom: -0.25em; + } + + sup { + top: -0.5em; + } + + /* Embedded content + ========================================================================== */ + + /** + * Remove the border on images inside links in IE 10. + */ + + img { + border-style: none; + } + + /* Forms + ========================================================================== */ + + /** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + + button, + input, + optgroup, + select, + textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ + } + + /** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + + button, + input { + /* 1 */ + overflow: visible; + } + + /** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + + button, + select { + /* 1 */ + text-transform: none; + } + + /** + * Correct the inability to style clickable types in iOS and Safari. + */ + + button, + [type="button"], + [type="reset"], + [type="submit"] { + -webkit-appearance: button; + } + + /** + * Remove the inner border and padding in Firefox. + */ + + button::-moz-focus-inner, + [type="button"]::-moz-focus-inner, + [type="reset"]::-moz-focus-inner, + [type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; + } + + /** + * Restore the focus styles unset by the previous rule. + */ + + button:-moz-focusring, + [type="button"]:-moz-focusring, + [type="reset"]:-moz-focusring, + [type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; + } + + /** + * Correct the padding in Firefox. + */ + + fieldset { + padding: 0.35em 0.75em 0.625em; + } + + /** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + + legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ + } + + /** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + + progress { + vertical-align: baseline; + } + + /** + * Remove the default vertical scrollbar in IE 10+. + */ + + textarea { + overflow: auto; + } + + /** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + + [type="checkbox"], + [type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ + } + + /** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + + [type="number"]::-webkit-inner-spin-button, + [type="number"]::-webkit-outer-spin-button { + height: auto; + } + + /** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + + [type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ + } + + /** + * Remove the inner padding in Chrome and Safari on macOS. + */ + + [type="search"]::-webkit-search-decoration { + -webkit-appearance: none; + } + + /** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + + ::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ + } + + /* Interactive + ========================================================================== */ + + /* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + + details { + display: block; + } + + /* + * Add the correct display in all browsers. + */ + + summary { + display: list-item; + } + + /* Misc + ========================================================================== */ + + /** + * Add the correct display in IE 10+. + */ + + template { + display: none; + } + + /** + * Add the correct display in IE 10. + */ + + [hidden] { + display: none; + } +} diff --git a/src/styles/ui/reset/styles/reset.css b/src/styles/ui/reset/styles/reset.css new file mode 100644 index 0000000000..1e7591ecb7 --- /dev/null +++ b/src/styles/ui/reset/styles/reset.css @@ -0,0 +1,135 @@ +/* Reset based on https://unpkg.com/tailwindcss@1.9.6/dist/base.css */ +@layer base { + html { + font-family: + system-ui, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + "Helvetica Neue", + Arial, + "Noto Sans", + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol", + "Noto Color Emoji"; + line-height: 1.5; + } + + blockquote, + dl, + dd, + h1, + h2, + h3, + h4, + h5, + h6, + figure, + p, + pre { + margin: 0; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + font-size: inherit; + font-weight: inherit; + } + + fieldset { + margin: 0; + padding: 0; + } + + ol, + ul { + list-style: none; + margin: 0; + padding: 0; + } + + pre, + code, + kbd, + samp { + font-family: + Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + } + + button { + background-color: transparent; + background-image: none; + } + + button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; + } + + img, + video { + max-width: 100%; + height: auto; + } + + *, + ::before, + ::after { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: #e2e8f0; + } + + hr { + border-top-width: 1px; + } + + img { + border-style: solid; + } + + textarea { + resize: vertical; + } + + input:-ms-input-placeholder, + textarea:-ms-input-placeholder { + color: #a0aec0; + } + + input::-ms-input-placeholder, + textarea::-ms-input-placeholder { + color: #a0aec0; + } + + input::placeholder, + textarea::placeholder { + color: #a0aec0; + } + + input::-webkit-search-cancel-button { + display: none; + } + + button, + [role="button"] { + cursor: pointer; + } + + table { + border-collapse: collapse; + } + + a { + color: inherit; + text-decoration: inherit; + } +} diff --git a/src/templates/examples.tsx b/src/templates/examples.tsx index 67cd3222a2..d5bcd9a58d 100644 --- a/src/templates/examples.tsx +++ b/src/templates/examples.tsx @@ -1,8 +1,8 @@ import React, { PropsWithChildren, useContext, useEffect, useRef } from 'react'; import Markdown from 'markdown-to-jsx/react'; import { Link } from 'gatsby'; -import Icon from '@ably/ui/core/Icon'; -import LinkButton from '@ably/ui/core/LinkButton'; +import Icon from 'src/components/Icon'; +import LinkButton from 'src/components/ui/LinkButton'; import { UnstyledOpenInCodeSandboxButton } from '@codesandbox/sandpack-react'; import { Head } from '../components/Head'; 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})`; +}; diff --git a/src/utilities/syntax-highlighter-registry.js b/src/utilities/syntax-highlighter-registry.js new file mode 100644 index 0000000000..0d7e4bfbc7 --- /dev/null +++ b/src/utilities/syntax-highlighter-registry.js @@ -0,0 +1,63 @@ +// This file can be used in the browser, but because of the weight of all the language +// definitions, preferably it should be used on the server. + +import bash from 'highlight.js/lib/languages/bash'; +import cpp from 'highlight.js/lib/languages/cpp'; +import csharp from 'highlight.js/lib/languages/csharp'; +import css from 'highlight.js/lib/languages/css'; +import dart from 'highlight.js/lib/languages/dart'; +import dos from 'highlight.js/lib/languages/dos'; +import diff from 'highlight.js/lib/languages/diff'; +import erlang from 'highlight.js/lib/languages/erlang'; +import elixir from 'highlight.js/lib/languages/elixir'; +import plaintext from 'highlight.js/lib/languages/plaintext'; +import go from 'highlight.js/lib/languages/go'; +import http from 'highlight.js/lib/languages/http'; +import java from 'highlight.js/lib/languages/java'; +import javascript from 'highlight.js/lib/languages/javascript'; +import typescript from 'highlight.js/lib/languages/typescript'; +import json from 'highlight.js/lib/languages/json'; +import objectivec from 'highlight.js/lib/languages/objectivec'; +import php from 'highlight.js/lib/languages/php'; +import python from 'highlight.js/lib/languages/python'; +import ruby from 'highlight.js/lib/languages/ruby'; +import swift from 'highlight.js/lib/languages/swift'; +import kotlin from 'highlight.js/lib/languages/kotlin'; +import sql from 'highlight.js/lib/languages/sql'; +import xml from 'highlight.js/lib/languages/xml'; +import yaml from 'highlight.js/lib/languages/yaml'; +import curl from 'highlightjs-curl/src/languages/curl'; + +const registry = [ + { label: 'Text', key: 'text', module: plaintext }, + { label: 'JS', key: 'javascript', module: javascript }, + { label: 'TS', key: 'typescript', module: typescript }, + { label: 'Java', key: 'java', module: java }, + { label: 'Ruby', key: 'ruby', module: ruby }, + { label: 'Python', key: 'python', module: python }, + { label: 'PHP', key: 'php', module: php }, + { label: 'Shell', key: 'bash', module: bash }, + { label: 'C#', key: 'cs', module: csharp }, + { label: 'CSS', key: 'css', module: css }, + { label: 'Go', key: 'go', module: go }, + { label: 'HTML', key: 'xml', module: xml }, + { label: 'HTTP', key: 'http', module: http }, + { label: 'C++', key: 'cpp', module: cpp }, + { label: 'Dart', key: 'dart', module: dart }, + { label: 'Swift', key: 'swift', module: swift }, + { label: 'Kotlin', key: 'kotlin', module: kotlin }, + { label: 'Objective C', key: 'objectivec', module: objectivec }, + { label: 'Node.js', key: 'javascript', module: javascript }, + { label: 'JSON', key: 'json', module: json }, + { label: 'DOS', key: 'dos', module: dos }, + { label: 'YAML', key: 'yaml', module: yaml }, + { label: 'Erlang', key: 'erlang', module: erlang }, + { label: 'Elixir', key: 'elixir', module: elixir }, + { label: 'Diff', key: 'diff', module: diff }, + { label: 'SQL', key: 'sql', module: sql }, + { label: 'cURL', key: 'curl', module: curl }, + { label: 'HTML', key: 'html', module: xml }, + { label: 'XML', key: 'xml', module: xml }, +]; + +export default registry; diff --git a/src/utilities/syntax-highlighter.js b/src/utilities/syntax-highlighter.js new file mode 100644 index 0000000000..7cb6e50a20 --- /dev/null +++ b/src/utilities/syntax-highlighter.js @@ -0,0 +1,210 @@ +import hljs from 'highlight.js/lib/core'; + +// Map certain frameworks, protocols etc to available langauage packs +const languageToHighlightKey = (lang) => { + let id; + + if (!lang) { + lang = 'text'; + } + + switch (lang.toLowerCase()) { + case 'android': + id = 'java'; + break; + + case '.net': + case 'net': + case 'dotnet': + case 'csharp': + case 'c#': + id = 'cs'; + break; + + case 'objc': + case 'objective c': + id = 'objectivec'; + break; + + case 'laravel': + id = 'php'; + break; + + case 'flutter': + id = 'dart'; + break; + + case 'node.js': + case 'js': + id = 'javascript'; + break; + + case 'ts': + id = 'typescript'; + break; + + case 'kotlin': + case 'kt': + id = 'kotlin'; + break; + + case 'shell': + case 'fh': + case 'sh': + id = 'bash'; + break; + + case 'https': + case 'http': + case 'txt': + case 'plaintext': + id = 'text'; + break; + + case 'cmd': + case 'bat': + id = 'dos'; + break; + + case 'yml': + id = 'yaml'; + break; + + case 'erl': + id = 'erlang'; + break; + + case 'patch': + id = 'diff'; + break; + + case 'svg': + id = 'xml'; + break; + + default: + break; + } + + return id || lang; +}; + +const registerDefaultLanguages = (register) => { + register.forEach(({ key, module }) => hljs.registerLanguage(key, module)); +}; + +const highlightSnippet = (languageKeyword, snippet) => { + const language = languageToHighlightKey(languageKeyword); + if (typeof snippet !== 'string' || !snippet || !language) return; + + return hljs.highlight(snippet, { language }).value; +}; + +/** + * Parse line highlight specifications from a meta string. + * + * Syntax: `highlight="+1-3,-5,7"` + * - `+` prefix: addition (green) + * - `-` prefix: removal (red) + * - no prefix: neutral highlight (blue) + * - `N-M`: inclusive line range + * - comma-separated for multiple specs + * + * @param {string} languageString - the language, e.g. "javascript" + * @param {string} [meta] - string containing highlight specs, e.g. 'highlight="+1-3,-5,7"' + * @returns {{ lang: string, highlights: Record }} + */ +const parseLineHighlights = (languageString, meta) => { + if (!meta) { + return { lang: languageString, highlights: {} }; + } + + const match = meta.match(/highlight=["']?([^"']+)["']?/); + if (!match) { + return { lang: languageString, highlights: {} }; + } + + const spec = match[1]; + const highlights = {}; + + const tokens = spec.split(','); + for (const token of tokens) { + const trimmed = token.trim(); + if (!trimmed) continue; + + let type = 'highlight'; + let rangePart = trimmed; + + if (trimmed.startsWith('+')) { + type = 'addition'; + rangePart = trimmed.slice(1); + } else if (trimmed.startsWith('-')) { + type = 'removal'; + rangePart = trimmed.slice(1); + } + + const rangeMatch = rangePart.match(/^(\d+)(?:-(\d+))?$/); + if (!rangeMatch) continue; + + const start = parseInt(rangeMatch[1], 10); + const end = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : start; + + for (let i = start; i <= end; i++) { + highlights[i] = type; + } + } + + return { lang: languageString, highlights }; +}; + +/** + * Split highlighted HTML by newlines, repairing any spans that cross + * line boundaries so each line fragment is valid HTML. + * + * @param {string} html - HTML string produced by highlight.js + * @returns {string[]} one HTML fragment per source line + */ +const splitHtmlLines = (html) => { + const rawLines = html.split('\n'); + const result = []; + let openTags = []; + + for (const rawLine of rawLines) { + let line = openTags.join('') + rawLine; + + // Process open/close tags in document order + const tagPattern = /<(\/?)span([^>]*)>/g; + let m; + while ((m = tagPattern.exec(rawLine)) !== null) { + if (m[1] === '/') { + openTags.pop(); + } else { + openTags.push(m[0]); + } + } + + // Close any tags still open so this line is valid HTML + for (let i = 0; i < openTags.length; i++) { + line += ''; + } + + result.push(line); + } + + return result; +}; + +const LINE_HIGHLIGHT_CLASSES = { + addition: 'code-line-addition', + removal: 'code-line-removal', + highlight: 'code-line-highlight', +}; + +export { + highlightSnippet, + languageToHighlightKey, + LINE_HIGHLIGHT_CLASSES, + parseLineHighlights, + registerDefaultLanguages, + splitHtmlLines, +}; diff --git a/tailwind.config.js b/tailwind.config.js index d9620ed5c2..9cb5896926 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,25 +1,260 @@ -const extendConfig = require('@ably/ui/tailwind.extend.js'); - -module.exports = extendConfig((ablyUIConfig) => ({ - ...ablyUIConfig, - content: [ - './src/pages/docs/*.{ts,tsx}', - './src/components/**/*.{ts,tsx}', - './src/templates/**/*.{ts,tsx}', - ...ablyUIConfig.content, - ], +// docs design tokens — originally @ably/ui's Tailwind theme, now owned locally +// (DX-1128). gui-disabled-* alias gui-unavailable for the disabled-state classes. +module.exports = { + content: ['./src/pages/docs/*.{ts,tsx}', './src/components/**/*.{ts,tsx}', './src/templates/**/*.{ts,tsx}'], + safelist: [{ pattern: /^hljs.*/ }], + darkMode: ['selector', '.ui-theme-dark'], theme: { - ...ablyUIConfig.theme, + screens: { + xs: '428px', + sm: '768px', + md: '1040px', + lg: '1280px', + xl: '1440px', + }, + fontFamily: { + sans_next: ['NEXT Book', 'Arial', 'Helvetica', 'sans-serif'], + serif: ['ui-serif', 'Georgia', 'Cambria', 'Times New Roman', 'Times', 'serif'], + mono_source_code: ['Source Code Pro', 'Courier', 'monospace'], + sans: [ + 'Manrope', + 'ui-sans-serif', + 'system-ui', + '-apple-system', + 'BlinkMacSystemFont', + 'Segoe UI', + 'Roboto', + 'Helvetica Neue', + 'Arial', + 'Noto Sans', + 'sans-serif', + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol', + 'Noto Color Emoji', + ], + mono: [ + 'JetBrains Mono', + 'ui-monospace', + 'SFMono-Regular', + 'Menlo', + 'Monaco', + 'Consolas', + 'Liberation Mono', + 'Courier New', + 'monospace', + ], + }, extend: { - ...ablyUIConfig.theme.extend, fontFamily: { serif: ['IBM Plex Serif', 'Georgia', 'Cambria', 'Times New Roman', 'Times', 'serif'], }, + colors: { + 'neutral-000': 'var(--color-neutral-000)', + 'neutral-100': 'var(--color-neutral-100)', + 'neutral-200': 'var(--color-neutral-200)', + 'neutral-300': 'var(--color-neutral-300)', + 'neutral-400': 'var(--color-neutral-400)', + 'neutral-500': 'var(--color-neutral-500)', + 'neutral-600': 'var(--color-neutral-600)', + 'neutral-700': 'var(--color-neutral-700)', + 'neutral-800': 'var(--color-neutral-800)', + 'neutral-900': 'var(--color-neutral-900)', + 'neutral-1000': 'var(--color-neutral-1000)', + 'neutral-1100': 'var(--color-neutral-1100)', + 'neutral-1200': 'var(--color-neutral-1200)', + 'neutral-1300': 'var(--color-neutral-1300)', + 'orange-100': 'var(--color-orange-100)', + 'orange-200': 'var(--color-orange-200)', + 'orange-300': 'var(--color-orange-300)', + 'orange-400': 'var(--color-orange-400)', + 'orange-500': 'var(--color-orange-500)', + 'orange-600': 'var(--color-orange-600)', + 'orange-700': 'var(--color-orange-700)', + 'orange-800': 'var(--color-orange-800)', + 'orange-900': 'var(--color-orange-900)', + 'orange-1000': 'var(--color-orange-1000)', + 'orange-1100': 'var(--color-orange-1100)', + 'yellow-100': 'var(--color-yellow-100)', + 'yellow-200': 'var(--color-yellow-200)', + 'yellow-300': 'var(--color-yellow-300)', + 'yellow-400': 'var(--color-yellow-400)', + 'yellow-500': 'var(--color-yellow-500)', + 'yellow-600': 'var(--color-yellow-600)', + 'yellow-700': 'var(--color-yellow-700)', + 'yellow-800': 'var(--color-yellow-800)', + 'yellow-900': 'var(--color-yellow-900)', + 'green-100': 'var(--color-green-100)', + 'green-200': 'var(--color-green-200)', + 'green-300': 'var(--color-green-300)', + 'green-400': 'var(--color-green-400)', + 'green-500': 'var(--color-green-500)', + 'green-600': 'var(--color-green-600)', + 'green-700': 'var(--color-green-700)', + 'green-800': 'var(--color-green-800)', + 'green-900': 'var(--color-green-900)', + 'blue-100': 'var(--color-blue-100)', + 'blue-200': 'var(--color-blue-200)', + 'blue-300': 'var(--color-blue-300)', + 'blue-400': 'var(--color-blue-400)', + 'blue-500': 'var(--color-blue-500)', + 'blue-600': 'var(--color-blue-600)', + 'blue-700': 'var(--color-blue-700)', + 'blue-800': 'var(--color-blue-800)', + 'blue-900': 'var(--color-blue-900)', + 'violet-100': 'var(--color-violet-100)', + 'violet-200': 'var(--color-violet-200)', + 'violet-300': 'var(--color-violet-300)', + 'violet-400': 'var(--color-violet-400)', + 'violet-500': 'var(--color-violet-500)', + 'violet-600': 'var(--color-violet-600)', + 'violet-700': 'var(--color-violet-700)', + 'violet-800': 'var(--color-violet-800)', + 'violet-900': 'var(--color-violet-900)', + 'pink-100': 'var(--color-pink-100)', + 'pink-200': 'var(--color-pink-200)', + 'pink-300': 'var(--color-pink-300)', + 'pink-400': 'var(--color-pink-400)', + 'pink-500': 'var(--color-pink-500)', + 'pink-600': 'var(--color-pink-600)', + 'pink-700': 'var(--color-pink-700)', + 'pink-800': 'var(--color-pink-800)', + 'pink-900': 'var(--color-pink-900)', + 'gui-blue-default-light': 'var(--color-gui-blue-default-light)', + 'gui-blue-hover-light': 'var(--color-gui-blue-hover-light)', + 'gui-blue-active-light': 'var(--color-gui-blue-active-light)', + 'gui-blue-default-dark': 'var(--color-gui-blue-default-dark)', + 'gui-blue-hover-dark': 'var(--color-gui-blue-hover-dark)', + 'gui-blue-active-dark': 'var(--color-gui-blue-active-dark)', + 'gui-blue-focus': 'var(--color-gui-blue-focus)', + 'gui-blue-visited': 'var(--color-gui-blue-focus)', + 'gui-unavailable': 'var(--color-gui-unavailable)', + 'gui-unavailable-dark': 'var(--color-gui-unavailable-dark)', + 'gui-disabled-light': 'var(--color-gui-disabled-light)', + 'gui-disabled-dark': 'var(--color-gui-disabled-dark)', + 'gui-success-green': 'var(--color-gui-success-green)', + 'gui-error-red': 'var(--color-gui-error-red)', + 'gui-focus': 'var(--color-gui-focus)', + 'gui-focus-outline': 'var(--color-gui-focus-outline)', + transparent: 'transparent', + 'cool-black': 'var(--color-neutral-1300)', + 'active-orange': 'var(--color-orange-600)', + 'red-orange': 'var(--color-orange-800)', + white: 'var(--color-neutral-000)', + 'electric-cyan': 'var(--color-blue-400)', + 'zingy-green': 'var(--color-green-400)', + 'bright-red': 'var(--color-orange-700)', + 'jazzy-pink': 'var(--color-pink-500)', + 'extra-light-grey': 'var(--color-neutral-100)', + 'light-grey': 'var(--color-neutral-200)', + 'mid-grey': 'var(--color-neutral-500)', + 'dark-grey': 'var(--color-neutral-800)', + 'charcoal-grey': 'var(--color-neutral-1000)', + 'gui-default': 'var(--color-gui-blue-default-light)', + 'gui-hover': 'var(--color-gui-blue-hover-light)', + 'gui-active': 'var(--color-gui-blue-active-light)', + 'gui-default-dark': 'var(--color-gui-blue-default-dark)', + 'gui-hover-dark': 'var(--color-gui-blue-hover-dark)', + 'gui-active-dark': 'var(--color-gui-blue-active-dark)', + 'gui-alt': 'var(--color-neutral-1300)', + 'gui-error': 'var(--color-gui-error-red)', + 'gui-success': 'var(--color-gui-success-green)', + 'icon-linkedin': 'var(--icon-color-linkedin)', + 'icon-twitter': 'var(--icon-color-twitter)', + 'icon-glassdoor': 'var(--icon-color-glassdoor)', + 'icon-github': 'var(--icon-color-github)', + 'icon-discord': 'var(--icon-color-discord)', + }, + fontSize: { + title: ['var(--fs-title)', 'var(--lh-min-normal)'], + 'title-xl': ['var(--fs-title-xl)', 'var(--lh-min-normal)'], + 'title-xs': ['var(--fs-title-xs)', 'var(--lh-min-normal)'], + h1: ['var(--fs-h1)', 'var(--lh-dense)'], + 'h1-xl': ['var(--fs-h1-xl)', 'var(--lh-min-normal)'], + 'h1-xs': ['var(--fs-h1-xs)', 'var(--lh-min-normal)'], + h2: ['var(--fs-h2)', 'var(--lh-min-normal)'], + 'h2-xl': ['var(--fs-h2-xl)', 'var(--lh-min-normal)'], + 'h2-xs': ['var(--fs-h2-xs)', 'var(--lh-min-normal)'], + h3: ['var(--fs-h3)', 'var(--lh-min-normal)'], + h4: ['var(--fs-h4)', 'var(--lh-min-normal)'], + h5: ['var(--fs-h5)', 'var(--lh-min-normal)'], + p1: ['var(--fs-p1)', 'var(--lh-relaxed)'], + p2: ['var(--fs-p2)', 'var(--lh-relaxed)'], + p3: ['var(--fs-p3)', 'var(--lh-relaxed)'], + p4: ['var(--fs-p4)', 'var(--lh-relaxed)'], + standfirst: ['var(--fs-standfirst)'], + 'standfirst-xl': ['var(--fs-standfirst-xl)'], + 'sub-header': ['var(--fs-sub-header)'], + 'sub-header-xs': ['var(--fs-sub-header-xs)'], + overline1: ['var(--fs-overline1)'], + overline2: ['var(--fs-overline2)'], + label1: ['var(--fs-label1)', 'var(--lh-snug)'], + label2: ['var(--fs-label2)', 'var(--lh-snug)'], + label3: ['var(--fs-label3)', 'var(--lh-snug)'], + label4: ['var(--fs-label4)', 'var(--lh-snug)'], + quote: ['var(--fs-quote)'], + code: ['var(--fs-code)', 'var(--lh-none)'], + code2: ['var(--fs-code2)', 'var(--lh-none)'], + }, + backgroundImage: { + 'gradient-active-orange': 'var(--gradient-active-orange)', + 'gradient-hot-pink': 'var(--gradient-hot-pink)', + 'gradient-priority-button': 'var(--gradient-priority-button)', + }, + borderRadius: { + md: '0.1875rem', + DEFAULT: '0.375rem', + }, + transitionProperty: { + input: 'background-color, box-shadow', + }, + dropShadow: { + toggle: ['0 4px 4px rgba(0,0,0,0.25)', '0 4px 8px rgba(0,0,0,0.15)'], + }, + outline: { + 'gui-focus': '1.5px solid var(--color-gui-focus-outline)', + }, + borderWidth: { + btn: '1.5px', + }, gridTemplateColumns: { + dynamic: + 'repeat(var(--dynamic-grid-columns-count), minmax(var(--dynamic-grid-column-min-width), var(--dynamic-grid-column-max-width)))', 'header-layout': '173px minmax(200px, 400px) 1fr', }, + lineHeight: { + dense: 'var(--lh-dense)', + snug: 'var(--lh-snug)', + relaxed: 'var(--lh-relaxed)', + }, + padding: { + btn: 'var(--spacing-btn)', + 'btn-small': 'var(--spacing-btn-small)', + 'btn-xsmall': 'var(--spacing-btn-xsmall)', + 'btn-large': 'var(--spacing-btn-large)', + 'menu-row': 'var(--spacing-menu-row)', + 'menu-row-snug': 'var(--spacing-menu-row-snug)', + 'menu-row-title': 'var(--spacing-menu-row-title)', + media: 'var(--spacing-media)', + input: 'var(--spacing-input)', + }, + boxShadow: { + subtle: '0px 1px 0px var(--color-neutral-500)', + tooltip: '0px 5px 10px 0px #00000022', + container: '0px 40px 40px rgba(0, 0, 0, 0.1)', + 'container-subtle': '0px 16px 64px rgba(0, 0, 0, 0.1)', + input: '0px 0px 8px 0px rgba(8, 103, 196, 1)', // color-gui-hover at 50% + quote: '0rem 1.5rem 2rem 0rem #0000000d', + }, keyframes: { - ...ablyUIConfig.theme.extend.keyframes, + tooltipEntry: { + '0%': { transform: 'translateY(8px)', opacity: 0 }, + '100%': { transform: 'translateY(0)', opacity: 1 }, + }, + tooltipExit: { + '0%': { opacity: 1 }, + '100%': { opacity: 0 }, + }, 'accordion-down': { from: { height: '0' }, to: { height: 'var(--radix-accordion-content-height)' }, @@ -28,12 +263,60 @@ module.exports = extendConfig((ablyUIConfig) => ({ from: { height: 'var(--radix-accordion-content-height)' }, to: { height: '0' }, }, + 'fade-in-ten-percent': { + from: { opacity: 0 }, + to: { opacity: 0.1 }, + }, + 'fade-out-ten-percent': { + from: { opacity: 0.1 }, + to: { opacity: 0 }, + }, + 'scale-in': { + from: { opacity: '0', transform: 'rotateX(-10deg) scale(0.9)' }, + to: { opacity: '1', transform: 'rotateX(0deg) scale(1)' }, + }, + 'scale-out': { + from: { opacity: '1', transform: 'rotateX(0deg) scale(1)' }, + to: { opacity: '0', transform: 'rotateX(-10deg) scale(0.95)' }, + }, + 'enter-from-right': { + from: { opacity: '0', transform: 'translateX(200px)' }, + to: { opacity: '1', transform: 'translateX(0)' }, + }, + 'enter-from-left': { + from: { opacity: '0', transform: 'translateX(-200px)' }, + to: { opacity: '1', transform: 'translateX(0)' }, + }, + 'exit-to-right': { + from: { opacity: '1', transform: 'translateX(0)' }, + to: { opacity: '0', transform: 'translateX(200px)' }, + }, + 'exit-to-left': { + from: { opacity: '1', transform: 'translateX(0)' }, + to: { opacity: '0', transform: 'translateX(-200px)' }, + }, }, animation: { - ...ablyUIConfig.theme.extend.animation, 'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out', + 'scale-in': 'scale-in 200ms ease', + 'scale-out': 'scale-out 200ms ease', + 'enter-from-left': 'enter-from-left 250ms ease', + 'enter-from-right': 'enter-from-right 250ms ease', + 'exit-to-left': 'exit-to-left 250ms ease', + 'exit-to-right': 'exit-to-right 250ms ease', }, }, + listStyleType: { + none: 'none', + disc: 'disc', + decimal: 'decimal', + square: 'square', + circle: 'circle', + }, + }, + corePlugins: { + preflight: false, }, -})); + plugins: [require('@tailwindcss/container-queries')], +}; diff --git a/yarn.lock b/yarn.lock index acf0862fb2..f6443f8210 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15187,6 +15187,14 @@ swr@^2.2.5: dequal "^2.0.3" use-sync-external-store "^1.6.0" +swr@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.4.1.tgz#c9e48abff6bf4b04846342e2f1f6be108a078cf6" + integrity sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA== + dependencies: + dequal "^2.0.3" + use-sync-external-store "^1.6.0" + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"