ProseMirror / Tiptap: how to ScrollIntoView without focus? - js-scrollintoview

I am working on a note-taking app that uses ProseMirror as the editor. I want to achieve the feature that when the user searches, the editor will automatically scroll to the most relevant position calculated by other functions. Meanwhile, the search bar should NOT lose the input focus so the user can modify the search content. Therefore, the straightforward way of
editor.chain().focus().setTextSelection().run()
would not work.
I have found a way to set the editor selection without focus. But the view is not scrolled. I need it to scroll to the new selection.
Here is an illustration of the scenario
I tried
editor.commands.scrollIntoView()
It does not work. But
editor.commands.setTextSelection()
does change the selection of editor.view.

You can use ProseMirror directly (instead of TipTap objects), or finding the DOM node worked for me too:
/**
* Scrolls to the current position/selection of the document. It does the same as scrollIntoView()
* but without requiring the focus on the editor, thus it can be called from the search box while
* typing or in shopping mode when the editor is disabled.
* #param {Editor} editor - A TipTap editor instance.
*/
function scrollToSelection(editor: Editor): void {
const { node } = editor.view.domAtPos(editor.state.selection.anchor);
if (node) {
(node as any).scrollIntoView?.(false);
}
}
Copied from SilentNotes

Related

How to prevent closing of cell editing in ag-grid on "Other Cell Focus"

I am working on an editable table in Angular application with ag-grid library. I would like to keep editing cells (in full row edit mode) until I finish with it and then close the editors manually via API. The problem is that the editor is closing on other cell click/focus (on some other line) as described here:
The grid will stop editing when any of the following happen:
Other Cell Focus: If focus in the grid goes to another cell, the editing will stop.
I cannot figure out how to disable this, if it is possible. Installing the onCellMouseDown() hook does not help, because the cellFocused event is fired before cellMouseDown. Therefore, the editing stops before I have a chance to intercept the mousedown event.
Here is my stackblitz little extract with related pieces of code.
The need for such scenario is that I want to validate the entry and not to allow a used to quit the editing if the form is not valid. The only workaround I found so far is that on any click outside of editing cells when the editor closing I reopen it right away in onRowEditingStopped() hook unless the editor has been closed via 'OK' button.
After all, I have managed to provide a custom solution that fits exactly into this problem which I was facing also.
First thing is to disable pointer events to non edited rows when a specific row is currently being edited. On Ag-grid's 'cellEditingStarted' callback I have added the following code:
public cellEditingStarted(event: any): void {
//not all rows are on dom ag-grid takes care of it
const nonSelectedGridRows = document.querySelectorAll('.ag-grid-custom-row:not(.ag-row-selected):not(.ag-row-editing):not(.pointer-events-none)');
forEach(nonSelectedGridRows, row => {
row.classList.add("pointer-events-none");
});
}
Because not all rows exist on dom (Ag-grid creates and destroys while you are scrolling )when a specific cell is being edited, I have also added a rowClassRule which is applied when rows are being created:
this.rowClassRules = {
'pointer-events-none': params => {
if (params.api.getEditingCells().length > 0) {
return true;
}
return false;
}
};
scss:
.pointer-events-none {
pointer-events: none
}
By disabling pointer events, when you click on a non edited cell the cell won't get focus and thus the currently edited cell will stil remain on edit mode. You can provide your own custom validation solution and close the editor manually through API. When you are done, you have to enable pointer events to all grid rows back again:
private enablePointerEvents(): void {
//not all rows are on dom ag-grid takes care of it
const nonSelectedGridRows = document.querySelectorAll('.ag-grid-custom-row.pointer-events-none');
forEach(nonSelectedGridRows, row => {
row.classList.remove("pointer-events-none");
});
}
I implemented the same above approach in Ag-Grid React.
I used getRowStyle callback for adding the css pointerEvents: none on dynemic basis.
It seems to be working for me fine.
Please refer the below code
const getRowStyle = (params) => {
// this is not initialized in read mode
// condition for me ==> currentEditRowIndex.current !== null && params.node.rowIndex !== currentEditRowIndex.current
if (someCondition for Row other than inline edit row) {
return { pointerEvents: "none" };
}
return null;
};
After adding this whenver you start the editing..You will need to call redrawRows so that css changes can be applied.
Hope this will help. Thank You!!
Thought I would share another solution that has been working out okay for me so far.
Using 'pointer-events-none' as suggested in the other answer is flawed because the Enter key can also close the editor.
In my case, I want to prevent the editor from closing when client side validation has failed and the data is invalid. When my conditions are met, I call stopPropagation() on the events to prevent the editor close from happening in the first place. It still has potential problems:
It cancels mousedown, dblclick, keydown, focusout and click for all elements that have a class name starting with ag- so if you happen to use this class prefix for other controls on the page, it could interfere. It also means any controls within the grid (sorting, resizing, etc.) don't work while the condition is met.
Calling stopPropagation() could potentially interfere with your own custom controls. So far I've been okay if I dont use the ag- prefix within the markup from my own custom cell editors and renderers
I hope they can add a proper API function to cancel the row/cell stopEditing function in the future.
["mousedown", "dblclick", "keydown", "focusout", "click"].forEach(function (eventName) {
document.addEventListener(eventName, function (e) {
if ( conditionForCancelingIsMet() ) {
// this appears to cancel some events in agGrid, it works for
// preventing editor closing on clicking other cells for example.
// It would be ideal if this worked for all ag-grid specific events
// and had a proper public API to use!
e["__ag_Grid_Stop_Propagation"] = true;
}
// convert element classList to normal js array so we can use some()
var classArray = [].slice.apply(e.target.classList);
if ( conditionForCancelingIsMet() && classArray.some(c => c.startsWith("ag-")) ) {
// unfortunately some events like pressing the 'enter' key still
// require stopPropagation() and could potentially interfere with unrelated controls
e.stopPropagation();
}
}, true);
});

Multi-page form: Bring invalid field into focus

I have a multipage form with more than 40 fields spread over multiple tabs, and grouped in collapsible fieldsets.
Now I have the case that upon form submission, a field is detected as invalid, and I want to find the field for the user, bring it into the visible area and focus it. So I have to switch to the right tab, open the fieldset if applicable, scroll the field into the visible area and focus it.
I would guess ExtJS has a function for this, but I don't find one. My code:
// Get first invalid field. C&P from Ext.form.Basic.isValid function
var invalidField = me.getForm().getFields().findBy(function(f) {return !f.isValid();});
if(invalidField) {
// TODO: Bring the field to front.
// Now focus the field:
invalidField.focus();
Is there a builtin function available?
Ext JS does not provide a built-in method for doing this specifically, but it does provide all of the necessary utility methods and supports animations. At a minimum, ensuring the form is configured as scrollable, setting the active tab, and focusing on the invalid field is enough to scroll to correct position. I created a fiddle example demonstrating a solution.
Sencha Fiddle: An Example of Scrolling to an Invalid Field in a Tab
tabPanel.items.each(function(tab) {
var formPanel = tab.down('form');
formPanel.getForm().getFields().each(function(field, index, length) {
if (!field.isValid()) {
tabPanel.setActiveTab(tab);
// Focusing an element will set the correct scroll position.
// However, an animation can help the user follow along.
formPanel.setScrollY(field.getPosition()[1], true);
field.focus();
return false;
}
return true;
});
});
http://docs.sencha.com/extjs/6.2.0/classic/Ext.Component.html#method-getPosition
http://docs.sencha.com/extjs/6.2.0/classic/Ext.Component.html#method-setScrollY

Eclipse ui: retrieving the first visible line of an editor

In the Eclipse UI, I'd like to set the visible area in an editor. In other words, if the number of lines of my file is larger than the number of lines my editor can show then I want to specify the first shown line. My first approach was to calculate the first visible line via the selection value of its vertical scroll bar. The following link points to my initial question. Its answer explains how to set the first visible line in an editor.
eclipse ui: setting scrollbar but editor does not follow
The problem now is that my initial way of retrieving the first visible line in an editor fails in some cases: Although I verify that the active page is indeed an editor, the focus might be assigned to another page. In such a case, the following code yields the ScrollBar of a different page:
public static void update(final IWorkbenchWindow w)
final Scrollable scrollable =
(Scrollable) w.getWorkbench().getDisplay().getFocusControl();
final ScrollBar vScrollBar = scrollable.getVerticalBar();
So, my question: If editor is the reference of an active editor (ITextEditor and IReusableEditor), how to I get its first visible line?
If you can access the editor ITextViewer or its extension ISourceViewer (usually implemented by the SourceViewer or TextViewer class) you can call the ITextViewer.getTopIndex() method to get the top line index.
If your editor is derived from AbstractTextEditor (or one of its subclasses such as TextEditor) there is a protected method getSourceViewer() that returns this. You may have to add a public method if you want to access this from outside of the editor.

Get notified when cursor position changed in Eclipse TextEditor

I am developing a plugin to eclipse and I want to add some actions to the context menu. But actually I wanted to prepare results beforehead according to the text selection in the editor and just show them when the menu item will be selected.
I followed that article http://www.eclipse.org/articles/Article-WorkbenchSelections/article.html - all interfaces (ISelectionListener, ISelectionChangedListener etc) allow to handle the SelectionChanged event, but editor counts changing only when length of selection also changes - so the simple click in the editor doesn't fire the event, although I want to get the word (for example) as a selection if cursor is inside the word now and lenght is 0.
So the question is - what is the simpliest solution for traking down cursor position/offset/selections with zero lengh value changing?
In that case you have to use KeyListener and MouseListener as well. For e.g take a look at org.eclipse.jface.text.PaintManager, and it listens to all these events.
If you are extending TextEditor you can override handleCursorPositionChanged() method to fire your event and use getCursorPosition() to get the cursor position as a String.

How do you change the mouse over highlighting?

In GWT, I am using CellTable.
When you mouse over the CellTable it highlights each row.
How do change the behavior of the highlighting from the mouse over? Specifically:
change the color of highlighting
disable/enable
make it highlight only the specific grid item at your cursor (instead of the entire row)
( The current hack I have is to create a bunch of 1 column wide CellTables and add them to a VerticalPanel layout... creating the illusion that there is one CellTable and it highlights each grid according to your cursor. Is this bad? Why? performance? )
You will notice the CellTable uses a ResourceBundle, which means all the css styles get obfuscated ... this makes it more difficult to override styles.
The CellTable constructor will actually allow you to override the default ResourceBundle. So first, you need to create your own resource bundle like this:
public interface CellTableResources extends Resources {
public CellTableResources INSTANCE =
GWT.create(CellTableResources.class);
/**
* The styles used in this widget.
*/
#Source("CellTable.css")
CellTable.Style cellTableStyle();
}
Then you need to create your own CSS file. I recommend copying the CellTable style directly into your project and use that as a starting point. You can find it here:
http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/user/cellview/client/CellTable.css
Make sure the style is injected first, and then you just feed it into the CellTable's constructor like this:
CellTableResources.INSTANCE.cellTableStyle().ensureInjected();
myCellTable = new CellTable<T>(Integer.MAX_VALUE,CellTableResources.INSTANCE);
Specifically, you'll want to tweak these styles:
cellTableKeyboardSelectedRow
cellTableKeyboardSelectedRowCell
cellTableSelectedRow
cellTableSelectedRowCell
cellTableKeyboardSelectedCell
It is important to note that the cell table differentiates between the 'selected row' and the 'keyboard selected row'. The selected row is the actual row selected (ie via SelectionModel). The keyboard selected row refers to what is highlighted when the user is pressing the up / down key, but does not mean the row is actually selected (if that makes sense).
I'll just add for number 2) on your list, you can simply do
cellList.setSkipRowHoverStyleUpdate(true)
That completely disables highlighting. There are also two more setSkip-functions on CellList related to hovering.
CellTable can be styled via CSS: How do I style a gwt 2.1 CellTables headers?
To disable highlighting just set the hover CSS property to nothing.
Possibly - try tweaking the .cellTableSelectedRow and .cellTableSelectedRowCell.
Here is the original CellTable.css: http://www.google.com/codesearch/p?hl=en#A1edwVHBClQ/user/src/com/google/gwt/user/cellview/client/CellTable.css&q=cellTableLastColumn&d=8