feat: flex component, document higher-order layout components
This commit is contained in:
parent
87b803790c
commit
2718ab9019
7 changed files with 153 additions and 33 deletions
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.
|
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
|
### Layout breakpoints
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,15 @@ export const defaultContentPageLayout: PageLayout = {
|
||||||
left: [
|
left: [
|
||||||
Component.PageTitle(),
|
Component.PageTitle(),
|
||||||
Component.MobileOnly(Component.Spacer()),
|
Component.MobileOnly(Component.Spacer()),
|
||||||
Component.Search(),
|
Component.Flex({
|
||||||
Component.Darkmode(),
|
components: [
|
||||||
|
{
|
||||||
|
Component: Component.Search(),
|
||||||
|
grow: true,
|
||||||
|
},
|
||||||
|
{ Component: Component.Darkmode() },
|
||||||
|
],
|
||||||
|
}),
|
||||||
Component.Explorer(),
|
Component.Explorer(),
|
||||||
],
|
],
|
||||||
right: [
|
right: [
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
|
|
||||||
export default ((component?: QuartzComponent) => {
|
export default ((component: QuartzComponent) => {
|
||||||
if (component) {
|
const Component = component
|
||||||
const Component = component
|
const DesktopOnly: QuartzComponent = (props: QuartzComponentProps) => {
|
||||||
const DesktopOnly: QuartzComponent = (props: QuartzComponentProps) => {
|
return <Component displayClass="desktop-only" {...props} />
|
||||||
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 () => <></>
|
|
||||||
}
|
}
|
||||||
}) 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"
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
|
|
||||||
export default ((component?: QuartzComponent) => {
|
export default ((component: QuartzComponent) => {
|
||||||
if (component) {
|
const Component = component
|
||||||
const Component = component
|
const MobileOnly: QuartzComponent = (props: QuartzComponentProps) => {
|
||||||
const MobileOnly: QuartzComponent = (props: QuartzComponentProps) => {
|
return <Component displayClass="mobile-only" {...props} />
|
||||||
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 () => <></>
|
|
||||||
}
|
}
|
||||||
}) 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 RecentNotes from "./RecentNotes"
|
||||||
import Breadcrumbs from "./Breadcrumbs"
|
import Breadcrumbs from "./Breadcrumbs"
|
||||||
import Comments from "./Comments"
|
import Comments from "./Comments"
|
||||||
|
import Flex from "./Flex"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ArticleTitle,
|
ArticleTitle,
|
||||||
|
@ -44,4 +45,5 @@ export {
|
||||||
NotFound,
|
NotFound,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
Comments,
|
Comments,
|
||||||
|
Flex,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue