diff --git a/web/panda.config.ts b/web/panda.config.ts index 2b28d42de..c261cd12d 100644 --- a/web/panda.config.ts +++ b/web/panda.config.ts @@ -14,6 +14,7 @@ import { input } from "src/theme/components/Input/input.recipe"; import { link } from "src/theme/components/Link/link.recipe"; import { menu } from "src/theme/components/Menu/menu.recipe"; import { skeleton } from "src/theme/components/Skeleton/skeleton.recipe"; +import { tabs } from "src/theme/components/Tabs/tabs.recipe"; import { titleInput } from "src/theme/components/TitleInput/titleInput.recipe"; // TODO: Dark mode = 40% @@ -69,6 +70,7 @@ export default defineConfig({ button: button, link: link, menu: menu, + tabs: tabs, checkbox: checkbox, skeleton: skeleton, }, diff --git a/web/src/screens/profile/components/Content/Content.tsx b/web/src/screens/profile/components/Content/Content.tsx index 74323f287..d3e233852 100644 --- a/web/src/screens/profile/components/Content/Content.tsx +++ b/web/src/screens/profile/components/Content/Content.tsx @@ -1,8 +1,13 @@ -import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; - import { PublicProfile } from "src/api/openapi/schemas"; import { MixedPostList } from "src/components/feed/mixed/MixedPostList"; import { Unready } from "src/components/site/Unready"; +import { + Tabs, + TabsContent, + TabsIndicator, + TabsList, + TabsTrigger, +} from "src/theme/components/Tabs"; import { CollectionList } from "../CollectionList/CollectionList"; import { PostList } from "../PostList/PostList"; @@ -18,30 +23,32 @@ export function Content(props: PublicProfile) { return ( - - - Posts - Replies - Collections - - - - - - - - - - - - - - - - + + + Posts + Replies + Collections + + + + + + + + + + + + + + + + + + ); diff --git a/web/src/theme/components/Tabs/index.ts b/web/src/theme/components/Tabs/index.ts new file mode 100644 index 000000000..587caec3a --- /dev/null +++ b/web/src/theme/components/Tabs/index.ts @@ -0,0 +1,32 @@ +import { Tabs as ArkTabs } from "@ark-ui/react/tabs"; +import { styled } from "styled-system/jsx"; +import { tabs } from "styled-system/recipes"; + +import { createStyleContext } from "src/theme/create-style-context"; + +const { withProvider, withContext } = createStyleContext(tabs); + +const Tabs = withProvider(styled(ArkTabs.Root), "root"); +const TabsContent = withContext(styled(ArkTabs.Content), "content"); +const TabsIndicator = withContext(styled(ArkTabs.Indicator), "indicator"); +const TabsList = withContext(styled(ArkTabs.List), "list"); +const TabsTrigger = withContext(styled(ArkTabs.Trigger), "trigger"); + +const Root = Tabs; +const Content = TabsContent; +const Indicator = TabsIndicator; +const List = TabsList; +const Trigger = TabsTrigger; + +export { + Content, + Indicator, + List, + Root, + Tabs, + TabsContent, + TabsIndicator, + TabsList, + TabsTrigger, + Trigger, +}; diff --git a/web/src/theme/components/Tabs/tabs.recipe.ts b/web/src/theme/components/Tabs/tabs.recipe.ts new file mode 100644 index 000000000..349b20763 --- /dev/null +++ b/web/src/theme/components/Tabs/tabs.recipe.ts @@ -0,0 +1,261 @@ +import { tabsAnatomy } from "@ark-ui/react"; +import { defineSlotRecipe } from "@pandacss/dev"; + +export const tabs = defineSlotRecipe({ + className: "tabs", + slots: tabsAnatomy.keys(), + base: { + root: { + display: "flex", + width: "full", + _horizontal: { + flexDirection: "column", + }, + _vertical: { + flexDirection: "row", + }, + }, + list: { + display: "flex", + flexShrink: "0", + _horizontal: { + flexDirection: "row", + }, + _vertical: { + flexDirection: "column", + }, + overflow: "auto", + position: "relative", + scrollbarWidth: "none", + "&::-webkit-scrollbar": { + display: "none", + }, + }, + trigger: { + alignItems: "center", + color: "fg.muted", + cursor: "pointer", + display: "inline-flex", + flexShrink: "0", + fontWeight: "semibold", + gap: "2", + justifyContent: "center", + transitionDuration: "normal", + transitionProperty: "color, background, border-color", + transitionTimingFunction: "default", + whiteSpace: "nowrap", + _disabled: { + color: "fg.disabled", + cursor: "not-allowed", + _hover: { + color: "fg.disabled", + }, + }, + _hover: { + color: "fg.muted", + }, + _selected: { + color: "fg.default", + _hover: { + color: "fg.default", + }, + }, + _vertical: { + justifyContent: "start", + }, + }, + }, + defaultVariants: { + size: "md", + }, + variants: { + variant: { + line: { + list: { + _horizontal: { + boxShadow: "0 -1px 0 0 inset var(--colors-border-default)", + gap: "4", + }, + _vertical: { + boxShadow: "1px 0 0 0 inset var(--colors-border-default)", + gap: "1", + }, + }, + indicator: { + background: "colorPalette.default", + _horizontal: { + height: "2px", + bottom: "0", + }, + _vertical: { + width: "2px", + left: "0", + }, + }, + content: { + pt: "4", + }, + trigger: { + _horizontal: { + pb: "2.5", + }, + }, + }, + outline: { + list: { + _horizontal: { + mb: "-1px", + }, + _vertical: { + mr: "-1px", + }, + }, + trigger: { + borderColor: "transparent", + borderWidth: "1px", + _horizontal: { + borderTopRadius: "l2", + }, + _vertical: { + borderTopLeftRadius: "l2", + borderBottomLeftRadius: "l2", + }, + _selected: { + background: "bg.default", + borderColor: "accent.500", + _horizontal: { + borderBottomColor: "transparent", + }, + _vertical: { + borderRightColor: "transparent", + }, + }, + }, + content: { + borderWidth: "1px", + borderColor: "accent.500", + background: "bg.default", + width: "full", + }, + }, + }, + size: { + sm: { + trigger: { + "& svg": { + width: "4", + height: "4", + }, + }, + }, + md: { + trigger: { + "& svg": { + width: "5", + height: "5", + }, + }, + }, + lg: { + trigger: { + "& svg": { + width: "5", + height: "5", + }, + }, + }, + }, + }, + compoundVariants: [ + { + size: "sm", + variant: "outline", + css: { + trigger: { + h: "9", + minW: "9", + textStyle: "sm", + px: "3.5", + }, + content: { + p: "3.5", + }, + }, + }, + { + size: "md", + variant: "outline", + css: { + trigger: { + h: "10", + minW: "10", + textStyle: "sm", + px: "4", + }, + content: { + p: "4", + }, + }, + }, + { + size: "lg", + variant: "outline", + css: { + trigger: { + h: "11", + minW: "11", + textStyle: "md", + px: "4.5", + }, + content: { + p: "4.5", + }, + }, + }, + { + size: "sm", + variant: "line", + css: { + trigger: { + fontSize: "sm", + h: "9", + minW: "9", + px: "2.5", + }, + content: { + pt: "3", + }, + }, + }, + { + size: "md", + variant: "line", + css: { + trigger: { + fontSize: "md", + h: "10", + minW: "10", + px: "3", + }, + content: { + pt: "4", + }, + }, + }, + { + size: "lg", + variant: "line", + css: { + trigger: { + px: "3.5", + h: "11", + minW: "11", + fontSize: "md", + }, + content: { + pt: "5", + }, + }, + }, + ], +}); diff --git a/web/styled-system/recipes/index.d.ts b/web/styled-system/recipes/index.d.ts index dae106766..3d020d6fb 100644 --- a/web/styled-system/recipes/index.d.ts +++ b/web/styled-system/recipes/index.d.ts @@ -8,4 +8,5 @@ export * from './link'; export * from './menu'; export * from './checkbox'; export * from './skeleton'; -export * from './select'; \ No newline at end of file +export * from './select'; +export * from './tabs'; \ No newline at end of file diff --git a/web/styled-system/recipes/index.mjs b/web/styled-system/recipes/index.mjs index 4a1e51299..d700eb0af 100644 --- a/web/styled-system/recipes/index.mjs +++ b/web/styled-system/recipes/index.mjs @@ -7,4 +7,5 @@ export * from './link.mjs'; export * from './menu.mjs'; export * from './checkbox.mjs'; export * from './skeleton.mjs'; -export * from './select.mjs'; \ No newline at end of file +export * from './select.mjs'; +export * from './tabs.mjs'; \ No newline at end of file diff --git a/web/styled-system/recipes/tabs.d.ts b/web/styled-system/recipes/tabs.d.ts new file mode 100644 index 000000000..495063c31 --- /dev/null +++ b/web/styled-system/recipes/tabs.d.ts @@ -0,0 +1,29 @@ +/* eslint-disable */ +import type { ConditionalValue } from '../types/index'; +import type { Pretty } from '../types/helpers'; +import type { DistributiveOmit } from '../types/system-types'; + +interface TabsVariant { + variant: "line" | "outline" +size: "sm" | "md" | "lg" +} + +type TabsVariantMap = { + [key in keyof TabsVariant]: Array +} + +export type TabsVariantProps = { + [key in keyof TabsVariant]?: TabsVariant[key] +} + +export interface TabsRecipe { + __type: TabsVariantProps + (props?: TabsVariantProps): Pretty> + raw: (props?: TabsVariantProps) => TabsVariantProps + variantMap: TabsVariantMap + variantKeys: Array + splitVariantProps(props: Props): [TabsVariantProps, Pretty>] +} + + +export declare const tabs: TabsRecipe \ No newline at end of file diff --git a/web/styled-system/recipes/tabs.mjs b/web/styled-system/recipes/tabs.mjs new file mode 100644 index 000000000..eb90cc57f --- /dev/null +++ b/web/styled-system/recipes/tabs.mjs @@ -0,0 +1,152 @@ +import { splitProps, getSlotCompoundVariant } from '../helpers.mjs'; +import { createRecipe } from './create-recipe.mjs'; + +const tabsDefaultVariants = { + "size": "md" +} +const tabsCompoundVariants = [ + { + "size": "sm", + "variant": "outline", + "css": { + "trigger": { + "h": "9", + "minW": "9", + "textStyle": "sm", + "px": "3.5" + }, + "content": { + "p": "3.5" + } + } + }, + { + "size": "md", + "variant": "outline", + "css": { + "trigger": { + "h": "10", + "minW": "10", + "textStyle": "sm", + "px": "4" + }, + "content": { + "p": "4" + } + } + }, + { + "size": "lg", + "variant": "outline", + "css": { + "trigger": { + "h": "11", + "minW": "11", + "textStyle": "md", + "px": "4.5" + }, + "content": { + "p": "4.5" + } + } + }, + { + "size": "sm", + "variant": "line", + "css": { + "trigger": { + "fontSize": "sm", + "h": "9", + "minW": "9", + "px": "2.5" + }, + "content": { + "pt": "3" + } + } + }, + { + "size": "md", + "variant": "line", + "css": { + "trigger": { + "fontSize": "md", + "h": "10", + "minW": "10", + "px": "3" + }, + "content": { + "pt": "4" + } + } + }, + { + "size": "lg", + "variant": "line", + "css": { + "trigger": { + "px": "3.5", + "h": "11", + "minW": "11", + "fontSize": "md" + }, + "content": { + "pt": "5" + } + } + } +] + +const tabsSlotNames = [ + [ + "root", + "tabs__root" + ], + [ + "list", + "tabs__list" + ], + [ + "trigger", + "tabs__trigger" + ], + [ + "content", + "tabs__content" + ], + [ + "indicator", + "tabs__indicator" + ] +] +const tabsSlotFns = /* @__PURE__ */ tabsSlotNames.map(([slotName, slotKey]) => [slotName, createRecipe(slotKey, tabsDefaultVariants, getSlotCompoundVariant(tabsCompoundVariants, slotName))]) + +const tabsFn = (props = {}) => { + return Object.fromEntries(tabsSlotFns.map(([slotName, slotFn]) => [slotName, slotFn(props)])) +} + +const tabsVariantKeys = [ + "variant", + "size" +] + +export const tabs = /* @__PURE__ */ Object.assign(tabsFn, { + __recipe__: false, + __name__: 'tabs', + raw: (props) => props, + variantKeys: tabsVariantKeys, + variantMap: { + "variant": [ + "line", + "outline" + ], + "size": [ + "sm", + "md", + "lg" + ] +}, + splitVariantProps(props) { + return splitProps(props, tabsVariantKeys) + }, +}) \ No newline at end of file