2) Open terminal, login to aio and create an app. While going through the prompts make sure you select the Universal Editor Extension Template and Add a Custom Renderer...
> aio login
> aio app init eaem-ue-html-container
3) Add the following code in eaem-ue-html-container\src\universal-editor-ui-1\web-src\src\components\ExtensionRegistration.js for a custom field/renderer implementation
import { Text } from "@adobe/react-spectrum";
import { register } from "@adobe/uix-guest";
import { extensionId } from "./Constants";
import metadata from '../../../../app-metadata.json';
function ExtensionRegistration() {
const init = async () => {
const guestConnection = await register({
id: extensionId,
metadata,
methods: {
canvas: {
getRenderers() {
return [
{
extension: extensionId,
dataType: 'eaem:html-container',
url: '/index.html#/eaem-html-container'
}
];
},
},
},
});
};
init().catch(console.error);
return <Text>IFrame for integration with Host (AEM)...</Text>
}
export default ExtensionRegistration;
4) Add the following code in src\universal-editor-ui-1\web-src\src\components\HtmlContainerField.js which has the main extension code for showing a TextArea to add HTML, convert to base64 while saving...
import React, { useState, useEffect } from 'react'
import { attach } from "@adobe/uix-guest"
import {
Provider,
defaultTheme,
View,
Flex,
TextArea,
Text,
Button
} from '@adobe/react-spectrum'
import { extensionId } from "./Constants"
/**
* Encodes a UTF-8 string to base64.
*/
const encodeToBase64 = (value) => {
if (!value) return '';
return window.btoa(unescape(encodeURIComponent(value)));
}
const decodeFromBase64 = (value) => {
if (!value) return '';
try {
return decodeURIComponent(escape(window.atob(value)));
} catch (e) {
return value;
}
}
export default function HtmlContainerField () {
const [guestConnection, setGuestConnection] = useState()
const [htmlValue, setHtmlValue] = useState('');
const [label, setLabel] = useState('HTML Code');
const [loading, setLoading] = useState(true);
useEffect(() => {
(async () => {
const connection = await attach({ id: extensionId })
setGuestConnection(connection);
const model = await connection.host.field.getModel();
if (model.label) {
setLabel(model.label);
}
const storedValue = await connection.host.field.getValue() || '';
setHtmlValue(decodeFromBase64(storedValue));
setLoading(false);
document.body.style.height = '650px';
})()
}, [])
const handleChange = (newValue) => {
setHtmlValue(newValue);
guestConnection?.host.field.onChange(encodeToBase64(newValue));
}
const openExpandedEditor = () => {
const width = Math.min(1400, window.screen.availWidth - 40);
const height = Math.min(1000, window.screen.availHeight - 40);
const left = (window.screen.availWidth - width) / 2;
const top = (window.screen.availHeight - height) / 2;
window.eaemHtmlContainerBridge = {
label,
value: htmlValue,
onChange: handleChange,
};
const popup = window.open(
`${window.location.origin}${window.location.pathname}#/eaem-html-container-editor`,
'eaemHtmlContainerEditor',
`width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`
);
if (!popup) {
// eslint-disable-next-line no-alert
alert('Please allow pop-ups for this site to expand the editor.');
}
}
return (
<Provider theme={defaultTheme} colorScheme='dark' height='100vh'>
<View padding='size-200' UNSAFE_style={{ overflow: 'hidden' }}>
<Flex direction="column" gap="size-100">
<TextArea
label={label}
value={htmlValue}
onChange={handleChange}
isDisabled={loading}
width="100%"
height="500px"
placeholder="Enter HTML code..."
/>
<Flex justifyContent="end">
<Button variant="secondary" isDisabled={loading} onPress={openExpandedEditor}>Expand</Button>
</Flex>
</Flex>
<Text UNSAFE_style={{ display: 'block', marginTop: '8px', fontSize: '11px', opacity: 0.7 }}>
Content is stored as base64-encoded HTML.
</Text>
</View>
</Provider>
)
}
5) Add the following code in src\universal-editor-ui-1\web-src\src\components\HtmlContainerModalEditor.js for expanded mode
import React, { useState } from 'react'
import {
Provider,
defaultTheme,
Flex,
View,
TextArea,
Heading,
Button,
Text
} from '@adobe/react-spectrum'
export default function HtmlContainerModalEditor() {
const bridge = window.opener && window.opener.eaemHtmlContainerBridge;
const [value, setValue] = useState(bridge ? bridge.value : '');
if (!bridge) {
return (
<Provider theme={defaultTheme} colorScheme="dark" height="100vh">
<View padding="size-300">
<Text>Unable to connect to the field editor. Please close this window and try again.</Text>
</View>
</Provider>
)
}
const handleChange = (newValue) => {
setValue(newValue);
bridge.onChange(newValue);
}
return (
<Provider theme={defaultTheme} colorScheme="dark" height="100vh">
<Flex direction="column" height="100vh">
<View paddingX="size-300" paddingTop="size-200" paddingBottom="size-100">
<Flex justifyContent="space-between" alignItems="center">
<Heading level={3} margin="size-0">{bridge.label}</Heading>
<Button variant="cta" onPress={() => window.close()}>Done</Button>
</Flex>
</View>
<View flexGrow={1} paddingX="size-300" paddingBottom="size-300">
<TextArea
aria-label={bridge.label}
value={value}
onChange={handleChange}
width="100%"
height="100%"
/>
</View>
</Flex>
</Provider>
)
}
6) A sample Block Model eg. blocks\eaem-html-container\_eaem-html-container.json
{
"definitions": [
{
"title": "EAEM HTML Container",
"id": "eaem-html-container",
"plugins": {
"xwalk": {
"page": {
"resourceType": "core/franklin/components/block/v1/block",
"template": {
"name": "EAEM HTML Container",
"model": "eaem-html-container"
}
}
}
}
}
],
"models": [
{
"id": "eaem-html-container",
"fields": [
{
"component": "text",
"name": "containerName",
"label": "Container Name",
"value": "",
"valueType": "string"
},
{
"component": "eaem:html-container",
"name": "containerHtml",
"label": "Container HTML",
"value": "",
"valueType": "string"
}
]
}
],
"filters": []
}
7) Block rendering eg. blocks\eaem-html-container\eaem-html-container.js can have the following code to convert base64 encoded html back to rendered html...
function decodeBase64(value) {
if (!value) return '';
try {
return decodeURIComponent(escape(window.atob(value)));
} catch (e) {
return value;
}
}
export default function decorate(block) {
const [nameRow, htmlRow] = [...block.children];
const containerName = nameRow?.textContent?.trim() || '';
if (containerName) {
block.dataset.containerName = containerName;
}
const encodedHtml = htmlRow?.textContent?.trim() || '';
const decodedHtml = decodeBase64(encodedHtml);
block.innerHTML = decodedHtml;
}
No comments:
Post a Comment