Document Vue SDK public APIs
This commit is contained in:
parent
99e646aaa4
commit
a743da54b6
|
|
@ -1,125 +1,227 @@
|
|||
# @open-pencil/vue
|
||||
|
||||
Headless Vue 3 SDK for embedding an OpenPencil design editor. Renderless components expose logic via scoped slots — bring your own UI.
|
||||
Headless Vue 3 SDK for building OpenPencil-powered editors.
|
||||
|
||||
`@open-pencil/vue` sits on top of `@open-pencil/core` and provides:
|
||||
|
||||
- Vue editor injection via `provideEditor()` / `useEditor()`
|
||||
- canvas integration via `useCanvas()`, `useCanvasInput()`, and `useTextEdit()`
|
||||
- selection, command, and panel composables
|
||||
- headless structural primitives like `CanvasRoot`, `LayerTreeRoot`, `PageListRoot`, and `ToolbarRoot`
|
||||
|
||||
The SDK is headless by design: it provides logic and structure, while your app owns styling and product-specific UI.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install @open-pencil/vue @open-pencil/core canvaskit-wasm
|
||||
bun add @open-pencil/vue @open-pencil/core canvaskit-wasm
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { createEditor } from '@open-pencil/core/editor'
|
||||
import { OpenPencilProvider, OpenPencilCanvas } from '@open-pencil/vue'
|
||||
import { provideEditor } from '@open-pencil/vue'
|
||||
|
||||
const editor = createEditor({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
})
|
||||
|
||||
const editor = createEditor()
|
||||
editor.createShape('RECTANGLE', 100, 100, 200, 150)
|
||||
editor.zoomToFit()
|
||||
|
||||
provideEditor(editor)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OpenPencilProvider :editor="editor">
|
||||
<OpenPencilCanvas style="width: 100%; height: 100vh" />
|
||||
</OpenPencilProvider>
|
||||
<div class="h-screen">
|
||||
<CanvasRoot v-slot="{ canvasRef }">
|
||||
<canvas ref="canvasRef" class="size-full" />
|
||||
</CanvasRoot>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Components
|
||||
## Core concepts
|
||||
|
||||
All components are **renderless** — they provide data and actions via scoped slots. You control the markup and styling.
|
||||
### Editor context
|
||||
|
||||
### `<OpenPencilProvider>`
|
||||
Use `provideEditor(editor)` once near the top of your subtree.
|
||||
|
||||
Provides the editor instance to all child components via Vue injection.
|
||||
```ts
|
||||
import { provideEditor } from '@open-pencil/vue'
|
||||
|
||||
```vue
|
||||
<OpenPencilProvider :editor="editor">
|
||||
<slot />
|
||||
</OpenPencilProvider>
|
||||
provideEditor(editor)
|
||||
```
|
||||
|
||||
### `<OpenPencilCanvas>`
|
||||
|
||||
Renders the CanvasKit/Skia drawing surface. Handles WebGL/WebGPU setup, resize, and the render loop.
|
||||
|
||||
| Prop | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `showRulers` | `boolean?` | Override ruler visibility |
|
||||
| `preserveDrawingBuffer` | `boolean?` | Keep buffer for screenshots |
|
||||
| `onReady` | `() => void` | Called when surface is ready |
|
||||
|
||||
### `<PageList>`
|
||||
|
||||
Exposes page data and navigation.
|
||||
|
||||
```vue
|
||||
<PageList v-slot="{ pages, currentPageId, switchPage, addPage, deletePage, renamePage }">
|
||||
<div v-for="page in pages" :key="page.id" @click="switchPage(page.id)">
|
||||
{{ page.name }}
|
||||
</div>
|
||||
</PageList>
|
||||
```
|
||||
|
||||
### `<LayerTree>`
|
||||
|
||||
Exposes the flattened layer tree with selection state.
|
||||
|
||||
```vue
|
||||
<LayerTree v-slot="{ layers, selectedIds, select, toggleVisibility, toggleLock }">
|
||||
<div
|
||||
v-for="layer in layers" :key="layer.node.id"
|
||||
:style="{ paddingLeft: layer.depth * 16 + 'px' }"
|
||||
@click="select([layer.node.id])"
|
||||
>
|
||||
{{ layer.node.name }}
|
||||
</div>
|
||||
</LayerTree>
|
||||
```
|
||||
|
||||
### `<ToolSelector>`
|
||||
|
||||
Exposes the active tool and setter.
|
||||
|
||||
```vue
|
||||
<ToolSelector v-slot="{ activeTool, setTool }">
|
||||
<button :class="{ active: activeTool === 'SELECT' }" @click="setTool('SELECT')">Select</button>
|
||||
<button :class="{ active: activeTool === 'RECTANGLE' }" @click="setTool('RECTANGLE')">Rectangle</button>
|
||||
</ToolSelector>
|
||||
```
|
||||
|
||||
### `<NodeProperties>`
|
||||
|
||||
Exposes the selected node and an update function.
|
||||
|
||||
```vue
|
||||
<NodeProperties v-slot="{ node, update }">
|
||||
<div v-if="node">
|
||||
<input :value="node.x" @change="update({ x: +$event.target.value })" />
|
||||
</div>
|
||||
</NodeProperties>
|
||||
```
|
||||
|
||||
## Composables
|
||||
|
||||
### `useEditor()`
|
||||
|
||||
Access the editor instance from any descendant of `<OpenPencilProvider>`.
|
||||
Read it anywhere below with `useEditor()`.
|
||||
|
||||
```ts
|
||||
import { useEditor } from '@open-pencil/vue'
|
||||
|
||||
const editor = useEditor()
|
||||
editor.createShape('ELLIPSE', 0, 0, 100, 100)
|
||||
```
|
||||
|
||||
### `useCanvas(canvasRef, editor, options?)`
|
||||
### Canvas wiring
|
||||
|
||||
Low-level composable for CanvasKit surface lifecycle. Used internally by `<OpenPencilCanvas>` — use directly only if you need custom canvas setup.
|
||||
At the composable level, the main canvas APIs are:
|
||||
|
||||
## Example
|
||||
- `useCanvas()`
|
||||
- `useCanvasInput()`
|
||||
- `useTextEdit()`
|
||||
|
||||
If you want SDK-provided structure, use headless primitives like `CanvasRoot` and `CanvasSurface`.
|
||||
|
||||
### Headless primitives
|
||||
|
||||
Main structural primitives include:
|
||||
|
||||
- `CanvasRoot`
|
||||
- `LayerTreeRoot`
|
||||
- `PageListRoot`
|
||||
- `PropertyListRoot`
|
||||
- `ToolbarRoot`
|
||||
- `ColorPickerRoot`
|
||||
- `FillPickerRoot`
|
||||
- `FontPickerRoot`
|
||||
|
||||
These components coordinate structure and state, but do not impose app styling.
|
||||
|
||||
## Public API tiers
|
||||
|
||||
### Core API
|
||||
|
||||
These are the main APIs most SDK consumers should start with.
|
||||
|
||||
#### Context and canvas
|
||||
|
||||
- `provideEditor()`
|
||||
- `useEditor()`
|
||||
- `useCanvas()`
|
||||
- `useCanvasInput()`
|
||||
- `useTextEdit()`
|
||||
|
||||
#### Selection and commands
|
||||
|
||||
- `useSelectionState()`
|
||||
- `useSelectionCapabilities()`
|
||||
- `useEditorCommands()`
|
||||
- `useMenuModel()`
|
||||
|
||||
#### Property panels
|
||||
|
||||
- `usePosition()`
|
||||
- `useLayout()`
|
||||
- `useAppearance()`
|
||||
- `useTypography()`
|
||||
- `useExport()`
|
||||
- `useFillControls()`
|
||||
- `useStrokeControls()`
|
||||
- `useEffectsControls()`
|
||||
|
||||
#### Variables and navigation
|
||||
|
||||
- `useVariablesEditor()`
|
||||
- `usePageList()`
|
||||
|
||||
#### Headless primitives
|
||||
|
||||
- `CanvasRoot`
|
||||
- `LayerTreeRoot`
|
||||
- `PageListRoot`
|
||||
- `PropertyListRoot`
|
||||
- `ToolbarRoot`
|
||||
|
||||
### Advanced API
|
||||
|
||||
These exports are intentionally public, but they are lower-level or more specialized.
|
||||
|
||||
- `useNodeProps()`
|
||||
- `useSceneComputed()`
|
||||
- `useFillVariableBinding()`
|
||||
- `useFillPicker()`
|
||||
- `useGradientStops()`
|
||||
- `useFontPicker()`
|
||||
- `usePropScrub()`
|
||||
- `useLayerDrag()`
|
||||
- `useInlineRename()`
|
||||
- `useToolbarState()`
|
||||
- `useNodeFontStatus()`
|
||||
- `useCanvasDrop()`
|
||||
- `extractImageFilesFromClipboard()`
|
||||
- `toolCursor()`
|
||||
|
||||
### Primitive context helpers
|
||||
|
||||
These are mostly useful when extending SDK primitives rather than building from top-level composables.
|
||||
|
||||
- `useCanvasContext()`
|
||||
- `useLayerTree()`
|
||||
- `useToolbar()`
|
||||
- `usePropertyList()`
|
||||
- `useScrubInput()`
|
||||
|
||||
## Example patterns
|
||||
|
||||
### Minimal provider component
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { provideEditor } from '@open-pencil/vue'
|
||||
|
||||
import type { Editor } from '@open-pencil/core/editor'
|
||||
|
||||
const props = defineProps<{
|
||||
editor: Editor
|
||||
}>()
|
||||
|
||||
provideEditor(props.editor)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
```
|
||||
|
||||
### Read selection state
|
||||
|
||||
```ts
|
||||
import { useSelectionState } from '@open-pencil/vue'
|
||||
|
||||
const { hasSelection, selectedCount, selectedNode } = useSelectionState()
|
||||
```
|
||||
|
||||
### Build a menu
|
||||
|
||||
```ts
|
||||
import { useMenuModel } from '@open-pencil/vue'
|
||||
|
||||
const { appMenu, canvasMenu } = useMenuModel()
|
||||
```
|
||||
|
||||
### Build a page list
|
||||
|
||||
```vue
|
||||
<PageListRoot v-slot="{ pages, currentPageId, switchPage }">
|
||||
<ul>
|
||||
<li v-for="page in pages" :key="page.id">
|
||||
<button :data-active="page.id === currentPageId" @click="switchPage(page.id)">
|
||||
{{ page.name }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</PageListRoot>
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
For fuller guides and API docs, see the documentation site:
|
||||
|
||||
- `packages/docs/programmable/sdk/`
|
||||
|
||||
## Example app
|
||||
|
||||
Run the included example:
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@
|
|||
import { ref } from 'vue'
|
||||
|
||||
import { useEditor } from '@open-pencil/vue/context/editorContext'
|
||||
import { useCanvas } from '@open-pencil/vue/shared/useCanvas'
|
||||
import { useCanvas, type UseCanvasOptions } from '@open-pencil/vue'
|
||||
import { provideCanvas } from './context'
|
||||
|
||||
import type { UseCanvasOptions } from '@open-pencil/vue/shared/useCanvas'
|
||||
|
||||
const props = withDefaults(defineProps<UseCanvasOptions>(), {
|
||||
showRulers: undefined
|
||||
})
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ import type {
|
|||
DragState
|
||||
} from '@open-pencil/vue/shared/input/types'
|
||||
|
||||
/**
|
||||
* Wires pointer and mouse interaction to an OpenPencil canvas.
|
||||
*
|
||||
* This composable coordinates selection, dragging, resizing, rotation,
|
||||
* panning, drawing tools, scoped hit testing, and text-edit interaction.
|
||||
* It is primarily intended for editor shell components that own the canvas.
|
||||
*/
|
||||
export function useCanvasInput(
|
||||
canvasRef: Ref<HTMLCanvasElement | null>,
|
||||
editor: Editor,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@ import type { Editor } from '@open-pencil/core/editor'
|
|||
|
||||
const CARET_BLINK_MS = 530
|
||||
|
||||
/**
|
||||
* Bridges DOM text input and the editor's canvas text-editing model.
|
||||
*
|
||||
* This composable manages textarea-backed input, IME composition, caret
|
||||
* blinking, keyboard editing behavior, text formatting shortcuts, and syncing
|
||||
* text/style-run updates back into the scene graph.
|
||||
*/
|
||||
export function useTextEdit(canvasRef: Ref<HTMLCanvasElement | null>, store: Editor) {
|
||||
const textareaRef = shallowRef<HTMLTextAreaElement | null>(null)
|
||||
let isComposing = false
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ function gradientCSS(stops: GradientStop[]): string {
|
|||
return stops.map((s) => `${colorToCSS(s.color)} ${s.position * 100}%`).join(', ')
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns category and conversion helpers for a single fill value.
|
||||
*
|
||||
* This composable is useful for fill pickers that switch between solid,
|
||||
* gradient, and image modes while keeping a live fill model in sync.
|
||||
*/
|
||||
export function useFillPicker(fill: Ref<Fill>, onUpdate: (fill: Fill) => void) {
|
||||
const category = computed(() => FILL_CATEGORY[fill.value.type] ?? 'SOLID')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
import { useFilter } from 'reka-ui'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
|
||||
/**
|
||||
* Options for {@link useFontPicker}.
|
||||
*/
|
||||
export interface UseFontPickerOptions {
|
||||
/** Writable model for the selected font family. */
|
||||
modelValue: { value: string }
|
||||
/** Async source for available font families. */
|
||||
listFamilies: () => Promise<string[]>
|
||||
/** Optional callback fired after a family is selected. */
|
||||
onSelect?: (family: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns searchable font-picker state and selection helpers.
|
||||
*/
|
||||
export function useFontPicker(options: UseFontPickerOptions) {
|
||||
const families = ref<string[]>([])
|
||||
const searchTerm = ref('')
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ const DEFAULT_TRANSFORMS: Record<GradientSubtype, GradientTransform> = {
|
|||
GRADIENT_DIAMOND: { m00: 0.5, m01: 0, m02: 0.5, m10: 0, m11: 0.5, m12: 0.5 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns gradient-stop state and mutation helpers for a fill.
|
||||
*
|
||||
* Use this composable for gradient editors that need subtype switching,
|
||||
* active-stop selection, stop dragging, and stop color/opacity editing.
|
||||
*/
|
||||
export function useGradientStops(fill: Ref<Fill>, onUpdate: (fill: Fill) => void) {
|
||||
const activeStopIndex = ref(0)
|
||||
const stops = computed(() => fill.value.gradientStops ?? [])
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@ import { computed } from 'vue'
|
|||
import { useEditor } from '@open-pencil/vue/context/editorContext'
|
||||
import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed'
|
||||
|
||||
/**
|
||||
* Returns reactive page state and page-management actions.
|
||||
*
|
||||
* Use this composable to build page switchers, page lists, or navigation
|
||||
* panels without manually reading the graph in each component.
|
||||
*/
|
||||
export function usePageList() {
|
||||
const editor = useEditor()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ import type { Tool, EditorToolDef } from '@open-pencil/core/editor'
|
|||
|
||||
const CATEGORY_COUNT = 3
|
||||
|
||||
/**
|
||||
* Returns responsive toolbar UI state for mobile category paging.
|
||||
*
|
||||
* This composable is presentation-oriented and complements {@link useToolbar}
|
||||
* when building toolbar shells.
|
||||
*/
|
||||
export function useToolbarState() {
|
||||
const mobileCategory = ref(0)
|
||||
const slideDirection = ref(1)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,18 @@ import { computed, type Component } from 'vue'
|
|||
import { useVariablesDialogState } from './useVariablesDialogState'
|
||||
import { useVariablesTable } from './useVariablesTable'
|
||||
|
||||
/**
|
||||
* Composes variables dialog state, table columns, and TanStack table wiring
|
||||
* into a single higher-level variables editor API.
|
||||
*/
|
||||
export function useVariablesEditor(options: {
|
||||
/** Component used for color variable editing. */
|
||||
colorInput: Component
|
||||
/** Icon map keyed by variable resolved type. */
|
||||
icons: Record<string, Component>
|
||||
/** Fallback icon when no specific icon matches a variable type. */
|
||||
fallbackIcon: Component
|
||||
/** Icon used for destructive remove actions. */
|
||||
deleteIcon: Component
|
||||
}) {
|
||||
const ctx = useVariablesDialogState()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import { useSelectionState } from '@open-pencil/vue/selection/useSelectionState'
|
|||
|
||||
import type { Component, ComputedRef } from 'vue'
|
||||
|
||||
/**
|
||||
* Stable command identifiers exposed by {@link useEditorCommands}.
|
||||
*/
|
||||
export type EditorCommandId =
|
||||
| 'edit.undo'
|
||||
| 'edit.redo'
|
||||
|
|
@ -30,10 +33,17 @@ export type EditorCommandId =
|
|||
| 'view.zoomFit'
|
||||
| 'view.zoomSelection'
|
||||
|
||||
/**
|
||||
* Reactive editor command descriptor.
|
||||
*/
|
||||
export interface EditorCommand {
|
||||
/** Stable command id. */
|
||||
id: EditorCommandId
|
||||
/** Human-readable label for UI. */
|
||||
label: string
|
||||
/** Whether the command can currently run. */
|
||||
enabled: ComputedRef<boolean>
|
||||
/** Executes the command. */
|
||||
run: () => void
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +63,13 @@ export interface EditorCommandMenuSeparator {
|
|||
|
||||
export type EditorCommandMenuEntry = EditorCommandMenuItem | EditorCommandMenuSeparator
|
||||
|
||||
/**
|
||||
* Builds a command-oriented interface on top of the current editor.
|
||||
*
|
||||
* Use this composable when building menus, toolbars, keyboard handlers, or
|
||||
* any other UI that should talk in terms of commands instead of raw editor
|
||||
* method calls.
|
||||
*/
|
||||
export function useEditorCommands() {
|
||||
const editor = useEditor()
|
||||
const selection = useSelectionState()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import { useEditorCommands } from '@open-pencil/vue/commands/useEditorCommands'
|
|||
import { useEditor } from '@open-pencil/vue/context/editorContext'
|
||||
import { useSelectionState } from '@open-pencil/vue/selection/useSelectionState'
|
||||
|
||||
/**
|
||||
* Action entry used by menu models returned from {@link useMenuModel}.
|
||||
*/
|
||||
export interface MenuActionNode {
|
||||
separator?: false
|
||||
label: string
|
||||
|
|
@ -21,6 +24,13 @@ export interface MenuSeparatorNode {
|
|||
|
||||
export type MenuEntry = MenuActionNode | MenuSeparatorNode
|
||||
|
||||
/**
|
||||
* Returns ready-to-render menu models derived from the current editor state.
|
||||
*
|
||||
* This is a higher-level API than {@link useEditorCommands}: it groups
|
||||
* commands into app and canvas menu structures and computes context-sensitive
|
||||
* labels like Hide/Show and Lock/Unlock.
|
||||
*/
|
||||
export function useMenuModel() {
|
||||
const editor = useEditor()
|
||||
const { menuItem: commandMenuItem, otherPages, moveSelectionToPage } = useEditorCommands()
|
||||
|
|
|
|||
|
|
@ -3,12 +3,30 @@ import { inject, provide } from 'vue'
|
|||
import type { Editor } from '@open-pencil/core/editor'
|
||||
import type { InjectionKey } from 'vue'
|
||||
|
||||
/**
|
||||
* Injection key for the current OpenPencil editor instance.
|
||||
*
|
||||
* Most SDK consumers should use {@link provideEditor} and {@link useEditor}
|
||||
* instead of interacting with this symbol directly.
|
||||
*/
|
||||
export const EDITOR_KEY: InjectionKey<Editor> = Symbol('open-pencil-editor')
|
||||
|
||||
/**
|
||||
* Provides an OpenPencil editor instance to the current Vue subtree.
|
||||
*
|
||||
* Call this once near the top of your editor shell so descendant composables
|
||||
* and headless primitives can access the editor with {@link useEditor}.
|
||||
*/
|
||||
export function provideEditor(editor: Editor) {
|
||||
provide(EDITOR_KEY, editor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current injected OpenPencil editor.
|
||||
*
|
||||
* Throws if called outside a subtree where {@link provideEditor} has already
|
||||
* been called.
|
||||
*/
|
||||
export function useEditor(): Editor {
|
||||
const editor = inject(EDITOR_KEY)
|
||||
if (!editor) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ const CORNER_RADIUS_TYPES = new Set([
|
|||
'INSTANCE'
|
||||
])
|
||||
|
||||
/**
|
||||
* Returns appearance-related state and actions for the current selection.
|
||||
*
|
||||
* Use this composable for visibility, opacity, and corner-radius controls in
|
||||
* property panels.
|
||||
*/
|
||||
export function useAppearance() {
|
||||
const editor = useEditor()
|
||||
const { nodes, node, active, isMulti, merged, updateProp, commitProp } = useNodeProps()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@ const EFFECT_LABELS: Record<string, string> = {
|
|||
|
||||
const EFFECT_TYPES = Object.keys(EFFECT_LABELS) as EffectType[]
|
||||
|
||||
/**
|
||||
* Returns effect-editing helpers for property panels.
|
||||
*
|
||||
* This composable manages default effect creation, expanded-row state,
|
||||
* scrub-preview behavior, and effect type/color updates.
|
||||
*/
|
||||
export function useEffectsControls() {
|
||||
const editor = useEditor()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,25 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed'
|
|||
|
||||
import type { ExportFormat } from '@open-pencil/core'
|
||||
|
||||
/**
|
||||
* Single export preset row managed by {@link useExport}.
|
||||
*/
|
||||
interface ExportSetting {
|
||||
/** Export scale multiplier. */
|
||||
scale: number
|
||||
/** Output file format. */
|
||||
format: ExportFormat
|
||||
}
|
||||
|
||||
const SCALES = [0.5, 0.75, 1, 1.5, 2, 3, 4] as const
|
||||
const FORMATS: ExportFormat[] = ['PNG', 'JPG', 'WEBP', 'SVG']
|
||||
|
||||
/**
|
||||
* Returns selection-aware export settings for export panel UIs.
|
||||
*
|
||||
* This composable manages export presets such as scale and format while also
|
||||
* exposing the current export target label derived from the selection.
|
||||
*/
|
||||
export function useExport() {
|
||||
const editor = useEditor()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@ import { DEFAULT_SHAPE_FILL } from '@open-pencil/core'
|
|||
|
||||
import { useFillVariableBinding } from './useFillVariableBinding'
|
||||
|
||||
/**
|
||||
* Returns fill-related panel helpers and a reusable default fill value.
|
||||
*
|
||||
* This composable extends variable-binding behavior with SDK-level defaults for
|
||||
* fill editing UIs.
|
||||
*/
|
||||
export function useFillControls() {
|
||||
const ctx = useFillVariableBinding()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ import { useEditor } from '@open-pencil/vue/context/editorContext'
|
|||
|
||||
import type { Variable } from '@open-pencil/core'
|
||||
|
||||
/**
|
||||
* Returns helpers for binding fill colors to color variables.
|
||||
*
|
||||
* This composable is used by fill editing UIs that need variable search,
|
||||
* binding, and unbinding behavior.
|
||||
*/
|
||||
export function useFillVariableBinding() {
|
||||
const store = useEditor()
|
||||
const colorVariables = computed(() => store.getVariablesByType('COLOR'))
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@ const TRACK_SIZING_OPTIONS: { value: GridTrackSizing; label: string }[] = [
|
|||
{ value: 'AUTO', label: 'Auto' }
|
||||
]
|
||||
|
||||
/**
|
||||
* Returns layout-related state and actions for the current selection.
|
||||
*
|
||||
* Use this composable to build auto-layout and grid panels that need sizing,
|
||||
* padding, alignment, and track editing behavior.
|
||||
*/
|
||||
export function useLayout() {
|
||||
const editor = useEditor()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,20 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed'
|
|||
|
||||
import type { Effect, Fill, SceneNode, Stroke } from '@open-pencil/core'
|
||||
|
||||
/** Sentinel value returned when a property differs across multiple selected nodes. */
|
||||
export const MIXED = Symbol('mixed')
|
||||
|
||||
/** Property value that may either be concrete or mixed across the selection. */
|
||||
export type MixedValue<T> = T | typeof MIXED
|
||||
|
||||
type ArrayItem = Fill | Stroke | Effect | Record<string, unknown>
|
||||
|
||||
/**
|
||||
* Returns shared property-panel helpers for the current selection.
|
||||
*
|
||||
* This composable centralizes mixed-value detection, multi-selection updates,
|
||||
* array-item editing, and commit semantics used by higher-level controls.
|
||||
*/
|
||||
export function useNodeProps() {
|
||||
const store = useEditor()
|
||||
const node = useSceneComputed(() => store.getSelectedNode() ?? null)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed'
|
|||
|
||||
import type { SceneNode } from '@open-pencil/core'
|
||||
|
||||
/**
|
||||
* Returns position-related state and actions for the current selection.
|
||||
*
|
||||
* This composable is designed for property panels that edit x/y, size,
|
||||
* rotation, alignment, flipping, and multi-node transforms.
|
||||
*/
|
||||
export function usePosition() {
|
||||
const editor = useEditor()
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ const DEFAULT_STROKE: Stroke = {
|
|||
align: 'CENTER'
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns stroke-related helpers for property panels.
|
||||
*
|
||||
* This composable provides alignment options, side presets, a default stroke,
|
||||
* and helpers for per-side border weight editing.
|
||||
*/
|
||||
export function useStrokeControls() {
|
||||
const store = useEditor()
|
||||
const sideMenuOpen = ref(false)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,21 @@ const WEIGHTS = Object.entries(FONT_WEIGHT_NAMES).map(([value, label]) => ({
|
|||
label
|
||||
}))
|
||||
|
||||
/**
|
||||
* Options for {@link useTypography}.
|
||||
*/
|
||||
export interface UseTypographyOptions {
|
||||
/**
|
||||
* Optional font loader invoked before changing family or weight.
|
||||
*/
|
||||
loadFont?: (family: string, style: string) => Promise<unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns typography-related state and actions for the current text selection.
|
||||
*
|
||||
* This composable is designed for text property panels and formatting controls.
|
||||
*/
|
||||
export function useTypography(options: UseTypographyOptions = {}) {
|
||||
const editor = useEditor()
|
||||
|
||||
|
|
|
|||
|
|
@ -7,22 +7,35 @@ export type {
|
|||
} from '@open-pencil/core/editor'
|
||||
export { createEditor, EDITOR_TOOLS, TOOL_SHORTCUTS } from '@open-pencil/core/editor'
|
||||
|
||||
/**
|
||||
* Public editor-context API for the Vue SDK.
|
||||
*
|
||||
* These are the primary entry points for making an editor available to a Vue
|
||||
* subtree and reading it back inside composables and headless primitives.
|
||||
*/
|
||||
export { provideEditor, useEditor, EDITOR_KEY } from './context/editorContext'
|
||||
|
||||
/** Canvas and input integration composables. */
|
||||
export { useCanvas } from './shared/useCanvas'
|
||||
export type { UseCanvasOptions } from './shared/useCanvas'
|
||||
export { useCanvasInput } from './Canvas/useCanvasInput'
|
||||
export { useTextEdit } from './Canvas/useTextEdit'
|
||||
export { useCanvasDrop, extractImageFilesFromClipboard } from './Canvas/useCanvasDrop'
|
||||
|
||||
/** Low-level selection, graph, and derived-state helpers. */
|
||||
export { useNodeProps, MIXED } from './controls/useNodeProps'
|
||||
export type { MixedValue } from './controls/useNodeProps'
|
||||
export { useSceneComputed } from './internal/useSceneComputed'
|
||||
export { useSelectionState } from './selection/useSelectionState'
|
||||
export { useSelectionCapabilities } from './selection/useSelectionCapabilities'
|
||||
|
||||
/** Command and menu composition helpers. */
|
||||
export { useEditorCommands } from './commands/useEditorCommands'
|
||||
export type { EditorCommand, EditorCommandId } from './commands/useEditorCommands'
|
||||
export { useMenuModel } from './commands/useMenuModel'
|
||||
export type { MenuEntry } from './commands/useMenuModel'
|
||||
|
||||
/** Miscellaneous editor-shell helpers. */
|
||||
export { useViewportKind } from './viewport/useViewportKind'
|
||||
export { useLayerDrag } from './LayerTree/useLayerDrag'
|
||||
export { useInlineRename } from './shared/useInlineRename'
|
||||
|
|
@ -30,25 +43,30 @@ export { useToolbarState } from './Toolbar/useToolbarState'
|
|||
export { useNodeFontStatus } from './shared/useFontStatus'
|
||||
export { usePropScrub } from './controls/usePropScrub'
|
||||
export { toolCursor } from './internal/toolCursor'
|
||||
|
||||
/** Property-panel composables. */
|
||||
export { usePosition } from './controls/usePosition'
|
||||
export { useLayout } from './controls/useLayout'
|
||||
export { useAppearance } from './controls/useAppearance'
|
||||
export { useTypography } from './controls/useTypography'
|
||||
export type { UseTypographyOptions } from './controls/useTypography'
|
||||
export { useExport } from './controls/useExport'
|
||||
export { useFillControls } from './controls/useFillControls'
|
||||
export { useFillVariableBinding } from './controls/useFillVariableBinding'
|
||||
export { useEffectsControls } from './controls/useEffectsControls'
|
||||
export { useStrokeControls } from './controls/useStrokeControls'
|
||||
|
||||
/** Variables, page navigation, and picker helpers. */
|
||||
export { useVariables } from './VariablesEditor/useVariables'
|
||||
export { useVariablesDialogState } from './VariablesEditor/useVariablesDialogState'
|
||||
export { useVariablesEditor } from './VariablesEditor/useVariablesEditor'
|
||||
export { useVariablesTable } from './VariablesEditor/useVariablesTable'
|
||||
export { useFillControls } from './controls/useFillControls'
|
||||
export { useFillPicker } from './FillPicker/useFillPicker'
|
||||
export { useFillVariableBinding } from './controls/useFillVariableBinding'
|
||||
export { useEffectsControls } from './controls/useEffectsControls'
|
||||
export { useGradientStops } from './GradientEditor/useGradientStops'
|
||||
export { useStrokeControls } from './controls/useStrokeControls'
|
||||
export { usePageList } from './PageList/usePageList'
|
||||
export { useFillPicker } from './FillPicker/useFillPicker'
|
||||
export { useGradientStops } from './GradientEditor/useGradientStops'
|
||||
export { useFontPicker } from './FontPicker/useFontPicker'
|
||||
|
||||
/** Headless structural primitives and their local contexts. */
|
||||
export { CanvasRoot, CanvasSurface, useCanvasContext } from './Canvas'
|
||||
export type { CanvasContext } from './Canvas'
|
||||
export { ColorInputRoot, ColorPickerRoot } from './ColorPicker'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { computed, type ComputedRef } from 'vue'
|
||||
|
||||
/**
|
||||
* Convenience wrapper for scene-derived computed state.
|
||||
*
|
||||
* Use this for values that should clearly read as editor/scene-backed derived
|
||||
* state in higher-level composables.
|
||||
*/
|
||||
export function useSceneComputed<T>(fn: () => T): ComputedRef<T> {
|
||||
return computed(fn)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@ import { computed } from 'vue'
|
|||
import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed'
|
||||
import { useSelectionState } from '@open-pencil/vue/selection/useSelectionState'
|
||||
|
||||
/**
|
||||
* Returns reactive booleans describing which selection-dependent actions are
|
||||
* currently available.
|
||||
*
|
||||
* This is useful for menus, toolbars, shortcuts, and action buttons that need
|
||||
* command-friendly capability checks.
|
||||
*/
|
||||
export function useSelectionCapabilities() {
|
||||
const {
|
||||
editor,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ import { useSceneComputed } from '@open-pencil/vue/internal/useSceneComputed'
|
|||
|
||||
import type { SceneNode } from '@open-pencil/core'
|
||||
|
||||
/**
|
||||
* Returns reactive selection-derived state for the current editor.
|
||||
*
|
||||
* Use this composable to drive UI from the current selection without manually
|
||||
* reading graph state in every component.
|
||||
*/
|
||||
export function useSelectionState() {
|
||||
const editor = useEditor()
|
||||
|
||||
|
|
|
|||
|
|
@ -39,12 +39,36 @@ async function initWebGPU(ck: CanvasKit): Promise<WebGPUContext | null> {
|
|||
return { device, deviceContext }
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for {@link useCanvas}.
|
||||
*/
|
||||
export interface UseCanvasOptions {
|
||||
/**
|
||||
* Forces ruler visibility on or off for this canvas.
|
||||
*
|
||||
* When omitted, the composable falls back to viewport and URL-param logic.
|
||||
*/
|
||||
showRulers?: boolean
|
||||
/**
|
||||
* Keeps the drawing buffer after presenting frames.
|
||||
*
|
||||
* Useful for screenshot or pixel-readback workflows, but may increase memory
|
||||
* usage depending on the browser and GPU backend.
|
||||
*/
|
||||
preserveDrawingBuffer?: boolean
|
||||
/**
|
||||
* Called once the rendering surface is ready.
|
||||
*/
|
||||
onReady?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects an OpenPencil editor to a real canvas element using CanvasKit.
|
||||
*
|
||||
* This composable owns renderer creation, surface recreation on resize,
|
||||
* render scheduling, and renderer-backed hit testing helpers used by higher-
|
||||
* level canvas interaction code.
|
||||
*/
|
||||
export function useCanvas(
|
||||
canvasRef: Ref<HTMLCanvasElement | null>,
|
||||
editor: Editor,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ import { isFontLoaded, DEFAULT_FONT_FAMILY } from '@open-pencil/core'
|
|||
|
||||
import type { SceneNode } from '@open-pencil/core'
|
||||
|
||||
/**
|
||||
* Returns missing-font information for a text node getter.
|
||||
*
|
||||
* This is useful for typography panels and warnings that need to surface fonts
|
||||
* that are referenced by a node but not yet loaded in the current runtime.
|
||||
*/
|
||||
export function useNodeFontStatus(node: () => SceneNode | null | undefined) {
|
||||
const missingFonts = computed(() => {
|
||||
const n = node()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ import { computed } from 'vue'
|
|||
|
||||
const breakpoints = useBreakpoints({ mobile: 768 })
|
||||
|
||||
/**
|
||||
* Returns coarse viewport kind flags used by responsive editor UI.
|
||||
*/
|
||||
export function useViewportKind() {
|
||||
const isMobile = breakpoints.smaller('mobile')
|
||||
const isDesktop = computed(() => !isMobile.value)
|
||||
|
|
|
|||
Loading…
Reference in a new issue