Editor Demo
Below you can see a working demo of the UI Editor.
You can drag components from the blueprints panel onto the canvas or reaarange them via drag and drop within the canvas itself.
Click components to see their properties in the component inspector.
The tree panel shows you an overview of components in the canvas where you can also hide or show individual components.
Double clicking any text will enter in place editing.
Show editor content
Show code
<template>
<CraftEditor
:config="config"
:inheritStyles="true"
:useIframe="true"
@iframe-load="onIframeLoad"
>
<CraftCanvas componentName="div" />
</CraftEditor>
<div class="editor-switch">
<label for="editorEnabled">Preview Content: </label>
<input
id="editorEnabled"
type="checkbox"
name="editorEnabled"
v-model="previewContent"
/>
<div class="text-sm">(disables editor drag and drop)</div>
</div>
</template>
<script lang="ts" setup>
import { CraftEditorConfig, useEditor } from "@versa-stack/v-craft";
import blueprintsLibrary from "./blueprints";
import { resolverMap } from "./resolvermap";
import { demoContent } from "./demo-content";
import { onBeforeMount, ref, watch } from "vue";
const editor = useEditor();
onBeforeMount(() => {
if (!editor.hasNodes) {
editor.setNodes(demoContent);
}
});
const previewContent = ref(false);
if (!previewContent.value) {
editor.enable();
}
watch(
() => previewContent.value,
(disabled) => {
if (!disabled) {
editor.enable();
}
if (disabled) {
editor.disable();
}
}
);
const config: CraftEditorConfig = {
blueprintsLibrary,
resolverMap,
};
const onIframeLoad = (iframe: HTMLIFrameElement) => {
const syncDarkMode = () => {
const isDark = document.documentElement.classList.contains('dark');
const iframeHtml = iframe.contentDocument?.documentElement;
if (iframeHtml) {
if (isDark) {
iframeHtml.classList.add('dark');
} else {
iframeHtml.classList.remove('dark');
}
iframeHtml.style.colorScheme = isDark ? 'dark' : 'light';
}
};
// Wait for iframe content to be fully loaded
const checkAndSync = () => {
if (iframe.contentDocument?.documentElement) {
syncDarkMode();
} else {
setTimeout(checkAndSync, 10);
}
};
checkAndSync();
const observer = new MutationObserver(syncDarkMode);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
});
};
</script>
<style lang="scss" scoped>
.editor-switch {
display: inline-block;
}
</style>Resolvers and Blueprints
Resolvers and Blueprints are essential concepts in the v-craft editor, enabling component management and preset layouts. They enhance the editor's functionality by defining available components and providing pre-configured component structures.
Resolver Maps
Resolver maps inform the editor about available components and their properties.
Purpose
- Define existing components for the editor
- Specify component events and properties
import {
CraftNodeResolverMap,
defaultResolvers,
} from "@versa-stack/v-craft";
import CraftContainerExample from "../../CraftContainerExample.vue";
import CraftContainerSingleSlot from "../../CraftContainerSingleSlot.vue";
const resolveHtmlElements = (elements: string[]) => {
const mapped: Record<string, any> = {};
elements.forEach((element) => {
mapped[element] = {
componentName: element,
eventsSchema: {
$el: "div",
children: [
{
$formkit: "textarea",
name: "click",
label: "onClick",
},
],
},
propsSchema: [
{
$formkit: "text",
label: "CSS Class(es)",
name: "class",
},
],
};
});
return mapped;
};
export const htmlResolvers = {
...resolveHtmlElements([
"article",
"aside",
"details",
"div",
"figure",
"footer",
"header",
"li",
"main",
"nav",
"ol",
"section",
"ul",
]),
};
export const resolverMap: CraftNodeResolverMap = {
...defaultResolvers,
...htmlResolvers,
CraftContainerExample: {
componentName: "CraftContainerExample",
slots: ["header", "body"],
},
CraftContainerSingleSlot: {
componentName: "CraftContainerSingleSlot",
},
CraftCanvas: {
componentName: "CraftCanvas",
},
};Blueprints
Blueprints describe preset component trees that can be added to the page layout via drag-and-drop.
Purpose
- Create reusable component structures
- Enable quick addition of complex layouts
import {
Blueprints,
BlueprintsLibrary,
CraftNodeResolverMap,
defaultBlueprints,
} from "@versa-stack/v-craft";
import { htmlResolvers } from "./resolvermap";
const createHtmlElementBlueprints = () => {
const resolverMap: CraftNodeResolverMap<any> = htmlResolvers;
const blueprints: Blueprints<any> = {};
Object.entries(resolverMap).forEach(([key, value]) => {
blueprints[key] = {
label: `HTML <${value.componentName}>`,
componentName: "CraftCanvas",
props: {
...value.defaultProps,
componentName: value.componentName,
},
};
});
return blueprints;
};
export default {
groups: [
defaultBlueprints,
{
metadata: {
name: "html-elements",
},
label: "HTML Elements",
blueprints: createHtmlElementBlueprints(),
},
{
metadata: {
name: "examples",
},
label: "Examples",
blueprints: {
CraftContainerExample: {
label: "Container (2 Slots)",
componentName: "CraftCanvas",
props: {
componentName: "CraftContainerExample",
},
},
CraftContainerSingleSlot: {
label: "Container (1 Slot)",
componentName: "CraftCanvas",
props: {
componentName: "CraftContainerSingleSlot",
},
},
},
},
],
} as BlueprintsLibrary;