feat: flex component, document higher-order layout components
2 files added
5 files modified
| New file |
| | |
| | | --- |
| | | 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()) |
| | | ``` |
| | |
| | | |
| | | 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 |
| | | |
| | |
| | | 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: [ |
| | |
| | | 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> |
| New file |
| | |
| | | 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> |
| | |
| | | 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> |
| | |
| | | import RecentNotes from "./RecentNotes" |
| | | import Breadcrumbs from "./Breadcrumbs" |
| | | import Comments from "./Comments" |
| | | import Flex from "./Flex" |
| | | |
| | | export { |
| | | ArticleTitle, |
| | |
| | | NotFound, |
| | | Breadcrumbs, |
| | | Comments, |
| | | Flex, |
| | | } |