diff --git a/examples/landing/components/editor/Viewport/Sidebar/index.tsx b/examples/landing/components/editor/Viewport/Sidebar/index.tsx index 325556b15..7677e376f 100644 --- a/examples/landing/components/editor/Viewport/Sidebar/index.tsx +++ b/examples/landing/components/editor/Viewport/Sidebar/index.tsx @@ -16,6 +16,86 @@ export const SidebarDiv = styled.div<{ enabled: boolean }>` margin-right: ${(props) => (props.enabled ? 0 : -280)}px; `; +const CarbonAdsContainer = styled.div` + width: 100%; + margin-top: auto; + + #carbonads * { + margin: initial; + padding: initial; + } + + #carbonads { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, + sans-serif; + + padding: 10px 0.5rem; + border-top: 1px solid rgb(0 0 0 / 6%); + } + + #carbonads { + display: flex; + width: 100%; + background-color: transparent; + z-index: 100; + } + + #carbonads a { + color: inherit; + text-decoration: none; + } + + #carbonads a:hover { + color: inherit; + } + + #carbonads span { + position: relative; + display: block; + overflow: hidden; + } + + #carbonads .carbon-wrap { + display: flex; + } + + #carbonads .carbon-img { + display: block; + margin: 0; + line-height: 1; + } + + #carbonads .carbon-img img { + display: block; + } + + #carbonads .carbon-text { + font-size: 11px; + padding: 10px; + margin-bottom: 16px; + line-height: 1.5; + text-align: left; + color: #333333; + font-weight: 400; + } + + #carbonads .carbon-poweredby { + display: block; + padding: 6px 8px; + text-align: center; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; + font-size: 8px; + line-height: 1; + position: absolute; + bottom: 0; + right: 0; + color: #8f8f8f; + } +`; + export const Sidebar = () => { const [layersVisible, setLayerVisible] = useState(true); const [toolbarVisible, setToolbarVisible] = useState(true); @@ -46,6 +126,14 @@ export const Sidebar = () => { + + + ); diff --git a/examples/landing/styles/app.css b/examples/landing/styles/app.css index d6751eb24..982abed39 100644 --- a/examples/landing/styles/app.css +++ b/examples/landing/styles/app.css @@ -25,3 +25,17 @@ body { .transition { transition: 0.4s cubic-bezier(0.19, 1, 0.22, 1); } + +#carbonads * { + margin: initial; + padding: initial; +} + +#carbonads { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, + sans-serif; + + padding: 10px 0.5rem; + border-top: 1px solid rgb(0 0 0 / 6%); +} diff --git a/site/src/css/custom.css b/site/src/css/custom.css index cf832302a..c7d04a538 100755 --- a/site/src/css/custom.css +++ b/site/src/css/custom.css @@ -323,6 +323,10 @@ html[data-theme='dark'] .api-description { outline: none; } +.footer { + z-index: 98; +} + .footer .footer__links { margin-bottom: 1.5rem; padding-bottom: 1.5rem; @@ -450,3 +454,98 @@ html[data-theme='dark'] .api-description { top: 0; margin-top: 0; } + +#carbonads-container { + padding: var(--ifm-menu-link-padding-vertical) + calc(var(--ifm-menu-link-padding-horizontal)) + var(--ifm-menu-link-padding-vertical) + var(--ifm-menu-link-padding-horizontal); + border-top: 1px solid var(--ifm-toc-border-color); + border-right: 1px solid var(--ifm-toc-border-color); +} + +@media screen and (max-width: 996px) { + #carbonads-container { + position: fixed; + margin-top: 20px; + width: 300px; + bottom: 5px; + z-index: 9; + left: 19px; + border-radius: 3px; + background: #fff; + box-shadow: 0px 3px 28px 0px rgb(1 1 1 / 11%); + border: none; + } +} + +#carbonads * { + margin: initial; + padding: initial; +} +#carbonads { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, + sans-serif; +} + +#carbonads { + display: flex; + max-width: 330px; + background-color: transparent; + z-index: 100; +} + +#carbonads a { + color: inherit; + text-decoration: none; +} + +#carbonads a:hover { + color: inherit; +} + +#carbonads span { + position: relative; + display: block; + overflow: hidden; +} + +#carbonads .carbon-wrap { + display: flex; +} + +#carbonads .carbon-img { + display: block; + margin: 0; + line-height: 1; +} + +#carbonads .carbon-img img { + display: block; +} + +#carbonads .carbon-text { + font-size: 11px; + padding: 10px; + margin-bottom: 16px; + line-height: 1.5; + text-align: left; + color: #333333; + font-weight: 400; +} + +#carbonads .carbon-poweredby { + display: block; + padding: 6px 8px; + text-align: center; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; + font-size: 8px; + line-height: 1; + position: absolute; + bottom: 0; + right: 0; + color: #8f8f8f; +} diff --git a/site/src/theme/DocSidebar/index.js b/site/src/theme/DocSidebar/index.js new file mode 100644 index 000000000..fc49d8b2f --- /dev/null +++ b/site/src/theme/DocSidebar/index.js @@ -0,0 +1,348 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import React, {useState, useCallback, useEffect, useRef, memo} from 'react'; +import clsx from 'clsx'; +import {useThemeConfig, isSamePath} from '@docusaurus/theme-common'; +import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext'; +import useLockBodyScroll from '@theme/hooks/useLockBodyScroll'; +import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize'; +import useScrollPosition from '@theme/hooks/useScrollPosition'; +import Link from '@docusaurus/Link'; +import isInternalUrl from '@docusaurus/isInternalUrl'; +import Logo from '@theme/Logo'; +import IconArrow from '@theme/IconArrow'; +import IconMenu from '@theme/IconMenu'; +import {translate} from '@docusaurus/Translate'; +import styles from './styles.module.css'; +const MOBILE_TOGGLE_SIZE = 24; + +function usePrevious(value) { + const ref = useRef(value); + useEffect(() => { + ref.current = value; + }, [value]); + return ref.current; +} + +const isActiveSidebarItem = (item, activePath) => { + if (item.type === 'link') { + return isSamePath(item.href, activePath); + } + + if (item.type === 'category') { + return item.items.some((subItem) => + isActiveSidebarItem(subItem, activePath), + ); + } + + return false; +}; // Optimize sidebar at each "level" +// TODO this item should probably not receive the "activePath" props +// TODO this triggers whole sidebar re-renders on navigation + +const DocSidebarItems = memo(function DocSidebarItems({items, ...props}) { + return items.map((item, index) => ( + + )); +}); + +function DocSidebarItem(props) { + switch (props.item.type) { + case 'category': + return ; + + case 'link': + default: + return ; + } +} + +function DocSidebarItemCategory({ + item, + onItemClick, + collapsible, + activePath, + ...props +}) { + const {items, label} = item; + const isActive = isActiveSidebarItem(item, activePath); + const wasActive = usePrevious(isActive); // active categories are always initialized as expanded + // the default (item.collapsed) is only used for non-active categories + + const [collapsed, setCollapsed] = useState(() => { + if (!collapsible) { + return false; + } + + return isActive ? false : item.collapsed; + }); + const menuListRef = useRef(null); + const [menuListHeight, setMenuListHeight] = useState(undefined); + + const handleMenuListHeight = (calc = true) => { + setMenuListHeight( + calc ? `${menuListRef.current?.scrollHeight}px` : undefined, + ); + }; // If we navigate to a category, it should automatically expand itself + + useEffect(() => { + const justBecameActive = isActive && !wasActive; + + if (justBecameActive && collapsed) { + setCollapsed(false); + } + }, [isActive, wasActive, collapsed]); + const handleItemClick = useCallback( + (e) => { + e.preventDefault(); + + if (!menuListHeight) { + handleMenuListHeight(); + } + + setTimeout(() => setCollapsed((state) => !state), 100); + }, + [menuListHeight], + ); + + if (items.length === 0) { + return null; + } + + return ( +
  • + + {label} + +
      { + if (!collapsed) { + handleMenuListHeight(false); + } + }}> + +
    +
  • + ); +} + +function DocSidebarItemLink({ + item, + onItemClick, + activePath, + collapsible: _collapsible, + ...props +}) { + const {href, label} = item; + const isActive = isActiveSidebarItem(item, activePath); + return ( +
  • + + {label} + +
  • + ); +} + +function useShowAnnouncementBar() { + const {isAnnouncementBarClosed} = useUserPreferencesContext(); + const [showAnnouncementBar, setShowAnnouncementBar] = useState( + !isAnnouncementBarClosed, + ); + useScrollPosition(({scrollY}) => { + if (!isAnnouncementBarClosed) { + setShowAnnouncementBar(scrollY === 0); + } + }); + return showAnnouncementBar; +} + +function useResponsiveSidebar() { + const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false); + useLockBodyScroll(showResponsiveSidebar); + const windowSize = useWindowSize(); + useEffect(() => { + if (windowSize === windowSizes.desktop) { + setShowResponsiveSidebar(false); + } + }, [windowSize]); + const closeResponsiveSidebar = useCallback( + (e) => { + e.target.blur(); + setShowResponsiveSidebar(false); + }, + [setShowResponsiveSidebar], + ); + const toggleResponsiveSidebar = useCallback(() => { + setShowResponsiveSidebar((value) => !value); + }, [setShowResponsiveSidebar]); + return { + showResponsiveSidebar, + closeResponsiveSidebar, + toggleResponsiveSidebar, + }; +} + +function HideableSidebarButton({onClick}) { + return ( + + ); +} + +function ResponsiveSidebarButton({responsiveSidebarOpened, onClick}) { + return ( + + ); +} + +function DocSidebar({ + path, + sidebar, + sidebarCollapsible = true, + onCollapse, + isHidden, +}) { + const showAnnouncementBar = useShowAnnouncementBar(); + const { + navbar: {hideOnScroll}, + hideableSidebar, + } = useThemeConfig(); + const {isAnnouncementBarClosed} = useUserPreferencesContext(); + const { + showResponsiveSidebar, + closeResponsiveSidebar, + toggleResponsiveSidebar, + } = useResponsiveSidebar(); + return ( +
    + {hideOnScroll && } +
    + +
      + +
    + +
    +
    + +
    + {hideableSidebar && } +
    + ); +} + +export default DocSidebar; diff --git a/site/src/theme/DocSidebar/styles.module.css b/site/src/theme/DocSidebar/styles.module.css new file mode 100644 index 000000000..c9508e40e --- /dev/null +++ b/site/src/theme/DocSidebar/styles.module.css @@ -0,0 +1,138 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +:root { + --collapse-button-bg-color-dark: #2e333a; +} + +@media (min-width: 997px) { + .sidebar { + display: flex; + flex-direction: column; + max-height: 100vh; + height: 100%; + position: sticky; + top: 0; + padding-top: var(--ifm-navbar-height); + width: var(--doc-sidebar-width); + transition: opacity 50ms ease; + } + + .sidebarWithHideableNavbar { + padding-top: 0; + } + + .sidebarHidden { + opacity: 0; + height: 0; + overflow: hidden; + visibility: hidden; + } + + .sidebarLogo { + display: flex !important; + align-items: center; + margin: 0 var(--ifm-navbar-padding-horizontal); + min-height: var(--ifm-navbar-height); + max-height: var(--ifm-navbar-height); + color: inherit !important; + text-decoration: none !important; + } + + .sidebarLogo img { + margin-right: 0.5rem; + height: 2rem; + } + + .menu { + flex-grow: 1; + padding: 0.5rem; + } + + .menuLinkText { + cursor: initial; + } + + .menuLinkText:hover { + background: none; + } + + .menuWithAnnouncementBar { + margin-bottom: var(--docusaurus-announcement-bar-height); + } + + .collapseSidebarButton { + display: block !important; + background-color: var(--ifm-button-background-color); + height: 40px; + position: sticky; + bottom: 0; + border-radius: 0; + border: 1px solid var(--ifm-toc-border-color); + } + + .collapseSidebarButtonIcon { + transform: rotate(180deg); + margin-top: 4px; + } + html[dir='rtl'] .collapseSidebarButtonIcon { + transform: rotate(0); + } + + html[data-theme='dark'] .collapseSidebarButton { + background-color: var(--collapse-button-bg-color-dark); + } + + html[data-theme='dark'] .collapseSidebarButton:hover, + html[data-theme='dark'] .collapseSidebarButton:focus { + background-color: var(--ifm-color-emphasis-200); + } +} + +.sidebarLogo, +.collapseSidebarButton { + display: none; +} + +.sidebarMenuIcon { + vertical-align: middle; +} + +.sidebarMenuCloseIcon { + display: inline-flex; + justify-content: center; + align-items: center; + height: 24px; + font-size: 1.5rem; + font-weight: var(--ifm-font-weight-bold); + line-height: 0.9; + width: 24px; +} + +:global(.menu__list) :global(.menu__list) { + overflow-y: hidden; + will-change: height; + transition: height var(--ifm-transition-fast) linear; +} + +:global(.menu__list-item--collapsed) :global(.menu__list) { + height: 0 !important; +} + +.menuLinkExternal { + align-items: center; +} +.menuLinkExternal:after { + content: ''; + height: 1.15rem; + width: 1.15rem; + min-width: 1.15rem; + margin: 0 0 0 3%; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(0,0,0,0.5)' d='M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z'/%3E%3C/svg%3E") + no-repeat; + filter: var(--ifm-menu-link-sublist-icon-filter); +}