I would like to set a default value for an image width in TYPO3 Neos.
Right now an editor may insert any image and the »width« value will be equal to the original size by default.
Example
First question:
I would like to set a default of 400 pixel instead. But the width field is no distinct node property, but an attribute of »image«. How do I set default values for attributes in Neos?
Second question:
What would I need to do, to completely hide the pixel based value field and offer an selection instead? Like „Option 1: Small teaser (150px), Option 2: Regular content image (400px), Option 3: Large image (980px)“.
Should I somehow remove the »width« attribute and add a new property node? Or may I change the type of the attribute somehow?
you can extend and configure default NodeType (TYPO3.Neos.NodeTypes:ImageMixin) for ImageEditor in Neos CMS.
Follow this steps:
Step 1:
Create new configuration file NodeTypes.Mixins.yaml in your sitepackage (for example: /Packages/Sites/YourSitePackageName/Configuration/NodeTypes.ImageMixin.yaml)
Step 2:
Copy default configuration for ImageMixin from Neos CMS Core (/Packages/Application/TYPO3.Neos.NodeTypes/Configuration/NodeTypes.Mixin.yaml) and remove properties which you doesn't like to extend/configure/override (for example: alternativeText and title). At the end you must have similar code:
`# Image mixin
'TYPO3.Neos.NodeTypes:ImageMixin':
abstract: TRUE
ui:
inspector:
groups:
image:
label: i18n
position: 5
properties:
image:
type: TYPO3\Media\Domain\Model\ImageInterface
ui:
label: i18n
reloadIfChanged: TRUE
inspector:
group: 'image'
position: 50`
Step 3: If you like to hide pixel base value fields (width, height) and crop button, you must add following editor options to image property:
position: 50
editorOptions:
features:
crop: FALSE --> hides crop button
resize: FALSE --> hides pixel based value fields
You can read more about this in Neos Documentation.
Step 4: For selection of predefined image sizes we add custom property imageSize (you can use other name) with following configuration:
imageSize:
type: string
ui:
label: 'Select Image Size'
reloadIfChanged: TRUE
inspector:
group: 'image'
position: 60
editor: 'TYPO3.Neos/Inspector/Editors/SelectBoxEditor'
editorOptions:
values:
small:
label: 'Small teaser (150x150)'
regular:
label: 'Regular content image (400x270)'
large:
label: 'Large image (980x500)'
allowEmpty: TRUE
This configuration add an select field with custom image sizes.
Step 5: Now we need to override default Image NodeType in TypoScript. Add following code to your Root.ts (/Packages/Sites/YourSitePackageName/Resources/Private/TypoScript/Root.ts2) (maybe you use other typoscript file).
prototype(TYPO3.Neos.NodeTypes:Image) {
// overwrite template for images
templatePath = 'resource://Vendor.YouSitePackageName/Private/Templates/NodeTypes/Image.html'
// define maximumWidth for images
maximumWidth = TYPO3.TypoScript:Case {
smallCondition {
condition = ${q(node).property('imageSize') == 'small'}
renderer = 150
}
regularCondition {
condition = ${q(node).property('imageSize') == 'regular'}
renderer = 400
}
largeCondition {
condition = ${q(node).property('imageSize') == 'large'}
renderer = 980
}
fallback {
condition = ${q(node).property('imageSize') == ''}
renderer = 400
}
}
// define maximumHeight for images
maximumHeight = TYPO3.TypoScript:Case {
smallCondition {
condition = ${q(node).property('imageSize') == 'small'}
renderer = 150
}
regularCondition {
condition = ${q(node).property('imageSize') == 'regular'}
renderer = 270
}
largeCondition {
condition = ${q(node).property('imageSize') == 'large'}
renderer = 500
}
fallback {
condition = ${q(node).property('imageSize') == ''}
renderer = 270
}
}
allowCropping = true
}
TYPO3.TypoScript:Case works like switch-function in PHP. We use this function for maximumWidth and maximumHeight. After create an condition for every option. In this condition we check which image size is selected and then write custom pixel value for width and height. With fallback condition you can define default value if image size is empty or was not selected.
The final solution may look as follows:
Example: Select Image Size
I hope this solution was helpful.
Related
I am using TYPO3 7.6 with 'CSS Styled Content'.
What I want to do is override the img tag if it is the first image listed in the content element.
So I have added a custom img tag under tt_content.image.20.1.1.layout.custom
Now how do I override tt_content.image.20.1.1.layoutKey if it is the first image?
Eg:
tt_content.image.20.1.1.layoutKey.override = custom
tt_content.image.20.1.1.layoutKey.override.if {
value = 1
equals.field = imageOrderNumber??
}
Or is there another way?
There is a register containing the current image number: register:IMAGE_NUM_CURRENT.
So it should work like this:
tt_content.image.20.1.1.layoutKey.override = custom
tt_content.image.20.1.1.layoutKey.override.if {
value = 1
equals.data = register:IMAGE_NUM_CURRENT
}
I am trying to render a set of footnotes at the end of my data set. Each footnote should be a full-width row. On the docs page for row height, it says that you can set an autoHeight property for the column you want to use to set the height. Full-width rows, however, aren't tied to any column, so I don't think there's a place to set that autoHeight property.
For reference, here is my cell renderer, which gets invoked if a flag in the data object is true.
import { Component } from '#angular/core';
import { ICellRendererComp, ICellRendererParams } from '#ag-grid-community/core';
#Component({
template: '',
})
export class FootnoteRendererComponent implements ICellRendererComp {
cellContent: HTMLElement;
init?(params: ICellRendererParams): void {
this.cellContent = document.createElement('div');
this.cellContent.innerHTML = params.data.title;
this.cellContent.setAttribute('class', 'footnote');
}
getGui(): HTMLElement {
return this.cellContent;
}
refresh(): boolean {
return false;
}
}
The footnote (the "title" property above) could be one line or several depending on its length and the browser's window size. There may also be several footnotes. Is there a way to set autoHeight for each footnote row? Thanks for any help!
Not sure of CSS autoHeight can be use, but here is some example for calculating height dynamically. Take a look to getRowHeight function, it's works for any rows (full-width too):
public getRowHeight: (
params: RowHeightParams
) => number | undefined | null = function (params) {
if (params.node && params.node.detail) {
var offset = 80;
var allDetailRowHeight =
params.data.callRecords.length *
params.api.getSizesForCurrentTheme().rowHeight;
var gridSizes = params.api.getSizesForCurrentTheme();
return (
allDetailRowHeight +
((gridSizes && gridSizes.headerHeight) || 0) +
offset
);
}
};
Here is the solution I ended up with, though I like #LennyLip's answer as well. It uses some ideas from Text Wrapping in ag-Grid Column Headers & Cells.
There were two parts to the problem - 1) calculating the height, and 2) knowing when to calculate the height.
1) Calculating the Height
I updated the footnote's Cell Renderer to add an ID to each footnote text node, and used it in the function below.
const footnoteRowHeightSetter = function(params): void {
const footnoteCells = document.querySelectorAll('.footnote .footnote-text');
const footnoteRowNodes = [];
params.api.forEachNode(row => {
if (row.data.dataType === 'footnote') { // Test to see if it's a footnote
footnoteRowNodes.push(row);
}
});
if (footnoteCells.length > 0 && footnoteRowNodes.length > 0) {
footnoteRowNodes.forEach(rowNode => {
const cellId = 'footnote_' + rowNode.data.id;
const cell = _.find(footnoteCells, node => node.id === cellId);
const height = cell.clientHeight;
rowNode.setRowHeight(height);
});
params.api.onRowHeightChanged();
}
};
To summarize, the function gets all HTML nodes in the DOM that are footnote text nodes. It then gets all of the table's row nodes that are footnotes. It goes through those row nodes, matching each up with its DOM text. It uses the clientHeight property of the text node and sets the row node height to that value. Finally, it calls the api.onRowHeightChanged() function to let the table know it should reposition and draw the rows.
Knowing when to calculate the height
When I set the gridOptions.getRowHeight property to the function above, it didn't work. When the function fires, the footnote rows hadn't yet been rendered, so it was unable to get the clientHeight for the text nodes since they didn't exist.
Instead, I triggered the function using these event handlers in gridOptions.
onFirstDataRendered: footnoteRowHeightSetter,
onBodyScrollEnd: footnoteRowHeightSetter,
onGridSizeChanged: footnoteRowHeightSetter,
onFirstDataRendered covers the case where footnotes are on screen when the grid first renders (short table).
onBodyScrollEnd covers the case where footnotes aren't on screen at first but the user scrolls to see them.
onGridSizeChanged covers the case of grid resizing that alters the wrapping and height of the footnote text.
This is what worked for me. I like #LennyLip's answer and looking more into it before I select an answer.
I'm using Vizframe component where there is two lines, when I try to show the Datalabel, the modifications is applied to both lines, I want to apply it just to one of them (A line with Datalabel set to True and a line with DataLabel set to False.)
Here's how it looks
This is how I change DataLabel property:
plotArea: {
dataLabel: {
visible: false
},
},
Well I know that this question might be older, but I had a similar issue recently. You can use the renderer function to customize the data labels to your needs.
For me, it worked, when I checked the context data for the correct measure and decide then, if I return nothing, which shows the label normally or return an empty DOM node.
dataLabel: {
visible: true,
style: {
color: "#000"
},
renderer: function (ctx) {
var node = document.createElement("text");
if (ctx.ctx.measureNames === "Optimum") {
var text = document.createTextNode("");
node.appendChild(text);
return node;
}
}
},
As you can see in the picture below, the label for the orange line (Optimum) is gone.
You can hide the values when they are overlapped using this:
plotArea: {
dataLabel: {
visible: true,
hideWhenOverlap: true
}
}
Julian's answer is useful yet in order for the change to take effect on the document we need to attach the returned node to the parent data point node.
The renderer function does not provide data regarding the graph itself but only the selected label, which leaves us clueless on how to render the node effectively.
Something like this would make me happier:
renderer: function(...) {
var parent = document.getElementById(some_given_data_point_id);
parent.appendChild(text_node);
return parent;
}
I need to provide a background color for an entire row in ag grid based on a condition in a column. I found no such examples where entire row is colored based on a certain value in a column..
The previous answer is somewhat outdated (although still correct and working) and now we have some more control over the styling of the grid. You could use getRowStyle(params) for this job, just like this:
gridOptions.getRowStyle(params) {
if (params.data.myColumnToCheck === myValueToCheck) {
return {'background-color': 'yellow'}
}
return null;
}
Obviously, myColumnToCheck would be the column you're checking your value against (the same name you input in the id/field property of the colDef object), and myValueToCheck would be the value you want said column to have to make the row all yellow.
I hope this helps others. A very common use case in any table or grid including AG Grid is going to be to set the even/odd background color of the whole row of the entire table in a performant way. ALSO, this needs to still work when SORTING.
ALL OF THESE WAYS OF DOING THIS IN AG-GRID ARE WRONG. Even though they WILL work without sort, they will not update properly when you go to use sorting. This is due to something the ag-grid team refers to in this issue https://github.com/ag-grid/ag-grid-react/issues/77 as initialization time properties.
// Initialization problem
getRowClass = (params) => {
if (params.node.rowIndex % 2 === 0) {
return this.props.classes.rowEven;
}
};
<AgGridReact
getRowClass={this.getRowClass}
>
// Initialization problem
getRowStyle = (params) => {
if (params.node.rowIndex % 2 === 0) {
return this.props.classes.rowEven;
}
};
<AgGridReact
getRowStyle={this.getRowStyle}
>
// Initialization problem
rowClassRules = {
rowEven: 'node.rowIndex % 2 === 0',
}
rowClassRules = {
rowEven: (params) => params.node.rowIndex % 2 === 0,
}
<AgGridReact
rowClassRules={this.rowClassRules}
>
// Trying to change the key so a rerender happens
// Grid also listens to this so an infinite loop is likely
sortChanged = (data) => {
this.setState({ sort: Math.random()})
}
<AgGridReact
key={this.state.sort}
onSortChanged={this.sortChanged}
>
Basically, most stuff in grid is just read once and not again, probably for performance reasons to save rerenders.
You end up with this problem when sorting when doing any of the above:
THE FOLLOWIUNG IS THE RIGHT WAY TO ACHIEVE EVEN ODD COLORING:
The correct way to add even/odd functionality in ag-grid is to apply custom css styles as follows:
You will need to overwrite/use ag variables as mentioned in the docs here:https://www.ag-grid.com/javascript-grid-styling/#customizing-sass-variables
The names of the variables in our case are
.ag-grid-even class name, or the .ag-grid-odd class name. You of course only need one if you just want an alternating color to help with visibility. For our purposes we only needed one.
Here is how this process looked in our repo:
1. Make a custom css file that overwrites/uses some of these ag- class variable names. We call it ag-theme-custom.css (I believe it needs to be a css file).
Note: We also have sass variables so this file just has a comment that this color I am adding in css is the value for our variable $GREY_100 so you don't need that part
You now will get the same result but it will still work when sorting.
Answer 2 is correct, but the syntax used is wrong, and caused me several problems trying to sort it out. Trying to minify the answer 2 code barfed, for example. It did work, but it's not proper syntax as far as I can see.
Note, this can be done inline, or with an external
function. For example an external function.
vm.gridOptions = {
columnDefs: columnDefs,
getRowStyle: getRowStyleScheduled
}
function getRowStyleScheduled(params) {
if (params.selected && params.data.status === 'SCHEDULED') {
return {
'background-color': '#455A64',
'color': '#9AA3A8'
}
} else if (params.data.status === 'SCHEDULED') {
return {
'background-color': '#4CAF50',
'color': '#F4F8F5'
};
}
return null;
};
You can add CSS classes to each row in the following ways:
rowClass: Property to set CSS class for all rows. Provide either a string (class name) or array of strings (array of class names).
getRowClass: Callback to set class for each row individually.
<ag-grid-angular
[rowClass]="rowClass"
[getRowClass]="getRowClass"
/* other grid options ... */>
</ag-grid-angular>
// all rows assigned CSS class 'my-green-class'
this.rowClass = 'my-green-class';
// all even rows assigned 'my-shaded-effect'
this.getRowClass = params => {
if (params.node.rowIndex % 2 === 0) {
return 'my-shaded-effect';
}
};
You can define rules which can be applied to include certain CSS classes via the grid option rowClassRules.
The following snippet shows rowClassRules that use functions and the value from the year column:
<ag-grid-angular
[rowClassRules]="rowClassRules"
/* other grid options ... */>
</ag-grid-angular>
this.rowClassRules = {
// apply green to 2008
'rag-green-outer': function(params) { return params.data.year === 2008; },
// apply amber 2004
'rag-amber-outer': function(params) { return params.data.year === 2004; },
// apply red to 2000
'rag-red-outer': function(params) { return params.data.year === 2000; }
};
You can't change the background color of an entire row in one command. You need to do it through the cellStyle callback setup in the columnDefs. This callback will be called per each cell in the row. You need to change the color of the row by changing the color of all the cells.
See the following column definition
{
headerName: "Street Address", field: "StreetAddress", cellStyle: changeRowColor
}
You need to do this for all your columns.
Here is your changeRowColor function.
function changeRowColor(params) {
if(params.node.data[4] === 100){
return {'background-color': 'yellow'};
}
}
It changes the color of a row if the value of the third cell is 100.
I set different color for even and odd rows you can do it in any way..
$scope.gridOptions.getRowStyle = function getRowStyleScheduled(params){
if(parseInt(params.node.id)%2==0) {
return {'background-color': 'rgb(87, 90, 90)'}
}else {
return {'background-color': 'rgb(74, 72, 72)'}
}
};
If you don't need to set the background color conditionally(based on the row data), it is not recommended to use rowStyle, as written on the row style documentation page:
// set background color on even rows
// again, this looks bad, should be using CSS classes
gridOptions.getRowStyle = function(params) {
if (params.node.rowIndex % 2 === 0) {
return { background: 'red' };
}
}
Instead, you can change the row colors using css:
#import "~ag-grid-community/dist/styles/ag-grid.css";
#import "~ag-grid-community/dist/styles/ag-theme-alpine.css";
#import "~ag-grid-community/dist/styles/ag-theme-balham.css";
#import "~ag-grid-community/src/styles/ag-theme-balham/sass/ag-theme-balham-mixin";
.ag-theme-balham {
#include ag-theme-balham((
// use theme parameters where possible
odd-row-background-color: red
));
}
If you are using AdapTable then the simplest way is to use a Conditional Style and apply it to a whole row.
The advantage of this is that it can be at run-time easily by users also.
https://demo.adaptabletools.com/style/aggridconditionalstyledemo
I have a simple question. I have a custom content area on my page called "left". Its added to the NodeType "Page" as a childNode in the yaml file:
'TYPO3.Neos.NodeTypes:Page':
properties:
[...]
childNodes:
'left':
type: 'TYPO3.Neos:ContentCollection'
In my TypoScript I added it to the page.body.content part:
page.body.content {
main = PrimaryContent {
nodePath = 'main'
}
left = ContentCollection {
nodePath = 'left'
}
}
I can add content to this new content area and it shows up in the frontend. Everything works just fine. Now I want to check if the ContentCollection of the current documentNode is empty and if this is the case I want to render the ContentCollection of the 'left' nodePath of the parent documentNode.
In other words: Subpages should render content of their parents if they dont have content on their own withing the defined content area.
How do I achieve this?
left = ContentCollection {
#override.node = ${q(node).children('left').children().count() == 0 ? q(node).parent().get(0) : node}
nodePath = 'left'
}
Is untested but should work just fine.
Note that this only goes one level up. If you need to fallback to more levels this needs to be done a bit differently.