Detecting which node is selected within a slate-react text editor - reactjs

I am currently working on a slate text editor where the user can add images and text. I would also like to have a hovering toolbar which serves different buttons depending on what type of element the user has selected.
For example if the user has selected an image then I would like to serve one set of buttons. If the user has selected a paragraph I would like to serve another set of buttons.
After looking through the examples found here:
https://www.slatejs.org/examples/richtext
I have pieced together a rough example of my desired text editor without the context dependant hovering toolbar buttons:
https://codesandbox.io/s/suspicious-pine-lrxgw
But I am struggling to work out how to detect what type of element is selected within the editor? I don't know if there is a way to do this using slate-react? Or even in vanilla JS?
Ideally I would also be able to get other information about the element as well. For example the images height and width as this would help with styling.
Any help appreciated

You can get the currently focused node by using the selection property from the editor and which is a Range. You then use the anchor or focus to select the currently selected children.
anchor & focus are Points which are basically an array the first item is the current block and the second item indicates the position in the block.
Point objects refer to a specific location in a text node in a Slate
document. Its path refers to the location of the node in the tree, and
its offset refers to distance into the node's string of text. Points
may only refer to Text nodes.
using the selection array we can get the current selected block like so
if (selection !== null && selection.anchor !== null) {
selected = editor.children[selection.anchor.path[0]];
} else {
selected = null;
}
Later in your render function you can have a check to render what you want.

Here, path comes from selection.anchor.path, which I noticed will walk into leaves down to a final text node (I want the cell of my table, not the text), same with paragraphs. I want the full { type: 'paragraph', children: [...text node] }, not just the text. So, I pop the end off and reduce.
const pathClone = [...path];
pathClone.pop(); // get rid of trailing text node postion in path.
const focusedNode = pathClone.reduce((node, pathPosition) => {
if (!node) return editor.children[pathPosition];
return node.children[pathPosition];
}, null);
// console.log('last node type before text node: ', focusedNode.type, focusedNode);

Related

Push value from input to object array and update tab names labels- Angular

I have a reactive form and I am using Material Angular, with a tab group and the tab names comes from an array, I need to add the possibility of changing those tabs names for new ones. If a write a new title in the input I need to update the name of the tab label.
I’m getting the value from the input, but I can’t figure it out how to send it to the names of the tabs:
app.component.ts
this.newTitleArm = selection.formdata.titleTextArms;
console.log(this.newTitleArm);
STACKBLITZ
SCREENSHOT
Can you have a look at this Stackblits
Note that I have modified the submit method and have added
if(this.form.get('titleArms').value === 'changeArm'){
this.tabs[this.selected.value].name = this.form.get('titleTextArms').value;
}
Here I'm assigning the titleTextArms control value to selected tab name. You can get selected tab by this.tabs[this.selected.value]
Also added the if condition to check selection of radio button.
UPDATE:
since you have used tab names to create the text in the parent component.
In the submit method
need to do the tab name update before emitting the updateDataEvent.
submit(): void {
if (this.form.get('titleArms').value === 'changeArm') {
if (this.form.get('titleTextArms').value !== '') {
this.tabs[this.selected.value].name =
this.form.get('titleTextArms').value;
}
}
this.updateDataEvent.emit({
formdata: this.form.getRawValue(),
tabs: this.tabs,
});
}
The problem is that you are not updating the respective tab name anywhere, neither in your submit, nor in your updateData.
You submit the value, but you are not updating the tabs.
For example, in your submit method add this hard-coded update and then press the Save button:
this.tabs[0].name = "test";
Because now you updated the first tab's name, you should see the change.

Load specific icons in tree.Panel depending on node type

I am begining in Extjs and i would like to make a tree (with tree.Panel) which contents both file and folder.
Moreover I would like to display specific icons for each type of node : a file must not have a folder child so the icon "create folder" which must only appear on folder nodes.
I already have a tree with a boutton "delete" which exists for both types.
I presume I must listen for a event like "renderer" and ask for the type of my node and load the corresponding icon but I don't know how to do : the event comes from a different column that the tree column.
How can I get the node type (leaf:true/false) from the actionColumn which will content the icon?
Please elaborate your question.
If it is related to getting the node type(leaf or not) all you have to do is to add a renderer function to your column as below
{
text: 'View in Filters',
flex: 2,
renderer: function (value, metaData, record) {
if(record.data.leaf){
//Do your stuff
}
else {
//Do something else.
}
}
Or if it is related to showing different icons for parent and children check this once
http://stackoverflow.com/questions/17631880/extjs-treepanel-css-to-change-default-icons-node-and-leaf
Hope this helps...

Issue with Nested List when data does not contain 'text' node

I want to render Nested list such that when I drill down back button shows name of parent node. I tried it but looks like if the JSON I am getting doesn't have node with name text, it won't work.
Here is the fiddle with local data (in my app I am using store to fetch this from service so I don't have control over name of nodes)
Please note back button where title is not populated when you go down the tree.
There is this displayField config of NestedList where you can specify which field to be used to set title and item text . By default it is set to text but you can specify it to be one of your model field.
Here's working fiddle based on your example. Only change is in config of NestedList at bottom.
But, if you are overriding getItemTextTpl or getTitleTextTpl, this config will be ignored.

Sencha Touch 1.1: Setting Panel properties after selecting item in List

I'm using Sencha Touch 1.1. I have a Ext.List and when the user selects an item, I switch to the details page using
rootPanel.setActiveItem('details');
That works fine. Now I want to get the details of the selected item and populate properties in the 'details' panel. How do I do that?
At the moment I'm passing the record around to the various panels of the details page by using this code:
onItemDisclosure: function (record) {
// user has selected a Country from the Country List.
// TODO: There must be a better way to pass the data around
CountryDetailsToolbar.setTitle(record.data.title);
var upperData = { upper: record.data.title.toUpperCase()};
CountryDetailsHeaderLeft.update(upperData);
CountryDetailsHeaderMonth.update(record.data);
viewport.setActiveItem('CountryDetailsCarousel');
}
This seems a bit messy to me. Is there a cleaner way to set titles and update panels with the data "record"?
you can use rootPanel.getActiveItem() to get instance of currently selected panel, i am not sure about what do you mean by populate properties in the details tab
Update:
i think there isn't any problem in passing record data between panels, i think that is the most efficient way
onItemDisclosure: function (record) {
// user has selected a Country from the Country List.
var title = record.get('title');
CountryDetailsToolbar.setTitle(title);
CountryDetailsHeaderLeft.update({ upper: title.toUpperCase() });
CountryDetailsHeaderMonth.update(record.data);
viewport.setActiveItem('CountryDetailsCarousel');
}

How to use .NET TextBoxRenderer with TextBoxState.Hot to draw a hot text box?

i am trying to use TextBoxRenderer to render a "hot" text box:
TextBoxRenderer.DrawTextBox(e.Graphics, rectangle, TextBoxState.Hot);
except that it doesn't work, it doesn't render the text box as hot.
TextBoxState.Selected doesn't render as selected
TextBoxState.Hot doesn't render as hot
How do i make TextBoxRenderer.DrawTextBox(..., Hot) render as Hot?
Related but different question:
How do i make TextBoxRenderer.DrawTextBox(..., Selected) render as Selected?
It seems that TextBoxRenderer uses EP_BACKGROUNDWITHBORDER, whereas EP_EDITBORDER_NOSCROLL is typically used by TextBox controls[1].
if (VisualStyleRenderer.IsSupported)
{
// Use the text control's focus rectangle.
// EP_EDITBORDER_NOSCROLL, EPSN_FOCUSED
VisualStyleElement element = VisualStyleElement.CreateElement("EDIT", 6, 3);
if (VisualStyleRenderer.IsElementDefined(element))
{
VisualStyleRenderer renderer = new VisualStyleRenderer(element);
renderer.DrawBackground(e.Graphics, ClientRectangle);
}
}
(It's tempting to try to get the element from VisualStyleElement but there's no nested class for EP_EDITBORDER_NOSCROLL. So numeric constants 6 and 3 it is.)

Resources