threlte logo
@threlte/flex

createClassParser

The prop class can be used on <Box> and <Flex> to easily configure the flexbox with predefined class names just as you would do in CSS. In order to use the prop, you need to create a ClassParser which accepts a single string and returns NodeProps. The function createClassParser is a helper function that provides the proper types.

Let’s assume, you want to create a parser that supports the following class names but in 3D space:

.container {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: stretch;
  gap: 10px;
  padding: 10px;
}
.item {
  width: auto;
  height: auto;
  flex: 1;
}

You then need to create a ClassParser which returns the corresponding props:

import { createClassParser } from '@threlte/flex'

const classParser = createClassParser((string, props) => {
  const classNames = string.split(' ')
  for (const className of classNames) {
    switch (className) {
      case 'container':
        props.flexDirection = 'Row'
        props.justifyContent = 'Center'
        props.alignItems = 'Stretch'
        props.gap = 10
        props.padding = 10
        break
      case 'item':
        props.width = 'auto'
        props.height = 'auto'
        props.flex = 1
    }
  }
  return props
})

Now you can use the prop class on <Flex> and <Box> to configure the flexbox:

<script lang="ts">
  import { Canvas, T } from '@threlte/core'
  import Scene from './Scene.svelte'
  import { NoToneMapping } from 'three'
  import { OrbitControls } from '@threlte/extras'

  let innerWidth = 0
</script>

<svelte:window bind:innerWidth />

<div class="relative h-screen w-screen">
  <Canvas toneMapping={NoToneMapping}>
    <T.OrthographicCamera
      makeDefault
      position.z={1000}
      zoom={innerWidth / 500}
    >
      <OrbitControls />
    </T.OrthographicCamera>

    <Scene />
  </Canvas>
</div>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Shape, ShapeGeometry } from 'three'

  export let color: string = 'white'
  export let height = 1
  export let width = 1
  export let radius = 5
  export let depth = 0

  let x = 1
  let y = 1

  const createGeometry = (width: number, height: number, radius: number): ShapeGeometry => {
    let shape = new Shape()
    shape.moveTo(x, y + radius)
    shape.lineTo(x, y + height - radius)
    shape.quadraticCurveTo(x, y + height, x + radius, y + height)
    shape.lineTo(x + width - radius, y + height)
    shape.quadraticCurveTo(x + width, y + height, x + width, y + height - radius)
    shape.lineTo(x + width, y + radius)
    shape.quadraticCurveTo(x + width, y, x + width - radius, y)
    shape.lineTo(x + radius, y)
    shape.quadraticCurveTo(x, y, x, y + radius)

    const geometry = new ShapeGeometry(shape)
    geometry.center()
    return geometry
  }

  $: geometry = createGeometry(width, height, radius)
</script>

<T.Mesh
  position.z={depth * 20}
  renderOrder={depth}
>
  <T is={geometry} />
  <T.MeshBasicMaterial {color} />
</T.Mesh>
<script lang="ts">
  import { Box, Flex, createClassParser } from '@threlte/flex'
  import RoundedPlane from './RoundedPlane.svelte'

  const classParser = createClassParser((string, props) => {
    const classNames = string.split(' ')
    for (const className of classNames) {
      switch (className) {
        case 'container':
          props.flexDirection = 'Row'
          props.justifyContent = 'Center'
          props.alignItems = 'Stretch'
          props.gap = 10
          props.padding = 10
          break
        case 'item':
          props.width = 'auto'
          props.height = 'auto'
          props.flex = 1
      }
    }
    return props
  })
</script>

<Flex
  width={300}
  height={150}
  {classParser}
  class="container"
>
  <RoundedPlane
    radius={15}
    color="#FE3D00"
    width={300}
    height={150}
  />
  <Box
    class="item"
    let:width
    let:height
  >
    <RoundedPlane
      color="#EB1688"
      {width}
      {height}
      depth={1}
    />
  </Box>
  <Box
    class="item"
    let:width
    let:height
  >
    <RoundedPlane
      color="#113BFA"
      {width}
      {height}
      depth={1}
    />
  </Box>
  <Box
    class="item"
    let:width
    let:height
  >
    <RoundedPlane
      color="#590C65"
      {width}
      {height}
      depth={1}
    />
  </Box>
</Flex>