Open xaviemirmon opened 11 months ago
I don't think this would be that difficult at this point, and the API would probably be similar to DropZones. We'd also probably use context
render: () => <p><InlineField name="title" /></p>
One question I would have is whether we retain the prop for the field. For example
fields: { title: { type: "text"} },
render: () => <p><InlineField name="title" /></p>
I guess it should probably be optional, but the inline field data should still be stored as a prop regardless.
Would this work with other interactive components (for example custom contentEditable
or some markdown editor)?
I might have a go at tackling this next as it's something I'm quite keen to see. I've implemented an auto-resizing inline text editing input before. Are we interested in supporting markdown? We could look to leverage something like: https://github.com/facebook/lexical
@4leite I was thinking of a plain text setup when I raised this. If we expand it to an RTE then we should probably use the same one as agreed upon for #149
I'm a little undecided over whether we want this, or if it would just detract from the field API. Playing around with a demo might help, if anyone's happy to put one together.
I would start with inline editing without MD or something, but would support just making the component clickable and interactive (so that you can plug in something like Lexical).
Advanced editors can be build on top of Custom Input for now.
I was thinking something like this https://craft.js.org/
100% @4leite, that was the example I had in mind too!
Took me a while to get back to this.
"use client";
import { usePuck } from "@measured/puck";
import { SerializedEditorState } from "lexical";
import { LexicalClient } from "./client";
const useSelected = (componentId: string) => {
const {
appState: {
ui: { itemSelector },
data,
},
dispatch,
} = usePuck();
if (!itemSelector) {
return {
isSelected: false,
onChange: () => {},
};
}
const item =
itemSelector.zone && itemSelector.zone !== "default-zone"
? data.zones[itemSelector.zone][itemSelector.index]
: data.content[itemSelector.index];
if (item.props.id !== componentId) {
return {
isSelected: false,
onChange: () => {},
};
}
return {
isSelected: true,
onChange: (props: Partial<typeof item.props>) =>
dispatch({
type: "replace",
destinationIndex: itemSelector.index,
destinationZone: itemSelector.zone,
data: {
props: { ...item.props, ...props },
type: item.type,
},
}),
};
};
export function InlineEditor({
id,
state,
}: {
id: string;
state: SerializedEditorState;
}) {
const { isSelected, onChange } = useSelected(id);
return (
<div
style={{
cursor: isSelected ? "default" : "grab",
pointerEvents: "auto",
}}
>
<LexicalClient
id={id}
state={state}
onChange={onChange}
editing={isSelected}
/>
</div>
);
}
This is brittle because we are effectively re-implementing the internal "getItem" code, which could change. @chrisvxd We could better support this in core by either: (a) exporting the above useSelected() hook, which would mean we could use the internal getItem functions and change it if we change the datastructure. (b) export the existing "selectedItem" object (which uses getItem internally) and expect users to check if their component is selected.
I'll link a working demo, with the lexical code included shortly, but the above should work with any contentEditable based or similar solution.
edit: https://inline-rich-text.vercel.app/ https://github.com/4leite/inline-rich-text
updated the above and raised a PR to support targeting the component toolbar in core: https://github.com/measuredco/puck/pull/360
What could be done if we want to have different selectable/editable text inside the same react component/block?
How could we reuse what you did @4leite?
@GoMino If what you want is a more portable version of the lexical implementation I used, you are welcome to raise a feature request here: https://github.com/Tohuhono/Oberon/issues
However, at the moment I can't see a way to have multiple editable areas in the same puck component. I did have a go when I first did this PoC.
There were two issues, one was setting the individual fields from in the component - I believe this is solved now with newer version of puck.
The other issue was to do with how puck components interact with focus. I couldn't find a way to have the focus for each editable area work without janky or weird select / cursor behavior. You would have to find a way to solve that.
I don't have a focus issue at the moment, but I don't know how to handle separate states for each editable text field in the same component, it seems like it should somehow be possible with lexical @4leite.
Or maybe using another editor like slate ?
Another feature related to #149 a bit is supporting inline editing of text on components.
@chrisvxd I'm sure there are many considerations when working with beautiful-react-dnd, but I'm adding an issue so it's on the radar.
I'd love to have the ability to edit the text on my component inline from the main canvas, and if it was rich text have the ability to change the text style inline.