Search bar in React TS not working as expected - javascript

I used this example snippet written in ReactJS to try and create a search box in my website, which is written in React TS
https://codemyui.com/input-highlight-seen-tripadvisor/
However when I have tried to convert it, I get the following issues
1) Input is read only, ie I cannot type into it
2) I am not sure what to do with this bit of the style in the original CSS, as it does not compile when I paste it into my CSS
&:focus {
+ .input-highlight {
border-top: 3px solid #fbc91b;
}
}
Can someone point out what I have done wrong in the conversion to TS?
My React control
import * as React from 'react';
import './App.css';
interface ISearchBarState {
inputValue : string
}
class SearchBar extends React.Component<{}, ISearchBarState> {
constructor(props: any) {
super(props);
this.state = {
inputValue: ''
};
this.onInputChange = this.onInputChange.bind(this);
}
public render() {
const { inputValue } = this.state;
return (<div className='input-wrapper'>
<input
placeholder='Search...'
value={inputValue}
spellCheck={false}
/>
<span className='input-highlight'>
{ inputValue.replace(/ /g, "\u00a0") }
</span>
</div>);
}
private onInputChange(e: any) {
const { value } = e.target;
this.setState({
inputValue: value
});
}
}
export default SearchBar;
and my CSS
.input-wrapper {
position: relative;
width: 350px;
margin-left: 5px;
}
.input-highlight {
font-size: 16;
user-select: none;
line-height: 20;
border-top: 1px solid white;
position: absolute;
left: 0;
bottom: 0;
color: #999999;
max-width: 100%;
height: 0;
color: transparent;
font-family: Roboto Slab, sans-serif;
overflow: hidden;
border-top: 3px solid #fbc91b;
}
input {
height: 30px;
width: 100%;
min-width: 100%;
padding: 0;
border-radius: 0;
line-height: 2;
background-color: transparent;
color: #999999;
font-size: 16;
border: none;
outline: none;
border-bottom: 1px solid #666666;
font-family: Roboto Slab, sans-serif;
}

It looks like the bit of css in question
&:focus {
+ .input-highlight {
border-top: 3px solid #fbc91b;
}
}
is actually scss, you'd need to compile that bit to css first or manually compile it to css. Here, below I help to manually change it to css for you
input:focus .input-highlight{
border-top: 3px solid #fbc91b;
}
you can add this css snippet to your css file, put it below the input css block.

Related

Changing text inside upload button after uploading

I have the following code:
<div className='file-upload-wrapper' data-text='Select your file!'>
<input
type='file'
id='upload'
className='file-upload-field'
placeholder='Enter Item Name'
required
onChange={(e) => setUpload(e.target.value)}
onClick={changeUploadTxt}
></input>
</div>
The CSS for this:
.file-upload-wrapper {
position: relative;
width: 400px;
height: 40px;
}
.file-upload-wrapper::after {
content: attr(data-text);
font-size: 18px;
position: absolute;
top: 0;
left: 0;
background: #fff;
padding: 10px 15px;
display: block;
width: calc(100% - 40px);
pointer-events: none;
z-index: 20;
height: 40px;
line-height: 40px;
color: #999;
border-radius: 5px 10px 10px 5px;
font-weight: 300;
}
.file-upload-wrapper::before {
content: "Upload";
position: absolute;
top: 0;
right: 0;
display: inline-block;
height: 60px;
background: #4daf7c;
color: #fff;
font-weight: 700;
z-index: 25;
font-size: 16px;
line-height: 40px;
padding: 0 15px;
text-transform: uppercase;
pointer-events: none;
border-radius: 0 5px 5px 0;
}
.file-upload-wrapper::hover {
background: darken(#4daf7c, 40%);
}
I was trying to do something with this, but it didn't worked:
const changeUploadTxt = () => {
//const txtDiv = document.getElementsByClassName("file-upload-wrapper");
};
I also changed the CSS content with before and after, but it didn't worked. Do you know a method for making sure that files are uploaded and change the text after inside the div tag?
This isn't how React works. Don't think of it in terms of directly manipulating the DOM in an event handler. Instead, think of it in terms of managing state.
The render operation displays a state value
The event updates the state value
So in the start of your component (assuming a function component, since that's the preferred approach for new development at this time) you might declare a state value:
const [uploadText, setUploadText] = useState('Select your file!');
Then you'd use that value in your rendering:
<div className='file-upload-wrapper' data-text={uploadText}>
Now all you have to do is update the state value any time you want it to change:
const changeUploadTxt = () => {
setUploadText('Some new value');
};
Any time you update state, that will trigger the component to re-render. Which is also why you don't directly manipulate the DOM in React (unless you really know what you're doing under the hood in the framework), because a re-render will break whatever changes you made to the DOM.
you can use innerHtml for adding a text.
const changeUploadTxt = () => {
document.getElementsByClassName("file-upload-wrapper").innerHTML += "Upload";
};

Style directly in JSX, changing when the value changes

i'm creating a simple login/signup form in react. The idea behind this form, is that above the form you have two button, one saying login, and the other sign-up. If you click on the login, you are in the login. But if you click on signup, it renders the signup page. To let user see where they are currently, under the two buttons there is a line that should change position when a button is clicked. Here is the react and css code:
React:
import React, { useState, useEffect } from 'react';
import '../styles/style.css';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [actMargin, setActMargin] = useState('0px');
const handleSubmit = (e) => {
e.preventDefault();
console.log('Submitted')
};
const changeHandlerL = () =>{
setActMargin('0px');
console.log(actMargin);
}
const changeHandlerS = () => {
setActMargin('77px');
console.log(actMargin);
}
return <form className='containerLogin' onSubmit={handleSubmit}>
<h1>Login</h1>
<div className="choose">
<button id="logS" onClick={changeHandlerL}>Login</button>
<button id="sigS" onClick={changeHandlerS}>Sign Up</button>
<div className="linea" style={{"margin-left":{actMargin}}}></div>
</div>
<p>Email:</p>
<input type="text" name="email" value={email} onChange={(e)=>setEmail(e.target.value)}/>
<p>Password:</p>
<input type="password" name="password" value={password} onChange={(e)=>setPassword(e.target.value)}/>
<button className="sign">Forgot Password?</button>
<button type="submit" id="sub" >Login</button>
<p>Don't have an account?<button className="sign">Sign up</button></p>
</form>
}
export default Login;
CSS:
#import url('https://fonts.googleapis.com/css2?family=Mochiy+Pop+P+One&display=swap');
:root {
--black: #000000;
--white: #ffffff;
--dark-blue: #1a3491;
--night: #050f30;
--grey: #6b728c;
}
body{
background: linear-gradient(to left, var(--night), var(--dark-blue));
}
*:focus{
outline: none;
}
.containerLogin{
background:var(--white);
display: flex;
flex-direction:column;
letter-spacing:1px;
margin:auto;
padding: 30px;
text-align:center;
font-family: 'Mochiy Pop P One', sans-serif;
margin-top: 150px;
border: var(--white) solid 2px;
max-width: 450px;
border-bottom-left-radius: 30px;
border-top-right-radius: 30px;
}
.containerLogin h1{
margin-bottom: 50px;
}
.containerLogin .choose{
margin:auto;
display: flex;
flex-direction: row;
margin-bottom: 50px;
border-radius: 5px;
}
.containerLogin .choose button{
border: none;
background: var(--white);
}
.containerLogin p{
margin-bottom: 20px;
}
.containerLogin input, .containerLogin input:focus{
margin-bottom: 20px;
border: none;
border-bottom: 2px solid black;
font-size:12pt;
background: var(--white);
}
#sub{
margin: auto;
margin-top: 20px;
max-width: 100px;
background: var(--dark-blue);
color:var(--white);
padding: 8px;
border-radius: 10px;
border:none;
transition:1s;
}
#sub:hover{
background-color: var(--night);
}
.sign{
padding: 8px;
border-radius: 10px;
border:none;
background: var(--white);
color:var(--dark-blue);
transition: 1s;
}
.sign:hover{
background: var(--dark-blue);
color:var(--white);
}
#logS, #sigS{
border: none;
padding:10px;
}
.linea{
position:absolute;
width: 70px;
height: 6px;
margin-top: 40px;
background: var(--dark-blue);
border-radius: 2px;
transition:1s;
}
The state I created should track the pixels of marign left that the line has. In the console, i can see that the state is changing correctly, but nothing changes. I guess it is a problem with the style syntax, as I read in some other posts, but I can't fix it. Can someone help me? Thanks
Another things: can I use ternary operators inside a style tag in JSX? For example:
<div style={{'background':{isMorning ? 'white': 'black'}}}>CIAO</div>
I ask because it shows that it is uncorrect on VSCODE. Thank you so much
css and div changes would work, needs more edit on it logically
.noMargin {
margin-left: 0px
}
.hasMargin {
margin-left: 77px
}
<div className={actMargin==='0px' ? "noMargin" : "hasMargin" }></div>
you can very well change logic of actMargin
Could you try it with
<div className="linea" style={{"margin-left":{actMargin}}}></div>
changed to
<div className="linea" style={{marginLeft : actMargin }}></div>
The syntax seems to be wrong
Ternaries are definitely wrong too, that is why VS code is yelling an error.
<div style={{'background':{isMorning ? 'white': 'black'}}}>CIAO</div>
Should be
<div style={{'background': ( isMorning ? 'white': 'black' ) }}>CIAO</div>

Convert Class from single Js web page to ReactJs

I'm new to react, and I'm working on a small project that uses a search bar in Single js Web page to find data from API.
The main code for this component is:
const main = () => {
const searchElement = document.querySelector("search-bar");
const clubListElement = document.querySelector("club-list");
const onButtonSearchClicked = async() => {
try{
const result = await DataSource.searchClub(searchElement.value);
renderResult(result);
} catch (message) {
fallbackResult(message)
}
};
const renderResult = (results) => {
clubListElement.clubs = results;
};
const fallbackResult = message => {
clubListElement.renderError(message);
};
searchElement.clickEvent = onButtonSearchClicked;
};
export default main;
also my Search-Bar component:
class SearchBar extends HTMLElement{
constructor(){
super();
this.shadowDOM = this.attachShadow({mode: "open"});
}
connectedCallback(){
this.render();
}
set clickEvent(event){
this._clickEvent = event;
this.render();
}
get value(){
//return this.querySelector("#searchElement").value;
return this.shadowDOM.querySelector("#searchElement").value;
}
render(){
//this.innerHTML = `
this.shadowDOM.innerHTML = `
<style>
.search-container {
max-width: 800px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
padding: 16px;
border-radius: 5px;
display: flex;
position: sticky;
top: 10px;
background-color: white;
}
.search-container > input {
width: 75%;
padding: 16px;
border: 0;
border-bottom: 1px solid cornflowerblue;
font-weight: bold;
}
.search-container > input:focus {
outline: 0;
border-bottom: 2px solid cornflowerblue;
}
.search-container > input:focus::placeholder {
font-weight: bold;
}
.search-container > input::placeholder {
color: cornflowerblue;
font-weight: normal;
}
.search-container > button {
width: 23%;
cursor: pointer;
margin-left: auto;
padding: 16px;
background-color: cornflowerblue;
color: white;
border: 0;
text-transform: uppercase;
}
#media screen and (max-width: 550px){
.search-container {
flex-direction: column;
position: static;
}
.search-container > input {
width: 100%;
margin-bottom: 12px;
}
.search-container > button {
width: 100%;
}
}
</style>
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit">Search</button>
</div>
`;
this.shadowDOM.querySelector("#searchButtonElement").addEventListener("click", this._clickEvent);
}
}
customElements.define("search-bar", SearchBar);
furthermore, can i convert this method to ReactJS? because we know if we can't declare a const in render() from React.
I have been through a bunch of flustered, and I'm not exactly sure how to go about doing that.
Can anyone help me with this please? Any other comments on the code are also welcome because i'm new in Reacts
Thank you before!
First and foremost, you should remove all that CSS declaration within the render method, and abstract them into a separate CSS file (you can consider CSS modules, which is supported by React), use CSS-in-JavaScript libraries(such as styled-components), or inline styles.
Next, instead of using event listeners, you should bind the button element's onClick event with the onButtonSearchClicked method, which is similar to what you have defined.
From that method, you will make the API request, and update your component's state with the response.
class SearchBar extends React.Component {
async onButtonSearchClicked() {
const result = await DataSource.searchClub(searchElement.value);
// set state with return result.
}
render() {
return (
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit" onClick={() => this.onButtonSearchClicked}>Search</button>
</div>
);
}
}
One of many variants how could SearchBar component look like in React
class SearchBar extends React.Component {
constructor() {
this.ref = React.createRef(null);
}
componentDidMount() {
this.shadowDOM = this.ref.current.attachShadow({mode: "open"});
this.shadowDOM.innerHTML = getStyle();
}
onClick(event) {
}
render() {
return <div ref={this.ref} id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button onClick={this.onClick} id="searchButtonElement"
type="submit">Search</button>
</div>
}

Drop-down list with bullets

I want to create a drop-down list with bullets using Angular 2 and JavaScript & CSS. I created a drop-down list but i couldn't able to create bullets in list.
This can't be achieved by jQuery, Bootstrap. Any suggestions?
Here is my Code.
dropdownList.component.ts
import { Component} from '#angular/core';
import{FormsModule } from '#angular/forms';
import {Option} from './option';
#Component({
selector: 'app-dropdown',
templateUrl: './dropdown.component.html',
styleUrls: ['./dropdown.component.css']
})
export class DropdownComponent {
selectedItem:Option= new Option(1,'../../assets/blue2.png','option1');
options= [
new Option(1,'../../assets/blue2.png','option1'),
new Option(2,'option2'),
new Option(3,'option3'),
new Option(4,'option4')
];
// OPtion.ts
export class Option{
constructor(public id?: number, public img?:string, public name?:
string )
{
}
}
// component.html:
<select class="dropdown" id="style">
<option *ngFor="let Option of options" value={{Option.id}}
class="dropdownList">{{Option.name}}{{Option.img}}
</option>
</select>
CSS can make anything looked like other stuff. I implement a really simple dropdown but only really basic features. To complete this dropdown you still need to implement ngModel and label/value dropdown items. But I think this have what you descibed already.
import { Component } from '#angular/core'
#Component({
selector: 'demo-dropdown',
template: `
<div class="ui-dropdown">
<label class="ui-dropdown-label">{{selectedItem}}</label>
<span class="ui-dropdown-button" (click)="showList = !showList">﹀</span>
<ul class="ui-dropdown-list" [class.active]="showList">
<li *ngFor="let item of items" (click)="onItemSelected($event)">{{item}}</li>
</ul>
</div>
`,
styles: [`
*{
box-sizing: border-box;
}
.ui-dropdown{
background: #fff;
border: 1px solid black;
border-radius: 3px;
display: inline-flex;
flex-wrap: wrap;
padding: 0;
user-select: none;
width: 250px;
}
.ui-dropdown-label{
flex: 1 1 0;
padding: 0.2em 0.5em;
}
.ui-dropdown-button{
border-left: 1px solid black;
cursor: pointer;
flex: 0 0 20px;
padding-top: 0.2em 0.2;
}
.ui-dropdown-list{
background: #fff;
border: 1px solid black;
border-radius: 3px;
display: none;
flex: 0 0 100%;
margin: 0;
margin-top: 25px;
padding: 0;
position: absolute;
width: 250px;
}
.ui-dropdown-list.active{
display: block;
}
.ui-dropdown-list>li{
cursor: pointer;
list-style: none;
}
.ui-dropdown-list>li::before{
content: '.';
}
.ui-dropdown-list>li:hover{
background: #eee;
}
`]
})
export class DemoDropdown{
showList:boolean = false;
selectedItem:string = null;
items: OptionItem[] = ["option 1","option 2"];
constructor() { }
onItemSelected(e){
this.selectedItem = e.target.innerText;
this.showList = false;
}
}

Should I use sessionStorage and if so, how?

I have built a simple task app that allows you to add different tasks. It works fine. I am not sure what is the best approach however to retain the data/HTML once the page is refreshed. I have heard of HTML5 session/localStorage but I am not sure if this would be the best method to use in this situation. Also, I would need help making this work if sessionStorage was a good choice.
window.onload = init;
function init() {
var generateBtn = document.getElementById("generate");
generateBtn.onclick = addTask;
var tasksWrapper = document.getElementById("tasksWrapper");
var taskDesc = document.getElementById("taskDesc");
}
var taskId = 0;
var taskBarArray = [];
function addTask() {
taskId++;
var taskBar = document.createElement("div");
var taskBarInput = document.createElement("input");
var taskBarDeleteBtn = document.createElement("input");
taskBar.setAttribute("id", taskId);
taskBar.setAttribute("class", "taskBar");
taskBarInput.setAttribute("class", "taskDesc");
taskBarInput.setAttribute("type", "text");
taskBarInput.setAttribute("placeholder", "Enter task");
function rmPlaceholder() {
taskBarInput.removeAttribute("placeholder", "Enter task");
}
function addPlaceholder() {
taskBarInput.setAttribute("placeholder", "Enter task");
}
taskBarInput.onfocus = rmPlaceholder;
taskBarInput.onblur = addPlaceholder;
taskBarInput.setAttribute("name", "taskDesc");
taskBarInput.setAttribute("value", taskDesc.value);
taskBarDeleteBtn.setAttribute("class", "deleteBtn");
taskBarDeleteBtn.setAttribute("type", "button");
taskBarDeleteBtn.setAttribute("value", "x");
var addTaskBar = tasksWrapper.appendChild(taskBar);
var targetTaskId = document.getElementById(taskId);
var addTaskBarInput = targetTaskId.appendChild(taskBarInput);
var AddTaskBarDeleteBtn = targetTaskId.appendChild(taskBarDeleteBtn);
taskBarArray.push(taskBar);
taskDesc.value = "";
taskBarDeleteBtn.onclick = removeTask;
function removeTask(e) {
taskBarDeleteBtn = e.target;
tasksWrapper.removeChild(taskBar);
taskBarArray.pop(e);
if (taskBarArray.length < 1) {
taskId = 0;
}
}
}
#main_wrapper {
margin: 0 auto;
max-width: 528px;
width: 100%;
height: 20px;
}
.taskBar {
width: 100%;
background: #333230;
border-bottom: 1px solid #fff;
border-radius: 10px;
}
.taskDesc {
margin: 10px 0 10px 10px;
background: none;
border: none;
outline: none;
font-size: 20px;
color: #fff;
text-transform: uppercase;
z-index: 9999;
}
.deleteBtn {
margin: 6px 6px 0 0;
padding: 6px;
width: 32px;
background: #8F0A09;
font-size: 15px;
color: #fff;
border-radius: 100px;
border-color: #000;
float: right;
outline: none;
}
#header {
padding: 10px;
background: #000;
border-bottom: 1px solid #fff;
border-radius: 10px;
}
#taskDesc {
padding: 2px 0;
width: 50%;
font-size: 20px;
}
#generate {
padding: 5px 83px;
background: #82CC12;
font-size: 20px;
border-color: #000;
border-radius: 5px;
outline: none;
}
::-webkit-input-placeholder {
color: #4C4B48;
}
::-moz-placeholder {
color: #4C4B48;
}
:-ms-placeholder {
color: #4C4B48;
}
<div id="main_wrapper">
<div id="header">
<input type="text" id="taskDesc"></input>
<input type="button" id="generate" value="Add task">
</div>
<div id="tasksWrapper">
</div>
</div>
Here I would use localStorage, it will be remembered even after the session has timed out. A session is probably ended if the user restarts their browser.
The only problems I see with localStorage is the 10 MB size limit on desktops (2 MB om mobile devices I think), and that it's not easy enough to get data from localStorage to the server. But localStorage would be a perfect fit for a TODO app with simple items.

Categories

Resources