feat: flex component, document higher-order layout components
This commit is contained in:
		
							
								
								
									
										62
									
								
								docs/layout-components.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								docs/layout-components.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
---
 | 
			
		||||
title: Higher-Order Layout Components
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Quartz provides several higher-order components that help with layout composition and responsive design. These components wrap other components to add additional functionality or modify their behavior.
 | 
			
		||||
 | 
			
		||||
## `Flex` Component
 | 
			
		||||
 | 
			
		||||
The `Flex` component creates a [flexible box layout](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) that can arrange child components in various ways. It's particularly useful for creating responsive layouts and organizing components in rows or columns.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
type FlexConfig = {
 | 
			
		||||
  components: {
 | 
			
		||||
    Component: QuartzComponent
 | 
			
		||||
    grow?: boolean // whether component should grow to fill space
 | 
			
		||||
    shrink?: boolean // whether component should shrink if needed
 | 
			
		||||
    basis?: string // initial main size of the component
 | 
			
		||||
    order?: number // order in flex container
 | 
			
		||||
    align?: "start" | "end" | "center" | "stretch" // cross-axis alignment
 | 
			
		||||
    justify?: "start" | "end" | "center" | "between" | "around" // main-axis alignment
 | 
			
		||||
  }[]
 | 
			
		||||
  direction?: "row" | "row-reverse" | "column" | "column-reverse"
 | 
			
		||||
  wrap?: "nowrap" | "wrap" | "wrap-reverse"
 | 
			
		||||
  gap?: string
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Example Usage
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
Component.Flex({
 | 
			
		||||
  components: [
 | 
			
		||||
    {
 | 
			
		||||
      Component: Component.Search(),
 | 
			
		||||
      grow: true, // Search will grow to fill available space
 | 
			
		||||
    },
 | 
			
		||||
    { Component: Component.Darkmode() }, // Darkmode keeps its natural size
 | 
			
		||||
  ],
 | 
			
		||||
  direction: "row",
 | 
			
		||||
  gap: "1rem",
 | 
			
		||||
})
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## `MobileOnly` Component
 | 
			
		||||
 | 
			
		||||
The `MobileOnly` component is a wrapper that makes its child component only visible on mobile devices. This is useful for creating responsive layouts where certain components should only appear on smaller screens.
 | 
			
		||||
 | 
			
		||||
### Example Usage
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
Component.MobileOnly(Component.Spacer())
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## `DesktopOnly` Component
 | 
			
		||||
 | 
			
		||||
The `DesktopOnly` component is the counterpart to `MobileOnly`. It makes its child component only visible on desktop devices. This helps create responsive layouts where certain components should only appear on larger screens.
 | 
			
		||||
 | 
			
		||||
### Example Usage
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
Component.DesktopOnly(Component.TableOfContents())
 | 
			
		||||
```
 | 
			
		||||
@@ -35,7 +35,9 @@ These correspond to following parts of the page:
 | 
			
		||||
 | 
			
		||||
Quartz **components**, like plugins, can take in additional properties as configuration options. If you're familiar with React terminology, you can think of them as Higher-order Components.
 | 
			
		||||
 | 
			
		||||
See [a list of all the components](component.md) for all available components along with their configuration options. You can also checkout the guide on [[creating components]] if you're interested in further customizing the behaviour of Quartz.
 | 
			
		||||
See [a list of all the components](component.md) for all available components along with their configuration options. Additionally, Quartz provides several built-in higher-order components for layout composition - see [[layout-components]] for more details.
 | 
			
		||||
 | 
			
		||||
You can also checkout the guide on [[creating components]] if you're interested in further customizing the behaviour of Quartz.
 | 
			
		||||
 | 
			
		||||
### Layout breakpoints
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,15 @@ export const defaultContentPageLayout: PageLayout = {
 | 
			
		||||
  left: [
 | 
			
		||||
    Component.PageTitle(),
 | 
			
		||||
    Component.MobileOnly(Component.Spacer()),
 | 
			
		||||
    Component.Search(),
 | 
			
		||||
    Component.Darkmode(),
 | 
			
		||||
    Component.Flex({
 | 
			
		||||
      components: [
 | 
			
		||||
        {
 | 
			
		||||
          Component: Component.Search(),
 | 
			
		||||
          grow: true,
 | 
			
		||||
        },
 | 
			
		||||
        { Component: Component.Darkmode() },
 | 
			
		||||
      ],
 | 
			
		||||
    }),
 | 
			
		||||
    Component.Explorer(),
 | 
			
		||||
  ],
 | 
			
		||||
  right: [
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,14 @@
 | 
			
		||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
 | 
			
		||||
export default ((component?: QuartzComponent) => {
 | 
			
		||||
  if (component) {
 | 
			
		||||
    const Component = component
 | 
			
		||||
    const DesktopOnly: QuartzComponent = (props: QuartzComponentProps) => {
 | 
			
		||||
      return <Component displayClass="desktop-only" {...props} />
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DesktopOnly.displayName = component.displayName
 | 
			
		||||
    DesktopOnly.afterDOMLoaded = component?.afterDOMLoaded
 | 
			
		||||
    DesktopOnly.beforeDOMLoaded = component?.beforeDOMLoaded
 | 
			
		||||
    DesktopOnly.css = component?.css
 | 
			
		||||
    return DesktopOnly
 | 
			
		||||
  } else {
 | 
			
		||||
    return () => <></>
 | 
			
		||||
export default ((component: QuartzComponent) => {
 | 
			
		||||
  const Component = component
 | 
			
		||||
  const DesktopOnly: QuartzComponent = (props: QuartzComponentProps) => {
 | 
			
		||||
    return <Component displayClass="desktop-only" {...props} />
 | 
			
		||||
  }
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 | 
			
		||||
  DesktopOnly.displayName = component.displayName
 | 
			
		||||
  DesktopOnly.afterDOMLoaded = component?.afterDOMLoaded
 | 
			
		||||
  DesktopOnly.beforeDOMLoaded = component?.beforeDOMLoaded
 | 
			
		||||
  DesktopOnly.css = component?.css
 | 
			
		||||
  return DesktopOnly
 | 
			
		||||
}) satisfies QuartzComponentConstructor<QuartzComponent>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								quartz/components/Flex.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								quartz/components/Flex.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import { concatenateResources } from "../util/resources"
 | 
			
		||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
 | 
			
		||||
type FlexConfig = {
 | 
			
		||||
  components: {
 | 
			
		||||
    Component: QuartzComponent
 | 
			
		||||
    grow?: boolean
 | 
			
		||||
    shrink?: boolean
 | 
			
		||||
    basis?: string
 | 
			
		||||
    order?: number
 | 
			
		||||
    align?: "start" | "end" | "center" | "stretch"
 | 
			
		||||
    justify?: "start" | "end" | "center" | "between" | "around"
 | 
			
		||||
  }[]
 | 
			
		||||
  direction?: "row" | "row-reverse" | "column" | "column-reverse"
 | 
			
		||||
  wrap?: "nowrap" | "wrap" | "wrap-reverse"
 | 
			
		||||
  gap?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default ((config: FlexConfig) => {
 | 
			
		||||
  const Flex: QuartzComponent = (props: QuartzComponentProps) => {
 | 
			
		||||
    const direction = config.direction ?? "row"
 | 
			
		||||
    const wrap = config.wrap ?? "nowrap"
 | 
			
		||||
    const gap = config.gap ?? "1rem"
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div style={`display: flex; flex-direction: ${direction}; flex-wrap: ${wrap}; gap: ${gap};`}>
 | 
			
		||||
        {config.components.map((c) => {
 | 
			
		||||
          const grow = c.grow ? 1 : 0
 | 
			
		||||
          const shrink = (c.shrink ?? true) ? 1 : 0
 | 
			
		||||
          const basis = c.basis ?? "auto"
 | 
			
		||||
          const order = c.order ?? 0
 | 
			
		||||
          const align = c.align ?? "center"
 | 
			
		||||
          const justify = c.justify ?? "center"
 | 
			
		||||
 | 
			
		||||
          return (
 | 
			
		||||
            <div
 | 
			
		||||
              style={`flex-grow: ${grow}; flex-shrink: ${shrink}; flex-basis: ${basis}; order: ${order}; align-self: ${align}; justify-self: ${justify};`}
 | 
			
		||||
            >
 | 
			
		||||
              <c.Component {...props} />
 | 
			
		||||
            </div>
 | 
			
		||||
          )
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Flex.afterDOMLoaded = concatenateResources(
 | 
			
		||||
    ...config.components.map((c) => c.Component.afterDOMLoaded),
 | 
			
		||||
  )
 | 
			
		||||
  Flex.beforeDOMLoaded = concatenateResources(
 | 
			
		||||
    ...config.components.map((c) => c.Component.beforeDOMLoaded),
 | 
			
		||||
  )
 | 
			
		||||
  Flex.css = concatenateResources(...config.components.map((c) => c.Component.css))
 | 
			
		||||
  return Flex
 | 
			
		||||
}) satisfies QuartzComponentConstructor<FlexConfig>
 | 
			
		||||
@@ -1,18 +1,14 @@
 | 
			
		||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
 | 
			
		||||
export default ((component?: QuartzComponent) => {
 | 
			
		||||
  if (component) {
 | 
			
		||||
    const Component = component
 | 
			
		||||
    const MobileOnly: QuartzComponent = (props: QuartzComponentProps) => {
 | 
			
		||||
      return <Component displayClass="mobile-only" {...props} />
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MobileOnly.displayName = component.displayName
 | 
			
		||||
    MobileOnly.afterDOMLoaded = component?.afterDOMLoaded
 | 
			
		||||
    MobileOnly.beforeDOMLoaded = component?.beforeDOMLoaded
 | 
			
		||||
    MobileOnly.css = component?.css
 | 
			
		||||
    return MobileOnly
 | 
			
		||||
  } else {
 | 
			
		||||
    return () => <></>
 | 
			
		||||
export default ((component: QuartzComponent) => {
 | 
			
		||||
  const Component = component
 | 
			
		||||
  const MobileOnly: QuartzComponent = (props: QuartzComponentProps) => {
 | 
			
		||||
    return <Component displayClass="mobile-only" {...props} />
 | 
			
		||||
  }
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 | 
			
		||||
  MobileOnly.displayName = component.displayName
 | 
			
		||||
  MobileOnly.afterDOMLoaded = component?.afterDOMLoaded
 | 
			
		||||
  MobileOnly.beforeDOMLoaded = component?.beforeDOMLoaded
 | 
			
		||||
  MobileOnly.css = component?.css
 | 
			
		||||
  return MobileOnly
 | 
			
		||||
}) satisfies QuartzComponentConstructor<QuartzComponent>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import MobileOnly from "./MobileOnly"
 | 
			
		||||
import RecentNotes from "./RecentNotes"
 | 
			
		||||
import Breadcrumbs from "./Breadcrumbs"
 | 
			
		||||
import Comments from "./Comments"
 | 
			
		||||
import Flex from "./Flex"
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  ArticleTitle,
 | 
			
		||||
@@ -44,4 +45,5 @@ export {
 | 
			
		||||
  NotFound,
 | 
			
		||||
  Breadcrumbs,
 | 
			
		||||
  Comments,
 | 
			
		||||
  Flex,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user