From bbb0ebc106d52ddafa910d0e36f282bae71e1094 Mon Sep 17 00:00:00 2001
From: Danil Karpenko <81296950+LIMPIX31@users.noreply.github.com>
Date: Fri, 3 May 2024 15:38:51 +0200
Subject: [PATCH] feat: sidebar layout (#20)
---
.github/workflows/frontend.yml | 1 +
app/fragments/window/sidebar/index.tsx | 5 ++
app/fragments/window/sidebar/styles.css.ts | 6 +++
app/root.tsx | 17 +++++--
app/shared/nanolib.ts | 31 +++++++++++++
app/theme/base.css.ts | 7 ++-
app/theme/dark.css.ts | 1 +
app/theme/light.css.ts | 1 +
app/theme/preflight.css.ts | 9 +++-
app/theme/vars.css.ts | 18 +++++++-
app/ui/layout/index.tsx | 26 +++++++++++
app/ui/layout/shared.ts | 32 +++++++++++++
app/ui/layout/styles.css.ts | 54 ++++++++++++++++++++++
13 files changed, 197 insertions(+), 11 deletions(-)
create mode 100644 app/fragments/window/sidebar/index.tsx
create mode 100644 app/fragments/window/sidebar/styles.css.ts
create mode 100644 app/shared/nanolib.ts
create mode 100644 app/ui/layout/index.tsx
create mode 100644 app/ui/layout/shared.ts
create mode 100644 app/ui/layout/styles.css.ts
diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml
index 35fec7c..dd8f539 100644
--- a/.github/workflows/frontend.yml
+++ b/.github/workflows/frontend.yml
@@ -3,6 +3,7 @@ on:
pull_request:
branches: [ dev ]
paths:
+ - 'app/**'
- 'package.json'
- 'tsconfig.json'
- 'bun.lockb'
diff --git a/app/fragments/window/sidebar/index.tsx b/app/fragments/window/sidebar/index.tsx
new file mode 100644
index 0000000..30c440b
--- /dev/null
+++ b/app/fragments/window/sidebar/index.tsx
@@ -0,0 +1,5 @@
+import { sidebar } from './styles.css.ts'
+
+export function Sidebar() {
+ return
+}
diff --git a/app/fragments/window/sidebar/styles.css.ts b/app/fragments/window/sidebar/styles.css.ts
new file mode 100644
index 0000000..7de4392
--- /dev/null
+++ b/app/fragments/window/sidebar/styles.css.ts
@@ -0,0 +1,6 @@
+import { style } from '@vanilla-extract/css'
+
+export const sidebar = style({
+ width: '4rem',
+ position: 'relative',
+})
diff --git a/app/root.tsx b/app/root.tsx
index f0aa73e..a1fea4c 100644
--- a/app/root.tsx
+++ b/app/root.tsx
@@ -1,8 +1,17 @@
-import '&theme/preflight.css.ts'
-import '&theme/provider.tsx'
+import '&theme/preflight.css.ts'
+import '&theme/provider.tsx'
-import { Button } from '&ui/button'
+import { Sidebar } from '&fragments/window/sidebar'
+import { Box } from '&ui/layout'
+import { Row } from '&ui/layout'
export function Root() {
- return
+ return (
+
+
+
+ {/* TODO */}
+
+
+ )
}
diff --git a/app/shared/nanolib.ts b/app/shared/nanolib.ts
new file mode 100644
index 0000000..78a8b7a
--- /dev/null
+++ b/app/shared/nanolib.ts
@@ -0,0 +1,31 @@
+const { entries } = Object
+
+export type Leaves = T extends object
+ ? { [K in keyof T]: `${Exclude}${Leaves extends never ? '' : `.${Leaves}`}` }[keyof T]
+ : never
+
+type PathAccess = P extends `${infer K}.${infer R}`
+ ? K extends keyof T
+ ? PathAccess
+ : never
+ : P extends keyof T
+ ? T[P]
+ : never
+
+export function leaves(target: T, prefix: string[] = []): Leaves[] {
+ const list = []
+
+ for (const [key, value] of entries(target)) {
+ if (typeof value === 'object') {
+ list.push(...leaves(value, [...prefix, key]))
+ } else {
+ list.push([...prefix, key].join('.'))
+ }
+ }
+
+ return list as Leaves[]
+}
+
+export function pathAccess>(target: T, path: P) {
+ return path.split('.').reduce((a, c) => Reflect.get(a as object, c), target) as PathAccess
+}
diff --git a/app/theme/base.css.ts b/app/theme/base.css.ts
index 7afc3f9..a999e68 100644
--- a/app/theme/base.css.ts
+++ b/app/theme/base.css.ts
@@ -1,7 +1,6 @@
-import { clashDisplay } from '&theme/fonts.css.ts'
-import { manrope } from '&theme/fonts.css.ts'
-import { geistMono } from '&theme/fonts.css.ts'
-
+import { clashDisplay } from './fonts.css.ts'
+import { manrope } from './fonts.css.ts'
+import { geistMono } from './fonts.css.ts'
import type { ThemePart } from './shared.ts'
import type { vars } from './vars.css.ts'
diff --git a/app/theme/dark.css.ts b/app/theme/dark.css.ts
index 91202b6..d0490d2 100644
--- a/app/theme/dark.css.ts
+++ b/app/theme/dark.css.ts
@@ -17,6 +17,7 @@ export const theme = createTheme(vars, {
readable: '#18181b',
},
gray: {
+ surface: '#212124',
input: '#3b3a38',
},
},
diff --git a/app/theme/light.css.ts b/app/theme/light.css.ts
index eb3e75c..fb23810 100644
--- a/app/theme/light.css.ts
+++ b/app/theme/light.css.ts
@@ -17,6 +17,7 @@ export const theme = createTheme(vars, {
readable: '#f9f1e9',
},
gray: {
+ surface: '#ece3d9',
input: '#d2cfc9',
},
},
diff --git a/app/theme/preflight.css.ts b/app/theme/preflight.css.ts
index e360a82..30e1ca2 100644
--- a/app/theme/preflight.css.ts
+++ b/app/theme/preflight.css.ts
@@ -24,8 +24,8 @@ globalStyle('*', {
globalStyle('body', {
'@layer': {
[preflight]: {
- minHeight: '100vh',
- backgroundColor: vars.color.background,
+ height: '100vh',
+ backgroundColor: vars.color.gray.surface,
fontFamily: vars.font.sans,
fontSize: '87.5%',
transition: `all ${vars.transition.duration.l} ${vars.transition.timing}`,
@@ -55,3 +55,8 @@ globalStyle('img', {
},
},
})
+
+globalStyle('#root', {
+ height: '100vh',
+ display: 'flex',
+})
diff --git a/app/theme/vars.css.ts b/app/theme/vars.css.ts
index 7ac5ced..b8df049 100644
--- a/app/theme/vars.css.ts
+++ b/app/theme/vars.css.ts
@@ -1,4 +1,8 @@
-import { createThemeContract } from '@vanilla-extract/css'
+import type { style } from '@vanilla-extract/css'
+import { createThemeContract } from '@vanilla-extract/css'
+
+import { leaves } from '&shared/nanolib.ts'
+import { pathAccess } from '&shared/nanolib.ts'
const typography = {
size: null,
@@ -20,6 +24,7 @@ export const vars = createThemeContract({
readable: null,
},
gray: {
+ surface: null,
input: null,
},
},
@@ -73,3 +78,14 @@ export const vars = createThemeContract({
},
},
})
+
+export function mapVars(vars: T, fn: (val: keyof T) => Parameters[0]) {
+ return Object.fromEntries(Object.entries(vars).map(([key, value]) => [key, fn(value)])) as Record<
+ keyof T,
+ Parameters[0]
+ >
+}
+
+export function mapVarsPath(vars: T, fn: (val: string) => Parameters[0]) {
+ return Object.fromEntries(leaves(vars).map((path) => [path, fn(pathAccess(vars, path) as string)]))
+}
diff --git a/app/ui/layout/index.tsx b/app/ui/layout/index.tsx
new file mode 100644
index 0000000..024e3d9
--- /dev/null
+++ b/app/ui/layout/index.tsx
@@ -0,0 +1,26 @@
+import { clsx } from 'clsx'
+
+import type { LayoutProps } from './shared.ts'
+import { useStyle } from './shared.ts'
+import { box } from './styles.css.ts'
+import { col } from './styles.css.ts'
+import { row } from './styles.css.ts'
+
+export function Row(props: LayoutProps) {
+ const { style, rest } = useStyle(props)
+ return
+}
+
+export function Col(props: LayoutProps) {
+ const { style, rest } = useStyle(props)
+ return
+}
+
+export type BoxProps = Omit
+
+export function Box(props: BoxProps) {
+ const { style, rest } = useStyle(props)
+ return
+}
+
+export type { LayoutProps }
diff --git a/app/ui/layout/shared.ts b/app/ui/layout/shared.ts
new file mode 100644
index 0000000..9a2248a
--- /dev/null
+++ b/app/ui/layout/shared.ts
@@ -0,0 +1,32 @@
+import type { ComponentProps } from 'react'
+
+import { clsx } from 'clsx'
+
+import type { Leaves } from '&shared/nanolib.ts'
+import type { vars } from '&theme/vars.css.ts'
+import { variants } from '&ui/layout/styles.css.ts'
+
+export type LayoutProps = ComponentProps<'div'> & {
+ gap?: keyof typeof vars.space
+ p?: keyof typeof vars.space
+ bg?: Leaves
+ rounded?: keyof typeof vars.rounded
+ roundedLeft?: keyof typeof vars.rounded
+ grow?: boolean
+}
+
+export function useStyle(props: LayoutProps) {
+ const { gap, p, bg, grow, rounded, roundedLeft, ...rest } = props
+
+ return {
+ style: clsx(
+ gap && variants.gap[gap],
+ p && variants.p[p],
+ bg && variants.bg[bg],
+ grow && variants.grow.true,
+ rounded && variants.rounded[rounded],
+ roundedLeft && variants.roundedLeft[roundedLeft],
+ ),
+ rest,
+ }
+}
diff --git a/app/ui/layout/styles.css.ts b/app/ui/layout/styles.css.ts
new file mode 100644
index 0000000..57de793
--- /dev/null
+++ b/app/ui/layout/styles.css.ts
@@ -0,0 +1,54 @@
+import { style } from '@vanilla-extract/css'
+import { styleVariants } from '@vanilla-extract/css'
+
+import { mapVars } from '&theme/vars.css.ts'
+import { mapVarsPath } from '&theme/vars.css.ts'
+import { vars } from '&theme/vars.css.ts'
+
+const base = style({
+ display: 'flex',
+ transitionProperty: 'all',
+ transitionDuration: vars.transition.duration.m,
+ transitionTimingFunction: vars.transition.timing,
+})
+
+export const variants = {
+ gap: styleVariants(mapVars(vars.space, (gap) => ({ gap }))),
+ p: styleVariants(mapVars(vars.space, (padding) => ({ padding }))),
+ bg: styleVariants(mapVarsPath(vars.color, (backgroundColor) => ({ backgroundColor }))),
+ rounded: styleVariants(mapVars(vars.rounded, (borderRadius) => ({ borderRadius }))),
+ roundedLeft: styleVariants(
+ mapVars(vars.rounded, (radius) => ({
+ borderTopLeftRadius: radius,
+ borderBottomLeftRadius: radius,
+ })),
+ ),
+ grow: styleVariants({
+ true: {
+ flexGrow: 1,
+ },
+ }),
+}
+
+export const row = style([
+ base,
+ {
+ width: '100%',
+ flexDirection: 'row',
+ },
+])
+
+export const col = style([
+ base,
+ {
+ height: '100%',
+ flexDirection: 'column',
+ },
+])
+
+export const box = style([
+ base,
+ {
+ flexGrow: 1,
+ },
+])