UNPKG

@zohodesk/docs-builder

Version:

docs-builder is used to build your own docs

369 lines (350 loc) 12 kB
import React, { useState, useEffect, useRef, useMemo } from "react"; import { Route, Switch, useRouteMatch, useHistory } from "react-router-dom"; import PropTypes from "../PropTypes/PropTypes"; import DocsCode from "../DocsCode/DocsCode"; import { ComponentPlayground } from "@zohodesk-private/component-playground"; import styles from "./Preview.module.css"; import { useResizer } from "./useResizer"; function IFrame(props) { return /*#__PURE__*/React.createElement("iframe", { ...props, ref: props.eref }); } // Custom NavLink component to avoid default Link styling and handle active state const TabLink = _ref => { let { to, isActive, disabled, className, onClick, children } = _ref; const history = useHistory(); const handleClick = e => { if (disabled) { e.preventDefault(); return; } if (onClick) { onClick(e); } history.push(to); }; return /*#__PURE__*/React.createElement("button", { onClick: handleClick, className: `${className} ${isActive ? styles.activeTab : ""} ${disabled ? styles.disabledTab : ""}`, disabled: disabled }, children); }; export default function Preview(_ref2) { let { componentName, components, handleFullScreen, isFullScreen } = _ref2; const iframeRef = useRef(null); const sliderRef = useRef(null); const match = useRouteMatch(); const history = useHistory(); const resizerRef = useRef(null); const iframeContainerRef = useRef(null); const [device, setDevice] = useState("DEFAULT"); const [loading, setLoading] = useState(true); const [frameWidth, setFrameWidth] = useState("100%"); const [isRTL, setIsRTL] = useState(false); const [isContentEditable, setIsContentEditable] = useState(false); const [windowSize, setWindowSize] = useState({ height: 0, width: 0 }); const [isResizingStarted, setIsResizingStarted] = useState(false); const { isResizing } = useResizer({ resizerHandleRef: resizerRef, resizableElementRef: iframeContainerRef, orientation: 'vertical', enabled: true, onWidthChange: width => { setFrameWidth(width); }, onResizeStart: started => { setIsResizingStarted(started); } }); const handleIframeLoad = () => { setLoading(false); }; useEffect(() => { // Initial setup if (sliderRef.current) { sliderRef.current.value = "11"; iframeSize(); } }, []); useEffect(() => { const iframeDocument = iframeRef.current?.contentDocument || iframeRef.current?.contentWindow?.document; if (iframeDocument) { iframeDocument.body.style.direction = isRTL ? "rtl" : "ltr"; iframeDocument.body.setAttribute("contentEditable", isContentEditable ? "true" : "false"); } }, [isRTL, isContentEditable]); const handleContentEditable = () => { setIsContentEditable(prevState => !prevState); }; const handleReload = () => { setLoading(true); iframeRef.current.contentWindow.location.reload(true); }; const handleRTL = () => { setIsRTL(prevState => !prevState); }; const handleReset = () => { if (sliderRef.current) { sliderRef.current.value = "11"; iframeSize(); } }; function iframeSize() { let value = sliderRef.current.value; let sizes = { "1": { device: "MOBILE_XS", frameWidth: 320 }, "2": { device: "MOBILE_S", frameWidth: 360 }, "3": { device: "MOBILE_M", frameWidth: 375.04 }, "4": { device: "MOBILE", frameWidth: 480 }, "5": { device: "TABLET_S", frameWidth: 640 }, "6": { device: "TABLET_M", frameWidth: 720 }, "7": { device: "TABLET", frameWidth: 768 }, "8": { device: "LAPTOP_S", frameWidth: 1024 }, "9": { device: "LAPTOP_M", frameWidth: 1280 }, "10": { device: "LAPTOP", frameWidth: 1440 }, "11": { device: "DEFAULT", frameWidth: `100%` }, "12": { device: "MONITOR_M", frameWidth: 1600 }, "13": { device: "MONITOR", frameWidth: 1920 } }; setIsResizingStarted(false); setDevice(sizes[value].device); setFrameWidth(sizes[value].frameWidth); getWindow(); } function getWindow() { if (iframeRef.current) { setWindowSize({ height: iframeRef.current.offsetHeight, width: iframeRef.current.offsetWidth }); } } const playGroundData = useMemo(() => components[componentName]?.docs?.playgroundProps, [components, componentName]); const displayName = componentName.split("__")[1] || componentName; const baseUrl = `/all/${componentName}`; // Determine active tab based on current path const currentPath = history.location.pathname; const isComponentActive = currentPath === baseUrl; const isPropTypesActive = currentPath === `${baseUrl}/proptypes`; const isCodeActive = currentPath === `${baseUrl}/code`; const isPlaygroundActive = currentPath === `${baseUrl}/playground`; return /*#__PURE__*/React.createElement("div", { className: styles.previewContainer }, /*#__PURE__*/React.createElement("header", { className: styles.header }, /*#__PURE__*/React.createElement("div", { className: styles.headerContent }, /*#__PURE__*/React.createElement("div", { className: styles.titleArea }, /*#__PURE__*/React.createElement("h1", { className: styles.componentTitle }, displayName)), /*#__PURE__*/React.createElement("nav", { className: styles.tabsContainer }, /*#__PURE__*/React.createElement("div", { className: styles.tabs }, /*#__PURE__*/React.createElement(TabLink, { to: baseUrl, className: styles.tab, isActive: isComponentActive }, /*#__PURE__*/React.createElement("span", { className: styles.tabIcon }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-cube" })), "Component"), /*#__PURE__*/React.createElement(TabLink, { to: `${baseUrl}/proptypes`, className: styles.tab, isActive: isPropTypesActive }, /*#__PURE__*/React.createElement("span", { className: styles.tabIcon }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-list-check" })), "PropTypes"), /*#__PURE__*/React.createElement(TabLink, { to: `${baseUrl}/code`, className: styles.tab, isActive: isCodeActive }, /*#__PURE__*/React.createElement("span", { className: styles.tabIcon }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-code" })), "Code"), /*#__PURE__*/React.createElement(TabLink, { to: `${baseUrl}/playground`, className: styles.tab, isActive: isPlaygroundActive, disabled: !playGroundData }, /*#__PURE__*/React.createElement("span", { className: styles.tabIcon }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-puzzle-piece" })), "Playground"))))), /*#__PURE__*/React.createElement("div", { className: styles.contentArea }, /*#__PURE__*/React.createElement(Switch, null, /*#__PURE__*/React.createElement(Route, { exact: true, path: baseUrl }, /*#__PURE__*/React.createElement("div", { className: styles.componentView }, /*#__PURE__*/React.createElement("div", { className: styles.iframeContainer, style: { width: `${frameWidth == "100%" ? frameWidth : `${frameWidth}px`}` }, ref: iframeContainerRef }, loading && /*#__PURE__*/React.createElement("div", { className: styles.loadingOverlay, id: "loadingText" }, /*#__PURE__*/React.createElement("div", { className: styles.loadingSpinner }, /*#__PURE__*/React.createElement("div", null), /*#__PURE__*/React.createElement("div", null))), isResizing && /*#__PURE__*/React.createElement("div", { className: styles.resizeOverlay }), /*#__PURE__*/React.createElement(IFrame, { id: "componentIframe", className: styles.iframe, src: `#/preview/${componentName}`, eref: iframeRef, onLoad: handleIframeLoad }), /*#__PURE__*/React.createElement("div", { ref: resizerRef, className: styles.resizeHandle }, /*#__PURE__*/React.createElement("div", { className: styles.resizeHandleBar })))), /*#__PURE__*/React.createElement("div", { className: styles.toolbar }, /*#__PURE__*/React.createElement("div", { className: styles.toolbarActions }, /*#__PURE__*/React.createElement("button", { onClick: handleReload, className: styles.toolbarButton, title: "Reload" }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-arrow-rotate-left" })), /*#__PURE__*/React.createElement("a", { className: styles.toolbarButton, href: `#/preview/${componentName}`, target: "_blank", rel: "noopener noreferrer", title: "Open in new tab" }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-up-right-from-square" })), /*#__PURE__*/React.createElement("button", { className: `${styles.toolbarButton} ${isRTL ? styles.active : ""}`, onClick: handleRTL, title: "Toggle RTL" }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-paragraph" })), /*#__PURE__*/React.createElement("button", { className: `${styles.toolbarButton} ${isContentEditable ? styles.active : ""}`, onClick: handleContentEditable, title: "Toggle content editable" }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-pen-to-square" })), /*#__PURE__*/React.createElement("button", { className: `${styles.toolbarButton} ${isFullScreen ? styles.active : ""}`, onClick: handleFullScreen, title: "Toggle fullscreen" }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-expand" }))), /*#__PURE__*/React.createElement("div", { className: styles.resizeControls }, /*#__PURE__*/React.createElement("button", { onClick: handleReset, className: `${styles.resetButton}`, title: "Reset to default width", disabled: frameWidth === "100%" }, "Reset"), /*#__PURE__*/React.createElement("input", { min: "1", max: "13", type: "range", defaultValue: "11", step: "1", id: "myRange", className: styles.rangeSlider, ref: sliderRef, onChange: iframeSize, disabled: isResizing }), /*#__PURE__*/React.createElement("div", { className: styles.deviceInfo }, /*#__PURE__*/React.createElement("span", { className: styles.deviceType }, isResizingStarted ? "CUSTOM" : device), windowSize && windowSize.width > 0 && /*#__PURE__*/React.createElement("span", { className: styles.dimensions }, frameWidth, " \xD7 ", windowSize.height))))), /*#__PURE__*/React.createElement(Route, { path: `${baseUrl}/proptypes` }, /*#__PURE__*/React.createElement("div", { className: styles.propTypesView }, /*#__PURE__*/React.createElement(PropTypes, { Components: components, propComName: componentName }))), /*#__PURE__*/React.createElement(Route, { path: `${baseUrl}/code` }, /*#__PURE__*/React.createElement("div", { className: styles.codeView }, /*#__PURE__*/React.createElement(DocsCode, { components: components, propComName: componentName }))), /*#__PURE__*/React.createElement(Route, { path: `${baseUrl}/playground` }, /*#__PURE__*/React.createElement("div", { className: styles.playgroundView }, playGroundData ? /*#__PURE__*/React.createElement(ComponentPlayground, { ...playGroundData }) : /*#__PURE__*/React.createElement("div", { className: styles.noPlayground }, /*#__PURE__*/React.createElement("div", { className: styles.emptyState }, /*#__PURE__*/React.createElement("i", { className: "fa-solid fa-puzzle-piece" }), /*#__PURE__*/React.createElement("h2", null, "No Playground Available"), /*#__PURE__*/React.createElement("p", null, "This component doesn't have a playground configured.")))))))); }