From fee490a6ada95f070cc791b37762438b898006a9 Mon Sep 17 00:00:00 2001 From: Bikash Date: Mon, 4 Dec 2023 11:05:23 +0530 Subject: [PATCH 1/9] Changed toast position --- lib/app-setup/root.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/app-setup/root.tsx b/lib/app-setup/root.tsx index b32b9d649..4a44c5036 100644 --- a/lib/app-setup/root.tsx +++ b/lib/app-setup/root.tsx @@ -191,22 +191,15 @@ ${URL_SUFFIX ? `window.URL_SUFFIX = ${`'${URL_SUFFIX}'`}` : ''} }} /> - {/* */} - - // 'rounded border-border-tertiary border bg-surface-tertiary-default flex flex-row items-center p-xl bodyMd-medium text-text-on-primary' - // } - /> + - {/* */} - From b263827ddc1459d8369e83a7aee476a1be0ff829 Mon Sep 17 00:00:00 2001 From: Bikash Date: Mon, 4 Dec 2023 11:06:00 +0530 Subject: [PATCH 2/9] WIP CR --- .../routes/_.$account.container-registry.repos/route.tsx | 1 - .../_.$account.repo_.$repo.buildcaches/handle-build-cache.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/apps/console/routes/_.$account.container-registry.repos/route.tsx b/src/apps/console/routes/_.$account.container-registry.repos/route.tsx index e18ece0a3..7565c256d 100644 --- a/src/apps/console/routes/_.$account.container-registry.repos/route.tsx +++ b/src/apps/console/routes/_.$account.container-registry.repos/route.tsx @@ -39,7 +39,6 @@ export const loader = async (ctx: IRemixCtx) => { const ContainerRegistryRepos = () => { const [visible, setVisible] = useState(false); const { promise } = useLoaderData(); - return ( <> { throw e[0]; } } - resetValues(); + // resetValues(); toast.success( `Build cache ${isUpdate ? 'updated' : 'created'} successfully` ); From a5a29205736cafe26120f12dd26403a8706b2636 Mon Sep 17 00:00:00 2001 From: Bikash Date: Mon, 4 Dec 2023 11:06:23 +0530 Subject: [PATCH 3/9] WIP dev doc --- src/apps/devdoc/components/container.tsx | 7 +- src/apps/devdoc/components/toc.tsx | 33 +++++--- src/apps/devdoc/layout/mdx-components.tsx | 2 +- src/apps/devdoc/layout/theme.tsx | 12 ++- src/apps/devdoc/next.config.js | 8 +- src/apps/devdoc/package.json | 2 +- src/apps/devdoc/pages/index.mdx | 50 +++--------- src/apps/devdoc/pnpm-lock.yaml | 98 +++++++++++------------ src/apps/devdoc/style.css | 14 +++- 9 files changed, 110 insertions(+), 116 deletions(-) diff --git a/src/apps/devdoc/components/container.tsx b/src/apps/devdoc/components/container.tsx index 500232487..379c0c46b 100644 --- a/src/apps/devdoc/components/container.tsx +++ b/src/apps/devdoc/components/container.tsx @@ -4,14 +4,19 @@ import { cn } from '~/utiltities/commons'; const Container = ({ children, className, + layout, }: { children: ReactNode; className?: string; + layout?: 'default' | 'full' | 'raw'; }) => { + console.log(layout); + return (
diff --git a/src/apps/devdoc/components/toc.tsx b/src/apps/devdoc/components/toc.tsx index 880f0af75..d0931c2ee 100644 --- a/src/apps/devdoc/components/toc.tsx +++ b/src/apps/devdoc/components/toc.tsx @@ -40,18 +40,27 @@ export function TOC({ headings }: TOCProps): ReactElement { ); if (anchor) { - scrollIntoView(anchor, { - behavior: 'smooth', - block: 'center', - inline: 'center', - scrollMode: 'always', - boundary: tocRef.current, - }); + console.log(activeSlug, anchor); + setTimeout(() => { + scrollIntoView(anchor, { + behavior: 'smooth', + block: 'center', + inline: 'center', + scrollMode: 'always', + boundary: tocRef.current, + }); + }, 100); + // anchor.scrollIntoView({ block: '' }); } }, [activeSlug]); return ( -
+
{hasHeadings && ( <> @@ -92,7 +101,11 @@ export function TOC({ headings }: TOCProps): ReactElement { )} -
+
{/* {renderComponent(config.editLink.component, { filePath, className: linkClassName, @@ -100,7 +113,7 @@ export function TOC({ headings }: TOCProps): ReactElement { })} {renderComponent(config.toc.extraContent)} */} -
+
{config.feedback ? ( +); + +export default MenuToggle; diff --git a/src/apps/devdoc/app/components/nav-footer.tsx b/src/apps/devdoc/app/components/nav-footer.tsx new file mode 100644 index 000000000..a204d80b7 --- /dev/null +++ b/src/apps/devdoc/app/components/nav-footer.tsx @@ -0,0 +1,12 @@ +import { ReactNode } from 'react'; + +const NavFooter = ({ children }: { children: ReactNode }) => { + return ( +
+
+ {children} +
+ ); +}; + +export default NavFooter; diff --git a/src/apps/devdoc/app/components/nav-links.tsx b/src/apps/devdoc/app/components/nav-links.tsx new file mode 100644 index 000000000..c00e54159 --- /dev/null +++ b/src/apps/devdoc/app/components/nav-links.tsx @@ -0,0 +1,61 @@ +import { ChevronLeft, ChevronRight } from '@jengaicons/react'; +import { Button } from 'kl-design-system/atoms/button'; +import Link from 'next/link'; +import type { Item } from 'nextra/normalize-pages'; +import type { ReactElement } from 'react'; +import { cn } from '~/utiltities/commons'; + +interface NavLinkProps { + currentIndex: number; + flatDirectories: Item[]; +} + +export const NavLinks = ({ + flatDirectories, + currentIndex, +}: NavLinkProps): ReactElement | null => { + const nav = true; + const navigation = { prev: nav, next: nav }; + let prev = navigation.prev && flatDirectories[currentIndex - 1]; + let next = navigation.next && flatDirectories[currentIndex + 1]; + + if (prev && !prev.isUnderCurrentDocsTree) prev = false; + if (next && !next.isUnderCurrentDocsTree) next = false; + + if (!prev && !next) return null; + + return ( +
+ {prev && ( +
+ ); +}; diff --git a/src/apps/devdoc/app/components/progress-tracker.tsx b/src/apps/devdoc/app/components/progress-tracker.tsx new file mode 100644 index 000000000..6d5a3f0a2 --- /dev/null +++ b/src/apps/devdoc/app/components/progress-tracker.tsx @@ -0,0 +1,28 @@ +import { Key, ReactNode } from "react" +import { cn } from "../utils/commons" + +const ProgressCircle = ()=>{ + return + + + } + +const ProgressTracker = ({items}:{items:{render:()=>ReactNode, id:Key}[]})=>{ + return
+ {items.map((item, index)=> + ( +
+
+ +
+
+
+ {item.render()} +
+
+ ) + )} +
+} + +export default ProgressTracker \ No newline at end of file diff --git a/src/apps/devdoc/app/components/search.tsx b/src/apps/devdoc/app/components/search.tsx new file mode 100644 index 000000000..5d98ddebd --- /dev/null +++ b/src/apps/devdoc/app/components/search.tsx @@ -0,0 +1,34 @@ +import { Search } from '@jengaicons/react'; +import { IconButton } from 'kl-design-system/atoms/button'; +import { cn } from '~/utiltities/commons'; +import useSearch from '~/utiltities/use-search'; + +const SearchBox = ({ className }: { className?: string }) => { + const { setShow } = useSearch(); + return ( +
+ +
+ } + variant="plain" + onClick={() => { + setShow(true); + }} + /> +
+
+ ); +}; + +export default SearchBox; diff --git a/src/apps/devdoc/app/components/sidebar.tsx b/src/apps/devdoc/app/components/sidebar.tsx new file mode 100644 index 000000000..2af1bcbe6 --- /dev/null +++ b/src/apps/devdoc/app/components/sidebar.tsx @@ -0,0 +1,432 @@ +/* eslint-disable react/jsx-no-constructed-context-values */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-unused-expressions */ +/* eslint-disable no-nested-ternary */ +import { useRouter } from 'next/router'; +import type { Heading } from 'nextra'; +import { useFSRoute, useMounted } from 'nextra/hooks'; +import { LayoutGroup, motion } from 'framer-motion'; + +import type { Item, MenuItem, PageItem } from 'nextra/normalize-pages'; +import type { ReactElement } from 'react'; +import { + createContext, + memo, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import scrollIntoView from 'scroll-into-view-if-needed'; +import { ChevronRight } from '@jengaicons/react'; +import { cn } from '~/utiltities/commons'; +import useMenu from '~/utiltities/use-menu'; +import { Collapse } from './collapse'; +import { Anchor } from './anchor'; +import Search from './search'; + +const TreeState: Record = Object.create(null); + +const FocusedItemContext = createContext(null); +const OnFocusItemContext = createContext any)>( + null +); +const FolderLevelContext = createContext(0); + +type FolderProps = { + item: PageItem | MenuItem | Item; + anchors: Heading[]; +}; + +const classes = { + link: cn( + 'flex flex-row items-center rounded py-lg px-2xl transition-all [word-break:break-word]', + 'cursor-pointer [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none] contrast-more:border' + ), + inactive: cn('bodyMd text-text-soft'), + active: cn('bodyMd-medium text-text-primary bg-surface-basic-active'), + list: cn('flex flex-col w-full gap-lg'), +}; + +function FolderImpl({ item, anchors }: FolderProps): ReactElement { + const routeOriginal = useFSRoute(); + const [route] = routeOriginal.split('#'); + const active = [route, `${route}/`].includes(`${item.route}/`); + const activeRouteInside = active || route.startsWith(`${item.route}/`); + + const focusedRoute = useContext(FocusedItemContext); + const focusedRouteInside = !!focusedRoute?.startsWith(`${item.route}/`); + const level = useContext(FolderLevelContext); + + const { setState: setMenu } = useMenu(); + const { theme } = item as Item; + const open = + TreeState[item.route] === undefined + ? active || + activeRouteInside || + focusedRouteInside || + (theme && 'collapsed' in theme ? !theme.collapsed : level < 1) + : TreeState[item.route] || focusedRouteInside; + + const rerender = useState({})[1]; + const sideBarAutoCollapse = false; + useEffect(() => { + const updateTreeState = () => { + if (activeRouteInside || focusedRouteInside) { + TreeState[item.route] = true; + } + }; + const updateAndPruneTreeState = () => { + if (activeRouteInside && focusedRouteInside) { + TreeState[item.route] = true; + } else { + delete TreeState[item.route]; + } + }; + + sideBarAutoCollapse ? updateAndPruneTreeState() : updateTreeState(); + }, [activeRouteInside, focusedRouteInside, item.route, sideBarAutoCollapse]); + + if (item.type === 'menu') { + const menu = item as MenuItem; + const routes = Object.fromEntries( + (menu.children || []).map((route) => [route.name, route]) + ); + item.children = Object.entries(menu.items || {}).map(([key, item]) => { + const route = routes[key] || { + name: key, + ...('locale' in menu && { locale: menu.locale }), + route: `${menu.route}/${key}`, + }; + return { + ...route, + ...item, + }; + }); + } + + const isLink = 'withIndexPage' in item && item.withIndexPage; + // use button when link don't have href because it impacts on SEO + const ComponentToUse = isLink ? Anchor : 'button'; + + return ( +
  • + { + const clickedToggleIcon = ['svg', 'path'].includes( + (e.target as HTMLElement).tagName.toLowerCase() + ); + if (clickedToggleIcon) { + e.preventDefault(); + } + if (isLink) { + // If it's focused, we toggle it. Otherwise, always open it. + if (active || clickedToggleIcon) { + TreeState[item.route] = !open; + } else { + TreeState[item.route] = true; + setMenu(false); + } + rerender({}); + return; + } + if (active) return; + TreeState[item.route] = !open; + rerender({}); + }} + > + {item.title} + + + +
    + + {Array.isArray(item.children) ? ( + + ) : null} + + +
  • + ); +} + +const Folder = memo(function FolderInner(props: FolderProps) { + const level = useContext(FolderLevelContext); + return ( + + + + ); +}); + +function Separator({ title }: { title: string }): ReactElement { + return ( +
  • + {title ||
    } +
  • + ); +} + +function File({ + base, + item, +}: { + base: string | undefined; + item: PageItem | Item; +}): ReactElement { + const route = useFSRoute(); + const onFocus = useContext(OnFocusItemContext); + + // It is possible that the item doesn't have any route - for example an external link. + const active = item.route && [route, `${route}/`].includes(`${item.route}/`); + const { setState: setMenu } = useMenu(); + + if (item.type === 'separator') { + return ; + } + + return ( +
  • + {!!base && active && ( + + )} + { + setMenu(false); + }} + onFocus={() => { + onFocus?.(item.route); + }} + onBlur={() => { + onFocus?.(null); + }} + > + {item.title} + + {/* {active && anchors.length > 0 && ( + + )} */} +
  • + ); +} + +interface MenuProps { + directories: PageItem[] | Item[]; + anchors: Heading[]; + base?: string; + className?: string; + onlyCurrentDocs?: boolean; +} + +interface SideBarProps { + docsDirectories: PageItem[]; + fullDirectories: Item[]; + asPopover?: boolean; + headings: Heading[]; + includePlaceholder: boolean; +} + +export function Sidebar({ + docsDirectories, + fullDirectories, + asPopover = false, + headings, + includePlaceholder, +}: SideBarProps): ReactElement { + const { state: menu, setState: setMenu } = useMenu(); + const router = useRouter(); + const [focused, setFocused] = useState(null); + const [showSidebar, _setSidebar] = useState(true); + + const anchors = useMemo( + () => headings.filter((v) => v.depth === 2), + [headings] + ); + const sidebarRef = useRef(null); + const containerRef = useRef(null); + const mounted = useMounted(); + useEffect(() => { + if (menu) { + document.body.classList.add('overflow-hidden', 'md:overflow-auto'); + } else { + document.body.classList.remove('overflow-hidden', 'md:overflow-auto'); + } + }, [menu]); + + useEffect(() => { + const activeElement = sidebarRef.current?.querySelector('li.active'); + + if (activeElement && (window.innerWidth > 767 || menu)) { + const scroll = () => { + scrollIntoView(activeElement, { + block: 'center', + inline: 'center', + scrollMode: 'always', + boundary: containerRef.current, + }); + }; + if (menu) { + // needs for mobile since menu has transition transform + setTimeout(scroll, 300); + } else { + scroll(); + } + } + }, [menu]); + + // Always close mobile nav when route was changed (e.g. logo click) + useEffect(() => { + setMenu(false); + }, [router.asPath, setMenu]); + + return ( + <> + {includePlaceholder && asPopover ? ( +
    + ) : null} +
    setMenu(false)} + /> + + + ); +} + +function Menu({ + base, + directories, + anchors, + className, + onlyCurrentDocs, +}: MenuProps): ReactElement { + return ( +
      + {directories.map((item) => + !onlyCurrentDocs || item.isUnderCurrentDocsTree ? ( + item.type === 'menu' || + (item.children && (item.children.length || !item.withIndexPage)) ? ( + + ) : ( + + ) + ) : null + )} +
    + ); +} diff --git a/src/apps/devdoc/app/components/steps.tsx b/src/apps/devdoc/app/components/steps.tsx new file mode 100644 index 000000000..f6bc77c2c --- /dev/null +++ b/src/apps/devdoc/app/components/steps.tsx @@ -0,0 +1,10 @@ +import { ReactNode } from 'react'; + +interface IStep { + children?: ReactNode; +} +const Steps = ({ children }: IStep) => { + return
    {children}
    ; +}; + +export default Steps; diff --git a/src/apps/devdoc/app/components/toc.tsx b/src/apps/devdoc/app/components/toc.tsx new file mode 100644 index 000000000..7c2e3d8b5 --- /dev/null +++ b/src/apps/devdoc/app/components/toc.tsx @@ -0,0 +1,143 @@ +import type { Heading } from 'nextra'; +import type { ReactElement } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; +import scrollIntoView from 'scroll-into-view-if-needed'; + +import { Button } from 'kl-design-system/atoms/button'; +import { ArrowSquareOut } from '@jengaicons/react'; +import Link from 'next/link'; +import { LayoutGroup, motion } from 'framer-motion'; +import { useActiveAnchor } from '~/utiltities/active-anchor'; +import { cn } from '~/utiltities/commons'; +import useConfig from '~/utiltities/use-config'; +import getGitIssueUrl from '~/utiltities/get-git-issue-url'; +import { BackToTop } from './back-to-top'; + +export type TOCProps = { + headings: Heading[]; +}; + +export function TOC({ headings }: TOCProps): ReactElement { + const { config } = useConfig(); + const activeAnchor = useActiveAnchor(); + const tocRef = useRef(null); + + const items = useMemo( + () => headings.filter((heading) => heading.depth > 1), + [headings] + ); + + const hasHeadings = items.length > 0; + + const activeSlug = Object.entries(activeAnchor).find( + ([, { isActive }]) => isActive + )?.[0]; + + useEffect(() => { + if (!activeSlug) return; + const anchor = tocRef.current?.querySelector( + `li > a[href="#${activeSlug}"]` + ); + + if (anchor) { + console.log(activeSlug, anchor); + setTimeout(() => { + scrollIntoView(anchor, { + behavior: 'smooth', + block: 'center', + inline: 'center', + scrollMode: 'always', + boundary: tocRef.current, + }); + }, 100); + // anchor.scrollIntoView({ block: '' }); + } + }, [activeSlug]); + + return ( +
    + {hasHeadings && ( + <> + + + + + )} + +
    + {/* {renderComponent(config.editLink.component, { + filePath, + className: linkClassName, + children: renderComponent(config.editLink.text), + })} + + {renderComponent(config.toc.extraContent)} */} +
    + {config.feedback ? ( +
    +
    + ); +} diff --git a/src/apps/devdoc/app/icons/devops.tsx b/src/apps/devdoc/app/icons/devops.tsx new file mode 100644 index 000000000..7e8a3fad4 --- /dev/null +++ b/src/apps/devdoc/app/icons/devops.tsx @@ -0,0 +1,87 @@ +import { ComponentProps } from 'react'; + +const DevopsIcon = (props: ComponentProps<'svg'>) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default DevopsIcon; diff --git a/src/apps/devdoc/app/icons/distribution.tsx b/src/apps/devdoc/app/icons/distribution.tsx new file mode 100644 index 000000000..3e041c772 --- /dev/null +++ b/src/apps/devdoc/app/icons/distribution.tsx @@ -0,0 +1,34 @@ +import { ComponentProps } from 'react'; + +const DistributionIcon = (props: ComponentProps<'svg'>) => ( + + + + + + + +); +export default DistributionIcon; diff --git a/src/apps/devdoc/app/icons/index.tsx b/src/apps/devdoc/app/icons/index.tsx new file mode 100644 index 000000000..276ee28e1 --- /dev/null +++ b/src/apps/devdoc/app/icons/index.tsx @@ -0,0 +1,11 @@ +import DevopsIcon from './devops'; +import InfraopsIcon from './infraops'; +import DistributionIcon from './distribution'; + +const Icons = { + DevopsIcon, + InfraopsIcon, + DistributionIcon, +}; + +export default Icons; diff --git a/src/apps/devdoc/app/icons/infraops.tsx b/src/apps/devdoc/app/icons/infraops.tsx new file mode 100644 index 000000000..7aac9051a --- /dev/null +++ b/src/apps/devdoc/app/icons/infraops.tsx @@ -0,0 +1,116 @@ +import { ComponentProps } from 'react'; + +const InfraopsIcon = (props: ComponentProps<'svg'>) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); +export default InfraopsIcon; diff --git a/src/apps/devdoc/app/layout/mdx-components.tsx b/src/apps/devdoc/app/layout/mdx-components.tsx new file mode 100644 index 000000000..cd6f1416f --- /dev/null +++ b/src/apps/devdoc/app/layout/mdx-components.tsx @@ -0,0 +1,126 @@ +/* eslint-disable consistent-return */ +/* eslint-disable jsx-a11y/heading-has-content */ + +import { Table, Td, Th, Tr } from 'nextra/components'; +import type { Components } from 'nextra/mdx'; +import type { ComponentProps, ReactElement } from 'react'; +import { useEffect, useRef } from 'react'; +import { Anchor, AnchorProps } from '~/components/anchor'; +import { + useIntersectionObserver, + useSetActiveAnchor, + useSlugs, +} from '~/utiltities/active-anchor'; +import { cn } from '~/utiltities/commons'; + +// Anchor links +function HeadingLink({ + tag: Tag, + context, + children, + id, + className, + ...props +}: ComponentProps<'h2'> & { + tag: `h${2 | 3 | 4 | 5 | 6}`; + context: { index: number }; +}): ReactElement { + const setActiveAnchor = useSetActiveAnchor(); + const slugs = useSlugs(); + const observer = useIntersectionObserver(); + const obRef = useRef(null); + + useEffect(() => { + if (!id) return; + const heading = obRef.current; + if (!heading) return; + slugs.set(heading, [id, (context.index += 1)]); + observer?.observe(heading); + + return () => { + observer?.disconnect(); + slugs.delete(heading); + setActiveAnchor((f) => { + const ret = { ...f }; + delete ret[id]; + return ret; + }); + }; + }, [id, context, slugs, observer, setActiveAnchor]); + + return ( + + {children} + {id && ( + + )} + + ); +} + +const EXTERNAL_HREF_REGEX = /https?:\/\//; + +export const Link = ({ href = '', className, ...props }: AnchorProps) => ( + +); + +const A = ({ href = '', ...props }) => ( + +); + +export const createComponents = ({components, isRawLayout}:{components?:any, isRawLayout?:boolean}): Components => { + if (isRawLayout) { + return { a: A }; + } + + const context = { index: 0 }; + return { + h1: (props) => ( +

    + ), + h2: (props) => , + h3: (props) => , + h4: (props) => , + h5: (props) => , + h6: (props) => , + ul: (props) =>