Skip to content

Canvas Manipulation ​

This guide explains how to interact with canvases in the Canvas UI system, including retrieving canvases, working with sections, understanding context, and updating canvases.

Getting Canvases ​

Current Canvas ​

To get the currently active canvas in your application:

typescript
// Using the useOpenedCanvas composable (Vue)
import useOpenedCanvas from '@composables/useOpenedCanvas'

const { openedCanvas } = useOpenedCanvas()
// openedCanvas.value contains the current canvas data

In a Canvas Builder component, the active canvas is typically available through props or composables:

typescript
// In a component that receives activeCanvas as a prop
const { activeCanvas } = defineProps<{
  activeCanvas: CanvasRetrieve
}>()

// Using reactive properties in canvas builder components
const activeCanvas = computed(() => props.activeCanvas)

Any Canvas ​

To fetch a specific canvas by ID:

typescript
// Using the ClientApi in impact apps
import { ClientApi } from '@api/client'

// Fetch by ID
const canvas = await ClientApi.getCanvas({ id: 'canvas-id' })

// Using the API in admin apps
import { getCanvas } from '@shared/api/canvas/canvas.api'

const canvas = await getCanvas('canvas-id')

Getting Canvas Sections ​

Canvas sections are available through the canvas object structure:

typescript
// Access sections from canvas object
const sections = canvas.sections

// In the Canvas Builder, filtered sections are often computed:
const activeCanvasSections = computed(() => {
  const visibleKey = getVisibleKey() // Based on current view mode
  
  return (props.data.sections ?? []).reduce(
    (sections, section, nodeIdx) => {
      const foundSection = storeActiveCanvasSections.value?.[section.id]
      
      if (foundSection) {
        // Process section visibility and content
        sections.push({
          section: {
            ...section,
            content: { 
              ...(section.content ?? {}), 
              data: filteredContent 
            },
          },
          index: nodeIdx,
        })
      }
      
      return sections
    },
    [] as { section: CanvasRetrieve['sections'][number]; index: number }[]
  )
})

Understanding Context ​

Context is a key-value object attached to a canvas that stores state information used by canvas components. It enables components to persist data and communicate with each other.

typescript
// Canvas context structure
interface CanvasContext {
  [key: string]: any // Flexible key-value storage
  pitcher?: {
    // System context
    completion_wizard?: {
      completed?: boolean
    }
  }
  // App-specific context
  myAppName?: {
    myState: string
    myData: any[]
  }
  // Section-specific context overrides
  ctx_overwrite_sectionId_index?: {
    // Section-specific overrides to merge with base context
  }
}

Context can be used to:

  • Store component state that persists between sessions
  • Share data between different components
  • Implement conditional logic based on context values
  • Override context values for specific sections
typescript
// Accessing context in a component

// 1. Direct access via composables
import { useCanvas } from '@canvas-builder/composables/useCanvas'

// Access context directly from activeCanvas
const { activeCanvas } = useCanvas()
const myAppData = computed(() => activeCanvas.value?.context?.myAppName?.myData)

// In standard apps, using useOpenedCanvas
import useOpenedCanvas from '@composables/useOpenedCanvas'

const { openedCanvas } = useOpenedCanvas()
const context = computed(() => openedCanvas.value?.context)

// 2. With section-specific context overrides
function getMergeContext() {
  const sectionInfo = sectionListSectionInfo.value
  if (!activeCanvas.value?.context) {
    return {}
  }
  
  // If there's no section info, return the base context
  if (!sectionInfo) {
    return activeCanvas.value.context
  }
  
  // Check for section-specific overrides
  const sectionOverrideKey = `ctx_overwrite_${sectionInfo.sectionListId}_${sectionInfo.sectionIdx}`
  const hasOverrides = activeCanvas.value.context[sectionOverrideKey]
  
  if (hasOverrides) {
    // Merge base context with section-specific overrides
    return merge(activeCanvas.value.context, activeCanvas.value.context[sectionOverrideKey])
  }
  
  return activeCanvas.value.context
}

// 3. In embedded apps via the `ui_app_set_data` message
import { useUi } from '@pitcher/js-api'

useUi().onAppSetData(async (data) => {
  // Access canvas context from the payload
  const canvasContext = data.canvas.context

  // Access app-specific context using your app's namespace
  const myAppContext = canvasContext.myAppName
})

Updating Canvas ​

You can update a canvas by using one of these methods:

Using UI API ​

typescript
import { useUi } from '@pitcher/canvas-ui'

// Update current canvas
await useUi().updateCanvas({
  context: {
    myApp: {
      someState: 'updated value'
    }
  }
})

Using Client API ​

typescript
import { ClientApi } from '@api/client'

// Update canvas by ID with specific fields
await ClientApi.updateCanvas({
  id: 'canvas-id',
  name: 'Updated Canvas Name',
  fields: 'id,name' // Specify fields to return after update
})

In Admin Applications ​

typescript
import { updateCanvas } from '@shared/api/canvas/canvas.api'

// Update canvas with mutation
const updateCanvasMutation = useUpdateCanvas()
updateCanvasMutation.mutate({
  id: 'canvas-id',
  context: { /* updated context */ },
  fields: 'id,name,context'
})

Updating From Embedded Apps ​

When updating from embedded applications, you can use the event system:

typescript
// Update via event
ClientApi.broadcast({
  type: PitcherEventName.CANVAS_UPDATED,
  body: {
    id: 'canvas-id',
    context: {
      myApp: {
        stateToUpdate: 'new value'
      }
    }
  }
})

The system automatically derives what fields have changed and updates only those fields on the server.