Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select Block from iframe and its config appear in sidebar #51

Merged
merged 20 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,69 @@ If you wish to make the editing experience faster you can register for ```onSave

#### Enable Show changes while editing

You will need to subscribe to an ```onEditChange``` event that will send blocks or metadata changes
You will need to subscribe to an ```onEditChange``` event that will send blocks or metadata changes.

TODO: not implemented yet.
The `onEditChange` method listens for changes in the Hydra and triggers a callback with updated data or the initial data if no update is available. It takes following args:
| Args | Description |
| :-----------:| :-------|
| *initialData*| The initial data to fall back on if no updated data is received. |
| *callback* | A function to call with the updated data when a change is detected. |

Usage:
```js
// the initial data (from ploneClient)
const initialData = data;

// Define the callback function
function handleEditChange(updatedData) {
console.log('Updated data:', updatedData);
}

// Set up the onEditChange listener
onEditChange(initialData, handleEditChange);
```

#### Enable Managing Blocks directly on your frontend

You will add data attributes to your rendered block html so hydra knows where they are on the page and it
will automatically handle click events and show a quanta toolbar when selecting a block.

TODO: not implemented yet
Add the `data-block-uid={<<BLOCK_UID>>}` attribute to your outer most container of the rendered block html.
The `data-block-uid` requires the block's UID, which you need to provide in the outermost container of the block.

For example, if you are using ploneClient to fetch `data`, it will be `data.blocks_layout.items[x]`.
Now, Click on your blocks in iframe and the sidebar will show its settings.

Usage:
```js
// Vanilla JS example to render Blocks

// Function to create the block list
function createBlockList(data) {
const blockList = document.createElement('ul');

data.blocks_layout.items.forEach(id => {
if (data.blocks[id]["@type"] === "slate") {
const slateValue = data.blocks[id].value;
const listItem = document.createElement('li');
listItem.className = 'blog-list-item';
listItem.setAttribute('data-block-uid', id); // Set Attribute to enable Clicking on Blocks

const pre = document.createElement('pre');
pre.className = 'pre-block';
pre.textContent = JSON.stringify(slateValue, null, 2);

listItem.appendChild(pre);
blockList.appendChild(listItem);
}
});

document.body.appendChild(blockList);
}

// Call the function to render the blocks
createBlockList(data);
```
#### Enable Editing blocks inplace

You will add data attributes to where a blocks text is editable and subscribe to ```onBlockFieldChanged``` events to handle fine grained
Expand Down
43 changes: 42 additions & 1 deletion hydra.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,21 @@ class Bridge {
}
}
});
this.enableBlockClickListener();
}
onEditChange(initialData, callback) {
window.addEventListener('message', (event) => {
if (event.origin === this.adminOrigin) {
if (event.data.type === 'FORM') {
if (event.data.data) {
callback(event.data.data);
} else {
callback(initialData);
}
}
}
});
}

async get_token() {
if (this.token !== null) {
return this.token;
Expand Down Expand Up @@ -102,6 +115,18 @@ class Bridge {
}
return null;
}
enableBlockClickListener() {
document.addEventListener('click', (event) => {
const blockElement = event.target.closest('[data-block-uid]');
if (blockElement) {
const blockUid = blockElement.getAttribute('data-block-uid');
window.parent.postMessage(
{ type: 'OPEN_SETTINGS', uid: blockUid },
this.adminOrigin,
);
}
});
}
JeffersonBledsoe marked this conversation as resolved.
Show resolved Hide resolved
}

// Export an instance of the Bridge class
Expand Down Expand Up @@ -130,3 +155,19 @@ export async function getToken() {
}
return '';
}
/**
* Enable the frontend to listen for changes in the admin and call the callback with updated data
* @param {*} initialData
* @param {*} callback
*/
export function onEditChange(initialData, callback) {
if (bridgeInstance) {
bridgeInstance.onEditChange(initialData, callback);
}
}

export function enableBlockClickListener() {
if (bridgeInstance) {
bridgeInstance.enableBlockClickListener();
}
}
8 changes: 8 additions & 0 deletions packages/volto-hydra/src/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { SET_SELECTED_BLOCK } from './constants';

export function setSelectedBlock(uid) {
return {
type: SET_SELECTED_BLOCK,
uid: uid,
};
}
46 changes: 30 additions & 16 deletions packages/volto-hydra/src/components/Iframe/View.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
import React, { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
import Cookies from 'js-cookie';
import './styles.css';
import { setSelectedBlock } from '../../actions';

const Iframe = () => {
const [url, setUrl] = useState('');

const [src, setSrc] = useState('');
const dispatch = useDispatch();
const history = useHistory();
const token = useSelector((state) => state.userSession.token);

const form = useSelector((state) => state.form.global);
const getDefualtUrlFromEnv = () =>
process.env['RAZZLE_DEFAULT_IFRAME_URL'] ||
(typeof window !== 'undefined' && window.env['RAZZLE_DEFAULT_IFRAME_URL']);

useEffect(() => {
const defaultUrl = getDefualtUrlFromEnv() || 'http://localhost:3002'; // fallback if env is not set
const savedUrl = Cookies.get('iframe_url');
const initialUrl = savedUrl
? `${savedUrl}${window.location.pathname.replace('/edit', '')}`
: `${defaultUrl}${window.location.pathname.replace('/edit', '')}`;

setUrl(initialUrl);
setSrc(initialUrl);
const defaultUrl = getDefualtUrlFromEnv() || 'http://localhost:3002'; // fallback if env is not set
const savedUrl = Cookies.get('iframe_url');
const initialUrl = savedUrl
? `${savedUrl}${history.location.pathname.replace('/edit', '')}`
: `${defaultUrl}${history.location.pathname.replace('/edit', '')}`;
const [url, setUrl] = useState(initialUrl);
const [src, setSrc] = useState(initialUrl);

useEffect(() => {
// Listen for messages from the iframe
const initialUrlOrigin = new URL(initialUrl).origin;
window.addEventListener('message', (event) => {
Expand All @@ -45,21 +43,37 @@ const Iframe = () => {
);
break;

case 'OPEN_SETTINGS':
if (history.location.pathname.endsWith('/edit')) {
dispatch(setSelectedBlock(event.data.uid));
}
break;

default:
break;
}
});
}, [token]);

useEffect(() => {
if (typeof window !== 'undefined') {
// Send the form data to the iframe
const origin = new URL(initialUrl).origin;
document
.getElementById('previewIframe')
.contentWindow.postMessage({ type: 'FORM', data: form }, origin);
}
}, [form]);

const handleUrlChange = (event) => {
setUrl(event.target.value);
};

const handleNavigateToUrl = (givenUrl = '') => {
// Update adminUI URL with the new URL
const formattedUrl = givenUrl ? new URL(givenUrl) : new URL(url);
const newUrl = formattedUrl.href;
setSrc(newUrl);
// const newUrl = formattedUrl.href;
// setSrc(newUrl);
const newOrigin = formattedUrl.origin;
Cookies.set('iframe_url', newOrigin, { expires: 7 });

Expand Down
1 change: 1 addition & 0 deletions packages/volto-hydra/src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SET_SELECTED_BLOCK = 'SET_SELECTED_BLOCK';
Loading