diff --git a/src/@adobe/gatsby-theme-aio/components/Code/index.js b/src/@adobe/gatsby-theme-aio/components/Code/index.js new file mode 100644 index 000000000..eef82aff1 --- /dev/null +++ b/src/@adobe/gatsby-theme-aio/components/Code/index.js @@ -0,0 +1,207 @@ +/* + * Copyright 2025 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import "@spectrum-css/typography"; +import "@spectrum-css/tooltip"; +import "@adobe/prism-adobe"; +import { ActionButton } from "@adobe/gatsby-theme-aio/src/components/ActionButton"; + +import React, { useState } from "react"; +import nextId from "react-id-generator"; +import classNames from "classnames"; +import Highlight, { defaultProps } from "prism-react-renderer"; +import PropTypes from "prop-types"; +import Prism from "prism-react-renderer/prism"; + +import "./styles.css"; + +// Todo - replace this with prod url before merging +const CODE_PLAYGROUND_URL = "https://168534.prenv.projectx.corp.adobe.com/new"; +const CODE_PLAYGROUND_MODE = "playground"; +const CODE_PLAYGROUND_SESSION = "new"; + +(typeof global !== "undefined" ? global : window).Prism = Prism; + +const getLoader = require("prismjs/dependencies"); +const components = require("prismjs/components"); + +const componentsToLoad = [ + "java", + "php", + "csharp", + "kotlin", + "swift", + "bash", + "sql", + "typescript", + "objectivec", + "yaml", + "json", +]; +const loadedComponents = ["clike", "javascript"]; + +// dynamically load all the prism language components +const loader = getLoader(components, componentsToLoad, loadedComponents); +try { + loader.load((id) => { + require(`prismjs/components/prism-${id}.min.js`); + }); +} catch (error) { + console.warn(error); +} + +// show/hide copy tooltip +const showCopyTooltip = (setShouldShowCopyTooltip) => { + setShouldShowCopyTooltip(true); + setTimeout(() => { + setShouldShowCopyTooltip(false); + }, 3000); +}; + +// copy to clipboard +const copyToClipboard = async (codeContent, setIsTooltipOpen) => { + await navigator.clipboard.writeText(codeContent); + showCopyTooltip(setIsTooltipOpen); +}; + +// open code playground +const openCodePlayground = (codeContent) => { + const playgroundData = { + codeJs: codeContent, + mode: "script", + }; + const url = new URL(CODE_PLAYGROUND_URL); + url.searchParams.set("mode", CODE_PLAYGROUND_MODE); + url.searchParams.set("session", CODE_PLAYGROUND_SESSION); + url.searchParams.set("playgroundData", btoa(encodeURIComponent(JSON.stringify(playgroundData)))); + window.open(url.toString(), "_blank"); +}; + +// parse attributes - language and try +// sample usage: ```js{try} +const parseAttributes = (className) => { + let language = className.replace(/language-/, ""); + const attributeMatch = language.match(/^(\w+)\s*\{([^}]+)\}$/); + + let shouldShowTry = false; + + if (attributeMatch) { + language = attributeMatch[1]; + const attributes = attributeMatch[2]; + shouldShowTry = attributes.includes("try"); + } + + return { language, shouldShowTry }; +}; + +const Code = ({ children, className = "", theme }) => { + const [tooltipId] = useState(nextId); + const [shouldShowCopyTooltip, setShouldShowCopyTooltip] = useState(false); + + const { language, shouldShowTry } = parseAttributes(className); + + return ( + + {({ className, tokens, getLineProps, getTokenProps }) => { + const isEmptyItem = (token) => + token && token.length === 1 && token[0].empty; + const lines = isEmptyItem(tokens[tokens.length - 1]) + ? tokens.slice(0, -1) + : tokens; + const isMultiLine = lines.length > 1; + + return ( +
+ {/* Copy Button */} + copyToClipboard(children, setShouldShowCopyTooltip)} + > + Copy + + + {/* Try Button */} + {shouldShowTry && ( + openCodePlayground(children)} + > + Try + + )} + +
+ + + Copied to your clipboard + + + +
+ +
+              {lines.map((line, i) => {
+                const { style: lineStyle, ...lineProps } = getLineProps({ line, key: i });
+
+                return (
+                  
+ {isMultiLine && ( + + )} + + {/* styling the tokens in the line */} + {line.map((token, key) => { + const { style: tokenStyle, ...tokenProps } = getTokenProps({ token, key }); + return ; + })} + +
+ ); + })} +
+
+ ); + }} +
+ ); +}; + +Code.propTypes = { + theme: PropTypes.oneOf(["light", "dark"]), +}; + +export { Code }; diff --git a/src/@adobe/gatsby-theme-aio/components/Code/styles.css b/src/@adobe/gatsby-theme-aio/components/Code/styles.css new file mode 100644 index 000000000..180ad4824 --- /dev/null +++ b/src/@adobe/gatsby-theme-aio/components/Code/styles.css @@ -0,0 +1,72 @@ +.code-container { + position: relative; + max-width: calc(100vw - var(--spectrum-global-dimension-size-800)); +} + +.code-pre { + padding-top: var(--spectrum-global-dimension-size-400) !important; +} + +.code-line { + display: table-row; +} + +.code-line-number { + display: table-cell; + color: var(--spectrum-global-color-gray-500); + text-align: left; + padding-right: var(--spectrum-global-dimension-size-200); + user-select: none; +} + +.code-line-number::before { + content: attr(data-pseudo-content); +} + +.code-line-content { + margin-right: var(--spectrum-global-dimension-size-1000); +} + +.code-action-button { + position: absolute; + inset-block-start: 0; + border-color: var(--spectrum-actionbutton-m-border-color, var(--spectrum-alias-border-color)) !important; + color: var(--spectrum-actionbutton-m-text-color, var(--spectrum-alias-text-color)) !important; + padding: var(--spectrum-global-dimension-size-65); +} + +.code-copy-button { + inset-inline-end: var(--spectrum-global-dimension-size-125); +} + +.code-copy-button.with-try { + inset-inline-end: var(--spectrum-global-dimension-size-700); +} + +.code-try-button { + inset-inline-end: var(--spectrum-global-dimension-size-125); +} + +.code-tooltip-container { + position: absolute; + inset-block-start: var(--spectrum-global-dimension-size-65); + height: var(--spectrum-global-dimension-size-300); + display: flex; + align-items: center; + pointer-events: none; +} + +.code-tooltip-container:not(.with-try) { + inset-inline-end: calc(var(--spectrum-global-dimension-size-125) + var(--spectrum-global-dimension-size-600) + var(--spectrum-global-dimension-size-125)); +} + +.code-tooltip-container.with-try { + inset-inline-end: calc(var(--spectrum-global-dimension-size-700) + var(--spectrum-global-dimension-size-600) + var(--spectrum-global-dimension-size-125)); +} + +.code-tooltip { + display: block; + position: relative; + white-space: nowrap; + font-family: var(--spectrum-alias-body-text-font-family, var(--spectrum-global-font-family-base)); +} \ No newline at end of file diff --git a/src/pages/references/document-sandbox/document-apis/index.md b/src/pages/references/document-sandbox/document-apis/index.md index 8bb1a2693..27ea74564 100644 --- a/src/pages/references/document-sandbox/document-apis/index.md +++ b/src/pages/references/document-sandbox/document-apis/index.md @@ -40,9 +40,7 @@ See the example below for further usage details. The following code snippet illustrates how to use the [Express Document APIs](./classes/Editor.md) from the document sandbox code running in your `code.js` for instance, to access the current document, create a rectangle, set some properties and a fill for the rectangle, and finally, add it to the document: -```js -import { editor, colorUtils } from "express-document-sdk"; - +```js{try} const insertionParent = editor.context.insertionParent; // get node to insert content into const rectangle = editor.createRectangle();