ReactJS multiline textarea with ellipsis - javascript

I'm trying to build a component with multiline textfield. If the text entered exceeds 2 lines, then I'd like to add the ellipsis (...) for text-overflow.
How can I achieve this by just manipulating the css to show the ellipsis in display only but not modify the actual text that will be stored to contain '...'?
I'm using this React component link
Thanks

I just figured out how to solve this for React.
As Khodor mentioned, line-clamp is what you want. However, it's not currently supported by the official CSS spec. So, you can use -webkit-line-clamp as a sort of workaround. However, I struggled to figure out the exact syntax needed for React. I eventually figured out it out by peeking at the source code for this NPM package react-lines-ellipses and searching for 'webkit' in his github repo.
The React-specific CSS
const textStyle = {
maxWidth: '100%',
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 3,
overflow: 'hidden',
textOverflow: 'ellipsis',
};
I set the maxWidth to ensure the text filled the whole width of the display element. This is optional.
overflow: 'hidden' hides the extra text beyond the 3 lines (I chose 3 at random).
textOverflow: 'ellipses' adds an ellipses (...) to the end of the line, where it gets cut off.
The JSX
<div
onClick={toggleTruncate}
style={calculateTextStyle()}
>
This is where my long text goes.
</div>
// This function returns the correct style to the above div.
function calculateTextStyle() {
return truncate ? textStyle : null;
}
// I used React Hooks to create a variable in state to manage if the text should be truncated or not.
const [truncate, setToggleTruncate] = React.useState(true);
// This function toggles the state variable 'truncate', thereby expanding and truncating the text every time the user clicks the div.
function toggleTruncate() {
setToggleTruncate(!truncate);
}

for CSS only, you can use line-clamp, though it doesn't have the best browser support
Check this codepen for implementation.
display: block; /* Fallback for non-webkit */
display: -webkit-box;
max-width: 400px;
height: $font-size*$line-height*$lines-to-show; /* Fallback for non-webkit */
margin: 0 auto;
font-size: $font-size;
line-height: $line-height;
-webkit-line-clamp: $lines-to-show;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;

The javascript for this could look something like below. You take the value, split it into lines, and if there's more than one line, you wrap the following lines in parentheses.
The React component your using appears to take in an onChange prop, which could use a similar function.
const textAreaElement = document.getElementById('t')
textAreaElement.addEventListener('keyup', () => {
const value = textAreaElement.value.replace(/[\(\)]/g, '')
const splitLines = value.split(/(?:\r\n|\r|\n)/)
const newValue = splitLines.length > 1 ?
`${splitLines[0]}\n(${splitLines.slice(1, splitLines.length).join('\n')})` : splitLines[0]
textAreaElement.value = newValue;
});
<textarea id="t"></textarea>

An easy implementation for multi-line ellipsis will be using antd typography component. You can provide a prop called ellipsis with the value of number of rows after which it should be truncated.
<Paragraph ellipsis={{ rows: 3, expandable: true }}>
Ant Design, a design language for background applications, is refined by Ant UED Team.
Ant Design, a design language for background applications, is refined by Ant UED Team.
Ant Design, a design language for background applications, is refined by Ant UED Team.
Ant Design, a design language for background applications, is refined by Ant UED Team.
Ant Design, a design language for background applications, is refined by Ant UED Team.
Ant Design, a design language for background applications, is refined by Ant UED Team.
</Paragraph>

Related

Multiline tooltip using only React

I'm trying to build a multiline tooltip using [this example], I'm using it as a refernce because it involves functional components.
It works just fine, when its a short line of text, but doesn't go on a new line, which is something that I need.
I tried adding < /br > inside of the content, changing the content to jsx format and adding code below to the css file:
width: 200px;
word-wrap: break-word;
All it managed to do was decrease the tooltip backgronud width to 200px.
How do I change code in the example to go on a new line if content size width is going above the predetermined width?
How do I change code in the example to go on a new line if content size width is going above the predetermined width?
Besides setting the width to the e.g. 200px, you should also remove the white-space: nowrap; property because that is simply disallowing the text to break, even if it does overflow the container.
Codesandbox: https://codesandbox.io/s/how-to-make-an-extremely-reusable-tooltip-component-with-react-and-nothing-else-forked-v09e4y
More info about white-space property: https://developer.mozilla.org/en-US/docs/Web/CSS/white-space

why isnt the flex attribute working in react styled components?

Okay so my code isn't complex at all. I am watching a tutorial on react styled components and my code got to this point:
const Container = styled.div
`height: 60px;
`
const Wrapper = styled.div`
padding : 10px 20px;
display : flex;
justify-content : space-between;
`
const Left = styled.div`
flex : 1;
`
const Center = styled.div`
flex: 1;
`
const Right = styled.div`
flex : 1;
`
This is the rest of the code:
const Navbar = () => {
return (
<Container>
<Wrapper>
<Left>loremmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm</Left>
<Center>Center</Center>
<Right>Right</Right>
</Wrapper>
</Container>
)
}
export default Navbar
So what is basically happening is that i created 3 components on the same line named Left, Center and Right components and i put them inside a Wrapper component. What the flex attribute is meant to do is to give Left, Center and Right components equal space on the browser. Meaning that if there is some long string inside Left (as seen above), the Center and Right components maintain their positions and are not pushed to the side to accommodate Left. Its meant to be working but when i open my browser, this is what i see:
loremmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmCenterRight
meaning that the flex attribute is obviously not working? so anyone knows what i am doing wrong? and btw, the tutorial i am watching just came out in September 2021 so i doubt its because there's a change in the syntax or something, but if it is the case then please point it out.
That is because flex attempts to size according to your proportions by organizing around element boundaries - or words in case of text.
But it is not an absolute rule. Since you're having a gigantic word that cannot be wrapped flex does the best it can do under this condition. Similar to how you would usually not want to break up or hide parts of a large button to respect proportions at all costs. This is called overflow.
Your CSS should work if you have different paragraph sizes with normal words.
One option is to hide overflow or show a scroll bar:
const Left = styled.div`
flex : 1;
overflow: hidden; # also scroll
# text-overflow: ellipsis; # Optional to show a nicer "Loremmmmm..."
`
Alternatively you can permit wrapping on letter boundary - breaking up long words will also make flex proportions achievable:
const Left = styled.div`
flex : 1;
overflow-wrap: anywhere;
`

How to apply styling for 3rd party css framework like bootstrap

Im working on react web app which has less file for its styling. As
shown below, EnPage is a 3rd party component, which has content within
it, Actually the main element class "page-body" has some styling
issue, so I want to overwrite it with a styling fix
<div class="Banner">
<EnPage>
<div class="page">
<main class="page-body"> ...</main>
</div>
</EnPage>
</div>
when on hovering over in chrome devtools, I can see
.page-body {
padding-right : var( --page-content-screen-lg-horizontal-padding , var(--spacing-m));
padding-left : var( --page-content-screen-lg-horizontal-padding , var(--spacing-m));
}
In dev tools, if set these both attributes to 0, then it fixes styling
issue
.page-body {
padding-right : 0;
padding-left : 0;
}
Now how to do this code , like the below?
.Banner {
--page-content-screen-lg-horizontal-padding : 0;
}
Generally third parts materials generate custom classes that style your element. Normally, their classes are inyected after yours, to be sure that their styles have precedence over inherited or previously defined styles.
Things you should try:
1 - Read the documentation of the material library.
Depending on the material library you are using, they may provide a custom way to overpass their basic styles. Some do, other don't. Please be sure to check their documentation to see if this is the case.This is always the best option as you are ensuring the material will work as designed and will not cause any bugs or conflicts.
2 - Give an id to your element and place your custom styles on the id.
This works because CSS styles are defined based on specificty precedence. As ids are more specific than classes, these styles have priority over the ones defined by classes.
Example:
html:
<main class="page-body" id="page-body"> ...</main>
css:
#page-body {
padding-right: 0;
padding-left: 0;
}
3 - If nothing else seems to work and you really need to replace the material style, you could use !important. But please note that this is a bad practice and many state that !important really shouldn't exist in the first place, as if your need to use it is because you are not understanding css precedences rules and you are just hacking the css logics.
Putting this duscission aside, you may place !important after your declaration and this is going to enforce your rule over any other that might exist.
Example:
.page-body {
padding-right: 0 !important;
padding-left: 0 !important;
}
Did I mention this is a bad idea?
If you want to read more about css precedence:
What is the order of precedence for CSS?
https://css-tricks.com/precedence-css-order-css-matters/

How to wrap column header in ag-grid using angular

I have some columns which has four words i.e Pre Trading Follow Up, Post Trading Follow Up and some of them having three words. I tried the below css to wrap the text to multiple lines.
::ng-deep .ag-theme-material .ag-header-cell-label .ag-header-cell-text{
white-space: normal;
overflow-wrap: break-word;
}
HTML
<ag-grid-angular class="ag-theme-material" [rowData]="rowData" [columnDefs]="columnDefs" [overlayLoadingTemplate]="overlayLoadingTemplate" [domLayout]="domLayout" [enableSorting]="true" (gridReady)="onGridReady($event)" (gridOptions)="gridOptions" >
</ag-grid-angular>
but the column header remains the same. I wanted to wrap the column header text to multiple lines. Is there anyway to do this?
Note: I can able to wrap the content using cellStyle: {'white-space': 'normal'}
{headerName: 'headername', field: 'headerfield', autoHeight:true, width: 100, cellStyle: {'white-space': 'normal'}},
But I wanted to wrap the header.
Please review the following stackblitz example.
https://stackblitz.com/edit/angular-ag-grid-angular-xmbm3p?embed=1&file=styles.css
In the global style sheet I applied the following... you could use ::ng-deep in your component css, this is the first stackblitz I could find with ag-grid to fork and is not mine so there was no component css to use.
.ag-header-cell-label .ag-header-cell-text {
white-space: normal !important;
}
The next piece is to use property headerHeight
this.gridOptions = <GridOptions>{
headerHeight:75,
This part unfortunately is unavoidable... it also doesn't allow for you to make the header height dynamic based on the word wrap requirements.
The reason why is that the content area is defined with top style dynamically when the view is rendered; adjusting the header height via ::ng-deep will not dynamically
shift the top of the content area down as it is calculated by the
headerHeight property... if undefined the default is 25px so the top for content area is also 25px.
Not to
mention that the z-index of the content area causes it to overlap the
header when you change the height with ::ng-deep.. so you don't know if ::ng-deep truly worked... visually that is... as the header extends under the content area.
Sorry to say but this will be as close as you can get... adjusting all elements, shifting down the top etc based on a dynamic header height via DOM manipulation I fear will just get too ugly... and if you need the header height dynamic to the point this is a show stopper... it may be best to explore other options as a replacement to ag-grid.
https://www.ag-grid.com/javascript-grid-column-header/#headerHeight
To achieve expected result , use below option of using line break tag - br in column definitions for that specific column headerName
{headerName: 'Pre<br>Trading<br> Follow<br> Up', field: 'headerfield', autoHeight:true, width: 100, cellStyle: {'white-space': 'normal'}}
Angular has encapsulation.
But ag-grid doesn't care encapsulation. so we need to set .ag-header-cell-label and .ag-header-cell-text globally.
We set ViewEncapsulation.None at Component property and write css or scss, this can set css globally.
Global css is applied all ag-grid component. If you want to set .ag-header-cell-label and .ag-header-cell-text only one ag-grid component, set headerClass at gridOptions
this.gridOptions = {
defaultColDef: {
headerClass: 'myfunc-default-header'
},
headerHeight:75
};
And write scss file like this.
.myfunc-default-header {
.ag-header-cell-label .ag-header-cell-text {
white-space: normal !important;
}
}
this can only apply .ag-header-cell-label and .ag-header-cell-text inside the my-func-deafult-header.
So we can apply only one ag-grid component.

How to get a dynamic height when clicking again on a toggled div

I have this code:
$(document).ready(function(){
/* SET VAR FOR IS OPEN */
var isopen = 0;
/* SHOW FIRST NEWS ON STARTUP EXTENDED */
/* SET ALL .TRIGGER PARENT .CONTAINER TO FIXED HEIGHT WITH CUTTED CONTENT (OVERFLOW) */
$('.trigger:not(:first)').css({
height: "70",
overflow: "hidden"
}, 200 );
/* CLOSE THE CLICKED ELEMENT */
$('.trigger').click(function() {
if (isopen == 0) {
// SET ALL TRIGGER TO 70PX HEIGHT
$('.trigger').css({overflow: "hidden"}).animate({
height: "70",
}, 200 );
$(this).animate({
height: "350",
}, 200 );
} else {
alert('this Alert shuld show up if isopen=1');
}
});
})
Its a news content of a website.
The user see three divs.
2 are cutted to 70px Height
The first news is extended to its original height.
after click the height: auto; does not work. So the height is set to 350px. How to get a dynamic height? Thanks!
Difficult to 100% verify my answer as I would need a fiddle/plunker with your HMTL/CSS/JS to work with, but here is my suggestion that I think will help.
Rather than explicitly setting CSS styles to the elements via JavaScript, via methods like 'css(value, property)', instead add or remove classes to the elements via 'addClass()', 'removeClass()', or 'toggleClass()'.
New look JS:
$('.trigger').click(function() {
$(this).toggleClass('is-closed');
}
New look CSS:
.trigger {
// Your current UI component styling, but no height specified
}
.trigger.is-closed {
height: 70px;
overflow: hidden;
}
The difference here being, is that you're not trying to assert "height: auto" as an overriding style for "height: something else", you are simply adding and withdrawing the fixed height on open/close - which I would suggest is much less error prone much more likely to bring about the behaviour that you want.
Furthermore, this is also a good practice to follow in any event because of the following reasons:
Separation of concerns, your styling belongs in your CSS files rather than JavaScript (i.e. component styles all together)
Reusable code, these styles reflecting the "closed" state could be re-used across all instances of this UI component, as well as others components, rather than re-written every time in JS click handler functions
Modifying CSS via jQuery functions such as 'css(property, value)' is a bad idea, it results in the styles added as inline styles in the DOM (e.g. style="height: 70px;") and this will take priority over other CSS, making managing your CSS harder and debugging presentation errors more difficult
This approach also has the added benefit of reducing the length and clarifying your JavaScript code significantly.
Height Animation
The above code will not provide the height animation that you currently have.
The solution here is to adopt a CSS transition approach rather than animating via JavaScript. Article: http://www.w3schools.com/css/css3_transitions.asp
This will again mean that all of your component related styling will remain together in one place, but CSS animation also performs much better/renders faster than JavaScript animation does.
Hope this helps.
Setting css height in javascript is basically adding an element style tag to it, which will override the css file always due to the rendering rules of css. If you set the value to an explicit height, to get it back to height: auto, you either have to write height: auto to it in the javascript or remove the style element you added completely.

Categories