EXTJS: Change the color of a node - extjs

I need to be able to change the color of a node in a treepanel. I thought this would be pretty simple, but i am having a heck of a time finding anything.
Thanks

of course, 5 minutes after I ask, I find the solution....
It does work without removing the current class, but I left the line there just because I don't know if it might cause problems later if its not there.
<style>
.RedText a span
{
color: Red;
}
</style>
var currentClass = opNode.attributes.cls;
opNode.ui.removeClass(currentClass);
opNode.ui.addClass('RedText');
opNode.attributes.cls = 'RedText';

this didn't work for me. I wanted to change the style of a double clicked treeNode. Here is my solution with extjs 4.2:
…
CSS
.treeNode-selected {
font-weight: bold;
}
js e.g. itemdblclick event binding
onFilterTreeItemDblClick: function(dataview, record, item, index, e, eOpts) {
var me = this;
this.store.getRootNode().cascadeBy(function(node) {
// if your root node is not visible
if (node.getId() === "root") { return; }
var nodeView = me.getView().getNodeById(node.getId());
var td = nodeView.childNodes[0];
if (node.getId() === record.getId()) {
td.classList.add("treeNode-selected");
} else {
td.classList.remove("treeNode-selected");
}
}
}
I hope this safes somebody some time.

Related

Implement custom editor for Quill blot

I'm trying to customize the Quill editor for my needs. I managed to implement and insert custom blots, as described in https://quilljs.com/guides/cloning-medium-with-parchment/ But I need to edit data, which is attached to my blots, like the URL of a link for example. The default implementation of Quill displays a small "inline" edit box for links. I want to implement something like that myself, but just don't get it. I did not find any hints in the docs and guides. Reading the source code of Quill, I was not able to figure out where the editing dialog for links is implemented. Any starting point would be very appreciated.
I've tried something similar. Proper way of doing it should be creating a module. Unfortunately as you already know it is not as easy as it seems.
Let me point you to some useful resources that helped me a lot with understanding how to create extensions for quill.
Quills maintainer is curating Awesome quill list.
I recommend looking especially into
quill-emoji it contains code to display tooltip emoji while writing
quill-form maybe some form extension has some code that will point you in the right direction
Here is my try on to it using custom quill module.
const InlineBlot = Quill.import('blots/inline');
class NamedLinkBlot extends InlineBlot {
static create(value) {
const node = super.create(value);
node.setAttribute('href', value);
node.setAttribute('target', '_blank');
return node;
}
}
NamedLinkBlot.blotName = 'namedlink';
NamedLinkBlot.tagName = 'A';
Quill.register('formats/namedlink', NamedLinkBlot);
const Tooltip = Quill.import('ui/tooltip');
class NamedLinkTooltip extends Tooltip {
show() {
super.show();
this.root.classList.add('ql-editing');
}
}
NamedLinkTooltip.TEMPLATE = [
'<a class="ql-preview" target="_blank" href="about:blank"></a>',
'<input type="text" data-link="https://quilljs.com">',
'Url displayed',
'<input type="text" data-name="Link name">',
'<a class="ql-action"></a>',
'<a class="ql-remove"></a>',
].join('');
const QuillModule = Quill.import('core/module');
class NamedLinkModule extends QuillModule {
constructor(quill, options) {
super(quill, options);
this.tooltip = new NamedLinkTooltip(this.quill, options.bounds);
this.quill.getModule('toolbar').addHandler('namedlink', this.namedLinkHandler.bind(this));
}
namedLinkHandler(value) {
if (value) {
var range = this.quill.getSelection();
if (range == null || range.length === 0) return;
var preview = this.quill.getText(range);
this.tooltip.show();
}
}
}
Quill.register('modules/namedlink', NamedLinkModule);
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
namedlink: {},
toolbar: {
container: [
'bold',
'link',
'namedlink'
]
}
}
});
CodePen Demo
To see the tooltip:
Select any word
Click invisible button on the right of link button, your cursor will turn to hand.
Main issues that need to be addressed:
in order to customize the tooltip you will need to copy majority of the code from SnowTooltip Main pain point is that you cannot easily extend That tooltip.
you need to also adapt the code of event listeners but it should be straightforward
Here's a partial answer. Please see lines 41-64 in file "https://github.com/quilljs/quill/blob/08107187eb039eababf925c8216ee2b7d5166d41/themes/snow.js" (Please note, that the authors have since moved to TypeScript, lines 45-70?, and possibly made other changes.)
I haven't tried implementing something similar but it looks like Quill is watching a "selection-change" event and checks if the selection is on a LinkBlot with a defined link.
The SnowTooltip class includes references to the selectors, 'a.ql-preview', 'ql-editing', 'a.ql-action', and 'a.ql-remove', which we find in the link-editing tooltip.
this.quill.on(
Emitter.events.SELECTION_CHANGE,
(range, oldRange, source) => {
if (range == null) return;
if (range.length === 0 && source === Emitter.sources.USER) {
const [link, offset] = this.quill.scroll.descendant(
LinkBlot,
range.index,
);
if (link != null) {
this.linkRange = new Range(range.index - offset, link.length());
const preview = LinkBlot.formats(link.domNode);
this.preview.textContent = preview;
this.preview.setAttribute('href', preview);
this.show();
this.position(this.quill.getBounds(this.linkRange));
return;
}
} else {
delete this.linkRange;
}
this.hide();
},
);

Extjs DatePicker: How to enable 'Click/Select' on disabled dates using mouse click?

I'm using Extjs 4.2.5 datepicker for Rooms management, when user select a date I add it to an array and then call 'setdisableDates':
sender.DisabledDates.push(date);
sender.setDisabledDates(sender.DisabledDates);
after user select a date it becomes disable and with css I change the background color to RED.
How can I enable click or Select on those dates that already disabled just in case the user made a mistake and want to cancel?
Thank you in advice
I manage to do so by overriding "handleDateClick":
function picker.beforeInit(sender, config)
{
config.cls='room_clndr';
config.DisabledDates=[];
config.disabledDaysText='חסום';
config.handleDateClick = function(e, t){
var me = this,
handler = me.handler;
e.stopEvent();
if(!me.disabled && t.dateValue){//<---------
me.doCancelFocus = me.focusOnSelect === false;
me.setValue(new Date(t.dateValue));
delete me.doCancelFocus;
me.fireEvent('select', me, me.value);
if (handler) {
handler.call(me.scope || me, me, me.value);
}
me.onSelect();
}
}
}
And:
function picker.select(sender, date, eOpts)
{
if(jQuery.inArray( Ext.Date.format(date, 'd/m/Y'), sender.DisabledDates )>=0) {
sender.DisabledDates.splice( $.inArray(Ext.Date.format(date, 'd/m/Y'), sender.DisabledDates), 1 );
if(sender.DisabledDates.length>0) {
sender.setDisabledDates(sender.DisabledDates);
}
else {
sender.setDisabledDates([null]);
}
}
else {
sender.DisabledDates.push(Ext.Date.format(date, 'd/m/Y'));
sender.setDisabledDates(sender.DisabledDates);
}
}
And here the css:
.room_clndr .x-datepicker-disabled .x-datepicker-date
{
background-color:#fe5757 !important;
color: #fff !important;
}

extjs treepanel load children without expand

I use Extjs 3.4. I am working on TreePanel with checkbox solution.
What I need is: When I check father node, all childNodes also checked. It's easy, but it depends on extjs expand all childNodes.
If I do
tree.expandAll();
tree.collapseAll();
My check strategy will work, but I don't want the expand effect.
My extjs code(checkchange event) is something like the following:
var checkchange = function(node, flag) {
if (node.hasChildNodes()) {
node.cascade(function(node) {
node.attributes.checked = flag;
node.ui.checkbox.checked = flag;
return true;
});
}
var pNode = node.parentNode;
for (; pNode != null; pNode = pNode.parentNode) {
if (flag || tree.getChecked("checked", pNode).length - 1 == 0) {
pNode.attributes.checked = flag;
pNode.ui.checkbox.checked = flag;
}
}
};
var tree = new Ext.tree.TreePanel({
listeners: {
'checkchange': checkchange
},
})
How can I do? Thank every one for giving advice.
[ EDIT ]
I create A Demo In jsfiddle, that can be easily test.
(Since Extjs 3.4.0 cdn used by jsfiddle not work, I append another cdn extjs resource from https://cdnjs.com/libraries/extjs/3.4.1-1)
I am not sure whether you really want the whole tree to be loaded node by node when checking the root node. I would recommend to lazily check the child nodes when they are loaded for an already-checked parent node, by attaching to the load event. Something like this:
load:function(me, node) {
if(node && node.attributes.checked) node.cascade(
... [function to check all children]
)
}
Depending on your intentions for further processing and your tree size, this could be better than expanding the whole tree to render and check all checkboxes.
If you want the tree to be loaded directly, I would recommend to use preloadChildren:true on the TreeLoader. This is easier than a manual implementation of cascaded load.
I didn't really solve this problem. But I got an eclectic solution.
Only expand when needed
checkchange : function(node, flag){
node.cascade(function(node) {
// when you check, first expand, then child nodes can be checked too
if(node.expanded == false)
node.expand(true);
node.attributes.checked = flag;
node.ui.checkbox.checked = flag;
return true;
});
......
}
This will meet the precondition that all child nodes should have expanded. But also no need expanded when first loaded.
If a parent node checked when first loaded, all child nodes need to be expand
this.treeLoader = new Ext.tree.TreeLoader({
url : 'xxx',
baseParams : {
someparam: ""
},
listeners : {
'load' : function(tree,node,response) {
var res = Ext.util.JSON.decode(response.responseText);
if(res.success != undefined && !res.success) {
Ext.Msg.alert('Hint', res.message);
return;
}
node.cascade(function(node) {
if(node.attributes.checked == true) {
node.expand(true);
}
});
},
}
});
This two methods does solve my prolem though not very perfect.
Hope this can help others.

Fieldset and disabling all child inputs - Work around for IE

I have a fieldset that has a ui-view under it.
Each view had lots of fields(a field is a directive that wraps an input) under it.
It looks something like this:
<fieldset ng-disabled='myCondition'>
<div ui-view></div> // this changes with lot's of fields that look like <div field='text-box'></div>
</fieldset>
Now, this worked great, the fields get disabled on all browsers except IE.
I've done some google and seen that ie doesn't support fieldset + disabled and I'm looking for a quick workaround.
I've tried some things that were close but not perfect and I assume I'm not the first one that needs a solution(even though I didn't find anything on google).
It has 1 line solution now.
.
Though status is fixed in Microsoft documentation Issue still not resolved!
But, Now we can also use pointer-events: none;. It will disable all input elements
fieldset[disabled] {
pointer-events: none;
}
Seems like related to IE issues, see this and related (sorry, can't post more than 2 links yet).
The first one will be fixed in next major IE release (Edge?).
The second one is still opened.
As I suspect, the problem is that user still can click into inputs inside disabled fieldset an edit them.
If so, there is "css only" workaround for IE 8+ that creates transparent overlay above disabled fieldset that prevents fieldset from being clicked.
The workaround is described in Microsoft Connect issues.
There is fiddle, that demonstrates workaround in action.
fieldset {
/* to set absolute position for :after content */
position: relative;
}
/* this will 'screen' all fieldset content from clicks */
fieldset[disabled]:after {
content: ' ';
position: absolute;
z-index: 1;
top: 0; right: 0; bottom: 0; left: 0;
/* i don't know... it was necessary to set background */
background: url( data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==);
}
The workaround has some limitations, see code for details.
There are some options with JavaScript.
Seems like for IE9+ you can catch mousedown events on fieldset and call e.preventDefault() if fieldset is disabled.
fieldset.onmousedown = function(e) {
if (!e) e = window.event;
if (fieldset.disabled) {
// for IE9+
if (e.preventDefault) {
e.preventDefault();
}
// for IE8-
else {
// actualy does not work
//e.returnValue = false;
}
return false;
}
}
For IE8 and below it is imposible to catch bubbling mousedown events on disabled fieldset, event handlers does not even gets called. But it is possible to catch them on fieldset ancestors, on documetn.body for exampe. But again, for IE8- you can't prevent element from being focused by preventing default action of mousedown event. See jQuery ticket #10345 for details (sorry, can't post more than 2 links). You can try to use UNSELECTABLE attribute to temporary forbid element to get focus. Something like this:
document.body.onmousedown = function(e) {
if (!e) e = window.event;
var target = e.target || e.srcElement;
if (fieldset.contains(target) && fieldset.disabled) {
// no need to do this on body!!! do it on fieldset itself
/*if (e.preventDefault) {
e.preventDefault();
}
else {*/
// this is useless
//e.returnValue = false;
// but this works
fieldset.setAttribute("UNSELECTABLE", "on");
window.setTimeout(function() { target.setAttribute("UNSELECTABLE", ""); },4);
/*}*/
return false;
}
}
I had the exact same problem, and i came up with this directive:
angular.module('module').directive('fieldset', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
if (angular.isUndefined(element.prop('disabled'))) { //only watch if the browser doesn't support disabled on fieldsets
scope.$watch(function () { return element.attr('disabled'); }, function (disabled) {
element.find('input, select, textarea').prop('disabled', disabled)
});
}
}
}
});
The feature detect is flawed though. On IEs it appears that the fieldset element (all elements it seems actually) have a 'disabled' property that is just set to false.
Edit: i just realised that it is inside an 'ng-view'. You may have to mess around with $timeouts to get it to apply the changes after the view has loaded. Or, even easier, place the fieldset inside the view.
This is a fix to disable fieldsets in IE11:
https://connect.microsoft.com/IE/feedbackdetail/view/962368/can-still-edit-input-type-text-within-fieldset-disabled
Detect IE:
Detecting IE11 using CSS Capability/Feature Detection
_:-ms-lang(x), fieldset[disabled].ie10up
{
pointer-events: none;
opacity: .65;
}
As other browser shows (disabled(/)) symbol on hover for disabled field so this change we should apply to only IE using #media
#media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
fieldset[disabled] {
pointer-events: none;
}
}

How do I capture table td elements using mousedown.dragselect event?

I have a directive which renders a HTML table where each td element has an id
What I want to accomplish is to use the mousedown.dragselect/mouseup.dragselect to determine which elements have been selected, and then highlight those selected elements. What I have so far is something like this:
var $ele = $(this);
scope.bindMultipleSelection = function() {
element.bind('mousedown.dragselect', function() {
$document.bind('mousemove.dragselect', scope.mousemove);
$document.bind('mouseup.dragselect', scope.mouseup);
});
};
scope.bindMultipleSelection();
scope.mousemove = function(e) {
scope.selectElement($(this));
};
scope.mouseup = function(e) {
};
scope.selectElement = function($ele) {
if (!$ele.hasClass('eng-selected-item'))
$ele.addClass('eng-selected-item'); //apply selection or de-selection to current element
};
How can I get every td element selected by mousedown.dragselect, and be able to get their ids and then highlight them?
I suspect using anything relating to dragging won't give you what you want. Dragging is actually used when moving elements about (e.g. dragging files in My Computer / Finder), when what you're after is multiple selection.
So there a number of things the directive needs:
Listen to mousedown, mouseenter and mouseup, events.
mousedown should listen on the cells of the table, and set a "dragging" mode.
mouseenter should listen on the cells as well, and if the directive is in dragging mode, select the "appropriate cells"
mouseup should disable dragging mode, and actually be on the whole body, in case the mouse is lifted up while the cursor is not over the table.
jQuery delegation is useful here, as it can nicely delegate the above events to the table, so the code is much more friendly to cells that are added after this directive is initialised. (I wouldn't include or use jQuery in an Angular project unless you have a clear reason like this).
Although you've not mentioned it, the "appropriate cells" I suspect all the cells "between" where the mouse was clicked, and the current cell, chosen in a rectangle, and not just the cells that have been entered while the mouse was held down. To find these, cellIndex and rowIndex can be used, together with filtering all the cells from the table.
All the listeners should be wrapped $scope.$apply to make sure Angular runs a digest cycle after they fire.
For the directive to communicate the ids of the selected elements to the surrounding scope, the directive can use bi-directional binding using the scope property, and the = symbol, as explained in the Angular docs
Putting all this together gives:
app.directive('dragSelect', function($window, $document) {
return {
scope: {
dragSelectIds: '='
},
controller: function($scope, $element) {
var cls = 'eng-selected-item';
var startCell = null;
var dragging = false;
function mouseUp(el) {
dragging = false;
}
function mouseDown(el) {
dragging = true;
setStartCell(el);
setEndCell(el);
}
function mouseEnter(el) {
if (!dragging) return;
setEndCell(el);
}
function setStartCell(el) {
startCell = el;
}
function setEndCell(el) {
$scope.dragSelectIds = [];
$element.find('td').removeClass(cls);
cellsBetween(startCell, el).each(function() {
var el = angular.element(this);
el.addClass(cls);
$scope.dragSelectIds.push(el.attr('id'));
});
}
function cellsBetween(start, end) {
var coordsStart = getCoords(start);
var coordsEnd = getCoords(end);
var topLeft = {
column: $window.Math.min(coordsStart.column, coordsEnd.column),
row: $window.Math.min(coordsStart.row, coordsEnd.row),
};
var bottomRight = {
column: $window.Math.max(coordsStart.column, coordsEnd.column),
row: $window.Math.max(coordsStart.row, coordsEnd.row),
};
return $element.find('td').filter(function() {
var el = angular.element(this);
var coords = getCoords(el);
return coords.column >= topLeft.column
&& coords.column <= bottomRight.column
&& coords.row >= topLeft.row
&& coords.row <= bottomRight.row;
});
}
function getCoords(cell) {
var row = cell.parents('row');
return {
column: cell[0].cellIndex,
row: cell.parent()[0].rowIndex
};
}
function wrap(fn) {
return function() {
var el = angular.element(this);
$scope.$apply(function() {
fn(el);
});
}
}
$element.delegate('td', 'mousedown', wrap(mouseDown));
$element.delegate('td', 'mouseenter', wrap(mouseEnter));
$document.delegate('body', 'mouseup', wrap(mouseUp));
}
}
});
Another thing that will make the experience a bit nicer, is to set the cursor to a pointer, and disable text selection
[drag-select] {
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
You can also see this in action in this working demo

Resources