How to specify custom rendering template for options in {{selectmenu}} tag - jsviews

I am looking for an example where I can specify template for rendering options, so that I can render an option as a two part: span with a background color and a value.
Simillar to this https://jqueryui.com/selectmenu/#custom_render
is this supported in the jsviews tag implementation?
Many thanks.

There are probably other approaches, including making your own custom tag - not using jQuery UI, or creating a derived version of the {{selectmenu}} tag control here, {{myselectmenu}} (using baseTag).
But here is a quick suggestion of one way you can do it:
{^{selectmenu person name="person" onBind=~onbind}}
{^{for people}}
<option data-style="{{:style}}" value="{{:id}}">{{:name}}</option>
{{/for}}
{{/selectmenu}}
Data:
people: [
{name: "John Resig", id: "1", style: "background-image: url(...);"},
...
Code:
pageTmpl.link("#page", model, {
onbind: function(val) {
// override onBind for this tag control instance
this.baseApply(arguments);
// override _renderItem for this widget instance:
this.widget._renderItem = function( ul, item ) {
var li = $( "<li>" ),
wrapper = $( "<div>", { text: item.label } );
if ( item.disabled ) {
li.addClass( "ui-state-disabled" );
}
$( "<span>", {
style: item.element.attr( "data-style" ),
"class": "ui-icon " + item.element.attr( "data-class" )
})
.appendTo( wrapper );
return li.append( wrapper ).appendTo( ul );
};
}
});

Related

Add value to existing WP Block Editor setting

I would like to add a 33% to the Wordpress Block "Button". So far it has 25%,50%,75% and 100%. Is it possible to insert my new value into the existing width selector?
I'm guessing Block Filters are the way to go.
I think I also found the way to get the settings object which might then help me to find out what I need to overwrite. However simply adding this code to my admin.js does not produce any output. Where would I need to load this?
const filterBlocks = (settings) => {
if (settings.name !== 'core/buttons') {
return settings
}
console.log(settings);
return settings;
}
Quick solution: Add a custom CSS class in the Buttons' block properties under "Advanced > Additional CSS class(es)" then define the custom width in your theme style.css
Detailed solution:
By using wp.hooks.addFilter() you can add a new control to the Button block with as many extra custom width options as you need. The Button blocks preset widths are defined within the function WidthPanel() of the blocks edit.js function:
function WidthPanel( { selectedWidth, setAttributes } ) {
...
return (
...
<ButtonGroup aria-label={ __( 'Button width' ) }>
{ [ 25, 50, 75, 100 ].map( ( widthValue ) => {
...
}
}
To add a new width value of 33% to the block, we need to add our own new button control to the InspectorControls and then use wp.hooks.addFilter() to add this to the existing core Button block, eg:
index.js
import { createHigherOrderComponent } from '#wordpress/compose';
import { Fragment } from '#wordpress/element';
import { InspectorControls } from '#wordpress/block-editor';
import { PanelBody, Button } from '#wordpress/components';
const withInspectorControls = createHigherOrderComponent((BlockEdit) => {
return (props) => {
const { setAttributes } = props;
let widthValue = 33; // must be a number
return (
<Fragment>
<BlockEdit {...props} />
<InspectorControls>
<PanelBody title="Custom Width">
<Button
key={widthValue}
isSmall
variant={widthValue}
onClick={() => setAttributes({ width: widthValue })}
>
{widthValue}%
</Button>
</PanelBody>
</InspectorControls>
</Fragment>
);
};
}, 'withInspectorControl');
wp.hooks.addFilter(
'editor.BlockEdit',
'core/button',
withInspectorControls
);
Next, a new additional css style needs to be added that (matches the existing width presets structure) for the new custom width, eg:
style.scss
$blocks-block__margin: 0.5em;
&.wp-block-button__width-33 {
width: calc(33.33% - #{ $blocks-block__margin });
}
And there you have it..
The easiest way to put all the code above together/working is to create your own Gutenberg block (and that in itself can be challenging if you aren't familiar with the process or ReactJS). I too have come across similiar challenges with Gutenberg, so I wanted to provide a detailed solution for this kind of issue that works.

Gutenberg add style attribute using block filter

I'm trying to add a style attribute in the block editor to a block's wrapper using a block filter:
const addStyle = createHigherOrderComponent( ( BlockListBlock ) => {
return ( props ) => {
return <BlockListBlock { ...props } className="my-class" style="color: red" />;
};
}, 'addStyle' );
wp.hooks.addFilter( 'editor.BlockListBlock', 'my-plugin/add-style', addStyle );
Only the my-class class name is added to the class attribute but no style attribute. Is it possible to add a style attribute as well? The documentation states:
It receives the original BlockListBlock component and returns a new
wrapped component.
but does not say you can add only class names.
For the rendering part it's:
wp.hooks.addFilter('blocks.getSaveContent.extraProps','my-plugin/add-style', function(props, name, atts){
return Object.assign(props, { 'style': 'color: red' });
});
Couldn't figure it out for the edit function tho.

Wordpress Gutenberg autocomplete - saving attributes

I want to develop a custom block that will let the user pick some information from autocomplete. I manage to create the autocomplete component on edit function.
A user can select an item but i don't know how to handle the attribute save.
I'm trying to save the selected item as attribute package_name. I created the onChange function on Autocomplete component but event.target.value is undefined.
Here is my code from block.js
const { __ } = wp.i18n; // Import __() from wp.i18n
const { AlignmentToolbar,
BlockControls,
registerBlockType } = wp.blocks;
const { RichText } = wp.editor;
const { Autocomplete, } =wp.components;
const MyAutocomplete = () => {
const autocompleters = [
{
name: 'fruit',
triggerPrefix: '#',
options: [
{ name: 'Apple', id: 1 },
{ name: 'Orange', id: 2 },
{ name: 'Grapes', id: 3 },
{ name: 'test', id: 4 },
],
getOptionLabel: option => (
<span>
{ option.name }
</span>
),
getOptionKeywords: option => [ option.name ],
isOptionDisabled: option => option.name === 'Grapes',
getOptionCompletion: option => (
<abbr title={ option.name }>{ option.name }</abbr>
),
}
];
function onChangeAuto(newContent){
console.log('autocompletexx '+newContent);
}
function onSelectAuto(event){
console.log(event.target);
console.log( event.target.value);
}
return (
<div>
<Autocomplete completers={ autocompleters }>
{ ( { isExpanded, listBoxId, activeId } ) => (
<div
contentEditable
suppressContentEditableWarning
aria-autocomplete="list"
aria-expanded={ isExpanded }
aria-owns={ listBoxId }
aria-activedescendant={ activeId }
onChange={onChangeAuto }
onSelect={onSelectAuto}
>
</div>
) }
</Autocomplete>
<p class="autocomplete_p">Type # for triggering the autocomplete.</p>
</div>
);
};
registerBlockType( 'residence-gutenberg-block/membership-package-settings', {
title: __( 'Residence - Membership Package Settings' ), // Block title.
icon: 'shield',
category: 'common',
keywords: [
__( 'membership-package-settings' ),
],
attributes:{
package_id:{
type:'string',
select:'p'
},
package_name:{
type:'string',
},
},
edit: function( props ) {
const { attributes: {package_name}, className,setAttributes,isSelected } = props;
return (
<div className={ props.className }>
<form>
<label className="wpresidence_editor_label">Current Package: {package_name}</label>
<MyAutocomplete></MyAutocomplete>
</form>
</div>
);
},
save: function( props ) {
// Rendering in PHP
return null;
},
} );
Passing down onChange, onSelect to the div element won't work, because these attributes are only applicable to the form field elements (as input, select, etc.).
I checked the documentation and the source code and didn't find any details or official approaches for dealing with the case.
However, I'm seeing two possible approaches for getting the selected value:
1. Using Autocomplete onReplace prop
Looking into the Autocomplete's source code, I noticed that onSelect callback invokes onReplace prop with the selected option as array. It may not fit all the cases, but you can give it a try! It may be enough for your case! You can try to add your handler to the onReplace as follows:
<Autocomplete
onReplace={ option => { console.log(option) } }
completers={ autocompleters }>
{ ( { isExpanded, listBoxId, activeId } ) => (
<div
contentEditable
suppressContentEditableWarning
aria-autocomplete="list"
aria-expanded={ isExpanded }
aria-owns={ listBoxId }
aria-activedescendant={ activeId }
/>
) }
</Autocomplete>
2. Listen for <div /> changes manually
You can add onInput, onBlur listeners to the <div />, having an uncontrolled react div component and when the div's value is changed then we can keep the changed value in your parent component's state.
Here's a great discussion, which describes these technique: React.js: onChange event for contentEditable
The good think is that there's already a plugin (based on this discussion) that can do it for you: react-contenteditable.
Firstly you have to convert your <MyAutocomplete /> component to be a statefull (not functional) and then:
import ContentEditable from 'react-contenteditable'
// Boilerplate ...
<Autocomplete completers={ autocompleters }>
{ ( { isExpanded, listBoxId, activeId } ) => (
<ContentEditable
html={this.state.html}
onChange={this.handleChange}
contentEditable
suppressContentEditableWarning
aria-autocomplete="list"
aria-expanded={ isExpanded }
aria-owns={ listBoxId }
aria-activedescendant={ activeId }
/>
) }
</Autocomplete>
Conclusion
I'm surprised that in the Autocomplete's documentation there aren't any details for this case. I guess it's because of the following statement (27.08.2018):
Gutenberg is being developed on GitHub, and you can try an early beta
version today from the plugin repository. Though keep in mind it’s not
fully functional, feature complete, or production ready.
However, one of both mentioned approaches above will help you, until they provide a complete API to work with their components.
I would suggest you to keep wrapping the Wordpress's <Autocomplete /> with your own component - in order to easily refactor your code later, when they release the complete API.
If you have questions, feel free to comment below.

Froala add custom pre code button

I'm trying to create a code button with the Froala editor which can basicly do the same thing as here on SO by pressing CNTRL+K. Now I think I have two choices.
The first one is to edit the froala-editor.js file, because Froala already has a 'code' button which only adds the <pre> tags. If I could somehow get it to also add the <code> tag, problem solved. Unfortunately I didn't get this to work.
The second option is to create a custom button, so far I have this piece of code:
$('textarea[name="description"]').editable({
//Settings here
customButtons: {
insertCode: {
title: 'Insert code',
icon: {
type: 'font',
value: 'fa fa-code'
},
callback: function() {
this.saveSelection();
if (!this.selectionInEditor()) {
this.$element.focus(); // Focus on editor if it's not.
}
var html = '<pre><code>' + this.text() + ' </code></pre>';
this.restoreSelection();
this.insertHTML(html);
this.saveUndoStep();
}
}
}
});
It works somehow, but it's buggy and produces strange html like so:
<p><code></code>
<pre><code>asdasdasdasd
</code></pre>
</p>
Any help with getting this done for either option one or two would be greatly appreciated.
If you upgrade to version 1.2.3 that is available on Github your code should work https://github.com/froala/wysiwyg-editor. It's not necessary to save/restore selection.
LATER EDIT:
Here is a jsFiddle for it http://jsfiddle.net/9pmmg1jk/.
customButtons: {
insertCode: {
title: 'Insert code',
icon: {
type: 'font',
value: 'fa fa-code'
},
callback: function() {
if (!this.selectionInEditor()) {
this.$element.focus(); // Focus on editor if it's not.
}
var html = '<code>' + (this.text() || '​') + '<span class="f-marker" data-type="false" data-id="0" data-fr-verified="true"></span><span class="f-marker" data-type="true" data-id="0" data-fr-verified="true"></span></code>';
this.insertHTML(html);
this.restoreSelectionByMarkers();
this.saveUndoStep();
}
}
}

ExtJS 4 / 5 - Set the correct CSS for a Drag and Drop that is not allowed with custom rules

I have two grids side by side.
The left grid has a list of tags the user can select, and the grid on the right is empty so the user can drag the tags he wants there.
The plugin code for both grids is:
viewConfig: {
plugins: [
Ext.create('Ext.grid.plugin.DragDrop', {
ddGroup: 'selectedTags'
})
]
}
So, as I wanted to limit the user to be able to drag only 5 tags, I've added the following code to the grid on the right:
listeners: {
beforedrop: {
fn: function() {
if (grid.getStore().data.items.length > 4) {
dropHandlers.cancelDrop();
}
},
scope: me
}
}
This is working perfectly but what I wanted is to show the NO-DROP icon when the items are dragged over the grid instead of showing the green line as if the action was allowed.
Thanks,
After looking for this solution for a while, I finally figured it out.
You must add two methods to the dropZone in the Target Grid:
notifyOver and onNodeDrop
The solution for my problem would be the code below:
Ext.create('Ext.grid.Panel', {
store: myStore,
columns: [columns],
viewConfig: {
plugins: {
ptype: 'gridviewdragdrop',
dragText: 'Drag and drop to reorganize',
pluginId : 'dragNdrop',
dropZone:{
notifyOver:function(source, e, data){
var store = this.view.ownerCt.getStore();
return store.getCount()<5?this.dropAllowed:this.dropNotAllowed
},
onNodeDrop:function(targetNode,dragZone,e,data){
var sourceStore = dragZone.view.ownerCt.getStore(),
targetStore = this.view.ownerCt.getStore(),
isDropValid = targetStore.getCount()<5;
if(isDropValid){
sourceStore.remove(data.records[0])
targetStore.add(data.records[0]);
}
return isDropValid;
}
}
}
},
height: 200,
width: 400,
renderTo: Ext.getBody()
});
Lopes, you can use column renderer in grid where you can check the items count and display appropriate icon. Code snippet for your reference:
gridCheckboxRenderer: function(value, meta, rec, rowInd, colInd, store){
var cssPrefix = Ext.baseCSSPrefix, cls = [cssPrefix + 'grid-checkheader'];
if (condition == false) {
cls.push(cssPrefix + 'grid-checkheader-checked-disabled');
return '<div class="' + cls.join(' ') + '"> </div>';
}
return '<div class="x-grid-row-checker"> </div>';
}
Hope it helps.

Resources