Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/nav #4536

Draft
wants to merge 22 commits into
base: feat/new-admin-ui
Choose a base branch
from
5 changes: 5 additions & 0 deletions packages/admin-ui/.storybook/overrides.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* storybook-overrides.css */
.sb-show-main.sb-main-centered #storybook-root {
margin: 0;
padding: 0;
}
1 change: 1 addition & 0 deletions packages/admin-ui/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Preview } from "@storybook/react";

import "../src/theme.scss";
import "./overrides.css";

const preview: Preview = {
parameters: {
Expand Down
1 change: 1 addition & 0 deletions packages/admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-collapsible": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",
"@radix-ui/react-label": "^2.1.0",
Expand Down
9 changes: 9 additions & 0 deletions packages/admin-ui/src/Collapsible/Collapsible.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";

const Collapsible = CollapsiblePrimitive.Root;

const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;

const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;

export { Collapsible, CollapsibleTrigger, CollapsibleContent };
1 change: 1 addition & 0 deletions packages/admin-ui/src/Collapsible/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Collapsible";
112 changes: 112 additions & 0 deletions packages/admin-ui/src/Sidebar/Sidebar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Sidebar } from "./Sidebar";
import React from "react";
import { ReactComponent as CreditCard } from "@material-design-icons/svg/outlined/credit_score.svg";
import { ReactComponent as Settings } from "@material-design-icons/svg/outlined/settings.svg";
import { ReactComponent as AuditLogsIcon } from "@material-design-icons/svg/outlined/assignment.svg";
import { ReactComponent as FormBuilderIcon } from "@material-design-icons/svg/outlined/check_box.svg";
import { ReactComponent as CmsIcon } from "@material-design-icons/svg/outlined/web.svg";
import { ReactComponent as PageBuilderIcon } from "@material-design-icons/svg/outlined/table_chart.svg";
import { ReactComponent as ApwIcon } from "@material-design-icons/svg/outlined/account_tree.svg";
import { ReactComponent as TenantManagerIcon } from "@material-design-icons/svg/outlined/domain.svg";
import { ReactComponent as SettingsIcon } from "@material-design-icons/svg/outlined/settings.svg";
import { ReactComponent as ArticleIcon } from "@material-design-icons/svg/outlined/article.svg";

import wbyLogo from "./stories/wby-logo.png";
import { SidebarProvider } from "~/Sidebar/components/SidebarProvider";

const meta: Meta<typeof Sidebar> = {
title: "Components/Sidebar",
component: Sidebar,
tags: ["autodocs"],
argTypes: {},
render: args => (
<>
<SidebarProvider>
<Sidebar {...args} collapsible={"icon"} />
<main className={"wby-bg-white wby-p-8"}>Main content goes here.</main>
</SidebarProvider>
</>
)
};

export default meta;

type Story = StoryObj<typeof Sidebar>;

export const Default: Story = {
args: {
title: "Webiny",
icon: <Sidebar.Icon element={<img src={wbyLogo} alt={"Webiny"} />} label={"Webiny"} />,
children: (
<>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Audit Logs" element={<AuditLogsIcon />} />}
content={"Audit Logs"}
/>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Form Builder" element={<FormBuilderIcon />} />}
content={"Form Builder"}
/>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Headless CMS" element={<CmsIcon />} />}
content={"Headless CMS"}
>
<Sidebar.Item content={"Content Models"}>
<Sidebar.Item content={"Groups"} />
<Sidebar.Item content={"Models"} />
</Sidebar.Item>
</Sidebar.Item>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Page Builder" element={<PageBuilderIcon />} />}
content={"Page Builder"}
>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Blocks" element={<CreditCard />} />}
content={"Blocks"}
>
<Sidebar.Item content={"Blocks"} />
<Sidebar.Item content={"Categories"} />
</Sidebar.Item>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Pages" element={<Settings />} />}
content={"Pages"}
>
<Sidebar.Item content={"Categories"} />
<Sidebar.Item content={"Menus"} />
<Sidebar.Item content={"Pages"} />
<Sidebar.Item content={"Templates"} />
</Sidebar.Item>
</Sidebar.Item>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Publishing Workflows" element={<ApwIcon />} />}
content={"Publishing Workflows"}
>
<Sidebar.Item
content={"Content Reviews"}
icon={
<Sidebar.Item.Icon
element={<ArticleIcon />}
label={"Content Reviews"}
/>
}
/>
<Sidebar.Item
content={"Workflows"}
icon={<Sidebar.Item.Icon element={<ArticleIcon />} label={"Workflows"} />}
/>
</Sidebar.Item>
<Sidebar.Item
icon={
<Sidebar.Item.Icon label="Tenant manager" element={<TenantManagerIcon />} />
}
content={"Tenant manager"}
/>
<Sidebar.Item
icon={<Sidebar.Item.Icon label="Settings" element={<SettingsIcon />} />}
content={"Settings"}
/>
</>
)
}
};
71 changes: 71 additions & 0 deletions packages/admin-ui/src/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as React from "react";
import { makeDecoratable, withStaticProps } from "~/utils";
import { SidebarRoot } from "./components/SidebarRoot";
import { SidebarContent } from "./components/SidebarContent";
import { SidebarSeparator } from "./components/SidebarSeparator";
import { SidebarMenuItem } from "./components/SidebarMenuItem";
import { SidebarGroup } from "./components/SidebarGroup";
import { SidebarMenu } from "./components/SidebarMenu";
import { SidebarHeader } from "./components/SidebarHeader";
import { SidebarIcon } from "./components/SidebarIcon";

interface SidebarProps
extends Omit<React.ComponentPropsWithoutRef<typeof SidebarRoot>, "title">,
Omit<React.ComponentPropsWithoutRef<typeof SidebarContent>, "title"> {
title?: React.ReactNode;
icon?: React.ReactNode;
children: React.ReactNode;
}

const SidebarBase = React.forwardRef<React.ElementRef<typeof SidebarRoot>, SidebarProps>(
(props, ref) => {
const { headerProps, rootProps, contentProps } = React.useMemo(() => {
const {
// Header props.
title,
icon,

// Root props.
side,
collapsible,

// Content props.
...rest
} = props;

return {
headerProps: {
title,
icon
},
rootProps: {
side,
collapsible
},
contentProps: rest
};
}, [props]);

return (
<SidebarRoot {...rootProps}>
<SidebarHeader {...headerProps} />
<SidebarContent {...contentProps} ref={ref}>
<SidebarMenu>{props.children}</SidebarMenu>
</SidebarContent>
</SidebarRoot>
);
}
);

SidebarBase.displayName = "Sidebar";

const DecoratableSidebar = makeDecoratable("Sidebar", SidebarBase);

const Sidebar = withStaticProps(DecoratableSidebar, {
Separator: SidebarSeparator,
Group: SidebarGroup,
Item: SidebarMenuItem,
Icon: SidebarIcon
});

export { Sidebar };
21 changes: 21 additions & 0 deletions packages/admin-ui/src/Sidebar/components/SidebarContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import { cn } from "~/utils";

const SidebarContent = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
({ className, ...props }, ref) => {
return (
<div
ref={ref}
data-sidebar="content"
className={cn(
"wby-flex wby-text-neutral-primary wby-min-h-0 wby-flex-1 wby-flex-col wby-gap-2 wby-overflow-auto group-data-[collapsible=icon]:wby-overflow-hidden",
className
)}
{...props}
/>
);
}
);
SidebarContent.displayName = "SidebarContent";

export { SidebarContent };
16 changes: 16 additions & 0 deletions packages/admin-ui/src/Sidebar/components/SidebarFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";
import { cn } from "~/utils";

const SidebarFooter = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
({ className, ...props }, ref) => {
return (
<div
ref={ref}
data-sidebar="footer"
className={cn("flex flex-col gap-2 p-2", className)}
{...props}
/>
);
}
);
SidebarFooter.displayName = "SidebarFooter";
18 changes: 18 additions & 0 deletions packages/admin-ui/src/Sidebar/components/SidebarGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { cn } from "~/utils";

const SidebarGroup = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
({ className, ...props }, ref) => {
return (
<div
ref={ref}
data-sidebar="group"
className={cn("wby-relative wby-flex wby-w-full wby-min-w-0 wby-flex-col wby-p-2", className)}
{...props}
/>
);
}
);
SidebarGroup.displayName = "SidebarGroup";

export { SidebarGroup };
26 changes: 26 additions & 0 deletions packages/admin-ui/src/Sidebar/components/SidebarGroupAction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cn } from "~/utils";

const SidebarGroupAction = React.forwardRef<
HTMLButtonElement,
React.ComponentProps<"button"> & { asChild?: boolean }
>(({ className, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";

return (
<Comp
ref={ref}
data-sidebar="group-action"
className={cn(
"absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 after:md:hidden",
"group-data-[collapsible=icon]:hidden",
className
)}
{...props}
/>
);
});
SidebarGroupAction.displayName = "SidebarGroupAction";
14 changes: 14 additions & 0 deletions packages/admin-ui/src/Sidebar/components/SidebarGroupContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import { cn } from "~/utils";

const SidebarGroupContent = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
({ className, ...props }, ref) => (
<div
ref={ref}
data-sidebar="group-content"
className={cn("w-full text-sm", className)}
{...props}
/>
)
);
SidebarGroupContent.displayName = "SidebarGroupContent";
24 changes: 24 additions & 0 deletions packages/admin-ui/src/Sidebar/components/SidebarGroupLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cn } from "~/utils";

const SidebarGroupLabel = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & { asChild?: boolean }
>(({ className, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "div";

return (
<Comp
ref={ref}
data-sidebar="group-label"
className={cn(
"duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
className
)}
{...props}
/>
);
});
SidebarGroupLabel.displayName = "SidebarGroupLabel";
47 changes: 47 additions & 0 deletions packages/admin-ui/src/Sidebar/components/SidebarHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from "react";
import { Separator } from "~/Separator";
import { IconButton } from "~/Button";
import { useSidebar } from "./SidebarProvider";
import { ReactComponent as ToggleSidebarIcon } from "@material-design-icons/svg/outlined/chrome_reader_mode.svg";

interface SidebarHeaderProps extends Omit<React.ComponentProps<"div">, "title"> {
icon?: React.ReactNode;
title?: React.ReactNode;
}

const SidebarHeader = ({ title, icon }: SidebarHeaderProps) => {
const { toggleSidebar } = useSidebar();

return (
<div>
<div className={"wby-px-xs-plus wby-transition-[width,height,padding]"}>
<div
data-sidebar="header"
className={
"wby-flex wby-justify-between wby-items-center wby-gap-sm wby-py-xs-plus wby-px-xs wby-overflow-x-hidden"
}
>
<div className={"wby-flex wby-items-center wby-gap-x-sm"}>
{icon}
<span className={"wby-text-md wby-font-semibold wby-truncate"}>
{title}
</span>
</div>

<IconButton
icon={<ToggleSidebarIcon />}
data-sidebar="trigger"
size="xs"
variant={"ghost"}
onClick={toggleSidebar}
/>
</div>
</div>
<div className={"wby-px-sm wby-py-xs"}>
<Separator variant={"subtle"} margin={"xs"} />
</div>
</div>
);
};

export { SidebarHeader, type SidebarHeaderProps };
Loading
Loading