Display user's initials on top of an SVG in ReactJS - javascript

I need the user's initials to appear on the avatars (the one in the menu is an SVG), just a grey circle with those initials. I have the function but now I have no idea how to call it in the JSX of the Dropdown menu (which is a SemanticUI library). Any cues?
const textToImage = require('text-to-image')
componentWillMount() {
let P = "", N = ""
if (res.data.Item.firstName && res.data.Item.firstName.length > 0) P = res.data.Item.firstName.charAt(0).toUpperCase()
if (res.data.Item.lastName && res.data.Item.lastName.length > 0) N = res.data.Item.lastName.charAt(0).toUpperCase()
this.setState({
initials: P + N
}, () => {
textToImage.generate(this.state.initials, { maxWidth: 30, maxHeight: 30 })
.then(dataUri => {
this.setState({ avatarInitiales: dataUri })
})
});
})
}
render() {
let avatarImage;
let userInitials;
let nomComplet;
if (this.state.user) {
avatarImage =
this.state.user.avatarImage === null || this.state.user.avatarImage === "image.jpg"
? (!this.props.pochette ?
"data:image/png;base64," + biquetteBase64
: "https://images-publiques.s3.amazonaws.com/avatar.png")
: `https://smartsplit-images.s3.us-east-2.amazonaws.com/${this.state.user.avatarImage}`;
userInitials =
this.state.user.avatarImage === null ? this.state.initials : null;
nomComplet = this.state.user.artistName
? this.state.user.artistName
: `${this.state.user.firstName} ${this.state.user.lastName}`;
}
let menu = (
<Dropdown text="" icon="angle down big black">
<Dropdown.Menu icon="down small">
<Dropdown.Item
content={nomComplet}
text={this.state.initials} //Not sure what to do around here
image={<AvatarInitialsSVG />}
/>
</Dropdown.Menu>
</Dropdown>
);
return (
<>
<div className="ui five wide column avatar--image profile"></div>
{nomComplet}
</Label>
//And here
{!userInitials && (
<img src={avatarImage} alt="user--avatar" className="user--img" />
)}
{menu}
</>
);
}

You probably need to add custom HTML + CSS, and re-use some of the classes from Semantic-UI.
Try something like this:
<Dropdown.Item>
<React.Fragment>
<div className="custom-initials-holder">
<AvatarInitialsSVG/>
<span className="custom-initials">{this.state.initials}</span>
</div>
<span className="text">{nomComplet}</span>
</React.Fragment>
</Dropdown.Item>
And your custom CSS overwrites:
.custom-initials-holder {
display: inline-block;
width: 24px;
height: 24px;
margin-right: 12px;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
}
.custom-initials-holder > svg {
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
Tweak according to your needs.
Small demo

Related

ReactJs increment translateX() on button click

Given :
function App() {
var xPos = 0;
const [style, setStyle] = React.useState({transform: `translateX(${xPos}px)`});
const onClick =(direction) => {
(direction === "left") ? xPos -= 100 : xPos += 100;
setStyle({transform: `translateX(${xPos}px)`});
console.log(xPos)
}
return (
<div className="main_container">
<button className="left_button" onClick={() => onClick("left")}>slide left</button>
<div className="forecast_slider" >
<div className="forecast_container" style={style} >
{forecastBuilder()}
</div>
</div>
<button className="right_button" onClick={() => onClick("right")}>slide right</button>
</div>
)
}
const forecastBuilder = () => {
const cell = [];
for(var i = 1 ; i < 8 ; i++){
cell.push(
<div className={i}>
{i}
<img src="https://imgs.michaels.com/MAM/assets/1/5E3C12034D34434F8A9BAAFDDF0F8E1B/img/0E9397ED92304202B4A25D7387A74515/M10118706_2.jpg" width="100" height="80" border="1px solid black" />
<br></br>
<span>day {i}</span>
</div>
)
}
return cell;
}
ReactDOM.render(<App />, document.querySelector("#app"));
.main_container {
display:flex;
}
.forecast_container {
display: flex;
width: 510px;
height: 130px;
margin-left: auto;
margin-right: auto;
align-items: center;
text-align: center;
transition: transform 250ms;
}
.forecast_slider {
background-color: black;
color: white;
overflow:hidden;
float:right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
with JSFiddle link here ,
I want to make the translateX() animation increment and decrement upon respective button click. Currently, I suspect that when I call setStyle() hook, the component gets rerendered such that the line
var xPos=0;
is read again. I was not able to find a way to increment or decrement in another way (without beforehand assigning the value of 0 such that style = {style} on the first render ignores the parameter).
Does anyone have any idea how I could solve this?
The problem is that the value of xPos is going to be set as 0 on every render, so you are not saving it's new value, it gets reset on every render.
You should store the xPos in the state as well.
const [xPos, setXpos] = useState(0)
and then increment / decrement in the function itself:
const onClick = (direction) => {
(direction === "left") ? setXpos(x => x - 100) : setXpos(x => x + 100)
}
This should work

Focused elements display wrong style

I have element with width 400% and I want to move it to left by using translateX(-(index/4)*100%) when focused index changes.
Changing focused element translateX property with tab keyboard button displays it wrong on middle elements (1,2) even though using same hardcoded styling works as expected. What am I missing here?
const {useState} = React;
const App = () => {
const [curr, setCurr] = useState(0);
const carouselStyles = {
transform: `translateX(${-(curr / 4) * 100}%)`
// uncomment to see that styling works fine with hardcoded values 1,2..
// transform: `translateX(${-(1 / 4) * 100}%)`
};
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
}
};
console.log(carouselStyles);
return (
<div>
<div className="carousel" style={carouselStyles}>
<div className="item">
11 very long text
<a href="/111" onFocus={() => handleFocus(0)}>
11111
</a>
</div>
<div className="item">
22 very long text
<a href="/222" onFocus={() => handleFocus(1)}>
22222
</a>
</div>
<div className="item">
33 very long text
<a href="/333" onFocus={() => handleFocus(2)}>
33333
</a>
</div>
<div className="item">
44 very long text
<a href="/444" onFocus={() => handleFocus(3)}>
44444
</a>
</div>
</div>
current: {curr}
</div>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
.carousel {
display: flex;
align-items: center;
width: 400%;
}
.item {
flex: 0 1 100%;
text-align: center;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I needed to prevent the scrolling and in my provided example its enough to add this line into handleFocus function
window.scrollTo(0, 0);
But in my real scenario parent wrapper also had overflow: hidden; which prevented above code from working. So I've used refs
const handleFocus = (num) => {
if (num !== curr) {
setCurr(num);
carouselRef.current.scrollTo(0, 0);
}
};
return (
<div ref={carouselRef}>
<div className="carousel" style={carouselStyles}>
...
</div>
current: {curr}
</div>
);

Scrollbar not showing all content

I have a unordered list like this,
return (
<div className="tailor-tabs">
<ul className="tailor-tab-list ">
{children.map((child) => {
const { label } = child.props;
return (
<TailorTab
activeTab={activeTab}
key={label}
label={label}
onClick={onClickTabItem}
/>
);
})}
</ul>
<div className="tailor-tab-content">
{children.map((child) => {
if (child.props.label !== activeTab) return undefined;
return child.props.children;
})}
</div>
</div>
);
And now I want to make that unordered list scrollable horizontally.
And I am making it horizontally scrollable like this,
.tailor-tab-list {
display: flex;
justify-content: center;
overflow-x: auto;
white-space: nowrap;
}
::-webkit-scrollbar {
display: none;
}
but the problem here is I cannot see the whole list, it shows most of the items but some items are hidden and can not be scrolled. like this,
Why is that? Any help?
After the list is made, it shows like this

How to find the index of an event target's parent node in JS?

When the page loads, an array is made that holds a group of divs and is used as a global variable catGroup. Each div has a button as a child, and when clicked, the target is saved as a global variable targ. What I'm trying to do is determine the index of the button's parent node every time it is clicked. I haven't been able to find a way to make this happen. Any help is appreciated.
i = catGroup.findIndex(node => node == targ.parentNode);
Wrap an element around all <div>s then register it to the click event:
document.querySelector('main').onclick = clickHandler;
Get the <div> of the button clicked by user:
const clicked = e.target;
const act = clicked.parentElement;
Collect all <div> that are direct descendants of <main>:
const divs = [...this.querySelectorAll('main > div')];
Remove '.active' from all <div> and then add .active to the parent <div> of clicked button:
divs.forEach(d => d.classList.remove('active'));
act.classList.add('active');
Filter the array of divs and return it's index:
idx = divs.flatMap((d, i) => d.className === 'active' ? i : []);
idx = divs.findIndex(d => d === act);
// As Peter Seliger suggested -- I brain farted
const main = document.querySelector('main');
main.onclick = getIndex;
function getIndex(e) {
let idx;
const clicked = e.target;
const divs = [...this.querySelectorAll('main > div')];
if (clicked.matches('[type="button"]') || clicked.matches('button')) {
const act = clicked.parentElement;
divs.forEach(d => d.classList.remove('active'));
act.classList.add('active');
idx = divs.findIndex(d => d === act);
}
document.querySelector('section').textContent = idx;
}
html {
font: 2ch/1 Consolas;
}
body {
position: relative;
height: 100vh
}
main {
padding: 5px 20px 10px;
}
section {
position: fixed;
top: 0;
left: 50%;
font-size: 5rem;
}
div {
width: 8.5ch;
text-align: center;
}
[type='button'], button {
display: block;
width: 10ch;
height: 3ch;
cursor: pointer;
}
<main>
<section></section>
<div><input type='button' value='0'></div>
<div><input type='button' value='1'></div>
<div><input type='button' value='2'></div>
<div><button>3</button></div>
<div><input type='button' value='4'></div>
<div><button>5</button></div>
<div><input type='button' value='6'></div>
<div><input type='button' value='7'></div>
<div><button>8</button></div>
<div><input type='button' value='9'></div>
</main>
This worked for me in a mouseover event. That's how I did it in my case:
onMouseOver={(e) => { const idx = Array.from(e.target.parentNode.children).indexOf(e.target); }}

How to design a multi tab chat with react/redux?

I am developing a chat platform to take multiple chats and he can switch between the chats.
var MessageList = React.createClass({
render() {
return (
<div className='messages'>
<h2> Conversation: </h2>
{
this.props.messages.map((message, i) => {
return (
<Message
key={i}
user={message.user}
text={message.text}
/>
);
})
}
</div>
);
}})
Let's just take an example of message list which will change when user switches the chat tab. Re rendering the same component with new messageList with respect to the chat makes sense but when there are 100 other component changes like this when there is a switch in chat, then there will be lot of repainting/rendering.(I know only diff will be changed in the dom but still.)
I would like to create different elements for different chats and hide and show them based on active chat. But react works under a single dom and replaces the dom with what's returned where it has been attached to.
React.render(<ChatApp/>, document.getElementById('app'));
Can anyone help me up with the design here?
Thanks in advance.
var Tabs = React.createClass({
displayName: 'Tabs',
propTypes: {
selected: React.PropTypes.number,
children: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.element
]).isRequired
},
getDefaultProps: function () {
return {
selected: 0
};
},
getInitialState: function () {
return {
selected: this.props.selected
};
},
shouldComponentUpdate(nextProps, nextState) {
return this.props !== nextProps || this.state !== nextState;
},
handleClick: function (index, event) {
event.preventDefault();
this.setState({
selected: index
});
},
_renderTitles: function () {
function labels(child, index) {
var activeClass = (this.state.selected === index ? 'active' : '');
return (
<li key={index}>
<a href="#"
className={activeClass}
onClick={this.handleClick.bind(this, index)}>
{child.props.label}
</a>
</li>
);
}
return (
<ul className="tabs__labels">
{this.props.children.map(labels.bind(this))}
</ul>
);
},
_renderContent: function () {
return (
<div className="tabs__content">
{this.props.children[this.state.selected]}
</div>
);
},
render: function () {
return (
<div className="tabs">
{this._renderTitles()}
{this._renderContent()}
</div>
);
}
});
var Pane = React.createClass({
displayName: 'Pane',
propTypes: {
label: React.PropTypes.string.isRequired,
children: React.PropTypes.element.isRequired
},
render: function () {
return (
<div>
{this.props.children}
</div>
);
}
});
var App = React.createClass({
render: function () {
return (
<div>
<Tabs selected={0}>
<Pane label="Tab 1">
<div>This is my tab 1 contents!</div>
</Pane>
<Pane label="Tab 2">
<div>This is my tab 2 contents!</div>
</Pane>
<Pane label="Tab 3">
<div>This is my tab 3 contents!</div>
</Pane>
</Tabs>
</div>
);
}
});
ReactDOM.render(<App />, document.querySelector('.container'));
* {
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
}
body {
font: 300 14px/1.4 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: #eee;
margin: 0;
padding: 0;
}
.tabs {
margin: 25px;
background: #fff;
border: 1px solid #e5e5e5;
border-radius: 3px;
}
.tabs__labels {
margin: 0;
padding: 0;
}
.tabs__labels li {
display: inline-block;
}
.tabs__labels li a {
padding: 8px 12px;
display: block;
color: #444;
text-decoration: none;
border-bottom: 2px solid #f5f5f5;
}
.tabs__labels li a.active {
border-bottom-color: #337ab7;
}
.tabs__content {
padding: 25px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div class="container"></div>
You are saying redux, so I'll try to give some insight but too lazy to provide any code, since it'll be too big / complex.
Beforehand, if needed, you can use normalizr when handling with nested JSON format since redux love to be immutable and nested makes it harder to be immutable.
The reducers:
chats { userid:"", message:"", time:"" },
users { userid:"", name:"" },
app { selectedUserId:"" }
Now, the number of tabs to be rendered / displayed is equals to number of users. The selected tab is based on app.selectedUserId. The message rendered in panel will be chats, which userid equals app.selectedUserId. Some snippet:
var lo = require('lodash');
var selectedChats = lo.filter(this.props.chats, k => k.userid == app.selectedUserId);
var messagesDom = selectedChats.map(k => <MessageLine chat={k});
var chatTabsDom = this.props.users.map(k => <ChatTab userid={k.userid} className={ k.userid == app.selectedUserId ? "active" : "" }> );
return <div>
<Tabs>{chatTabsDom}</Tabs>
<Messages>{messagesDom}</Messages>
</div>;

Categories