order matters in styles components - css

I noted that the styled-Component/Wrapper approach below can go wrong as a Child component CSS may be overridden by its parent. I came up with a simplified sample below:
This is the Parent component and its Styled-Component Wrapper:
 import styled from "styled-components";
const Child = () => {
  return (
    <ChildWrapper>
      <div className="nice-box" />
    </ChildWrapper>
  );
};
const Parent = () => {
  return (
    <ParentWrapper>
      <div className="nice-box" />
      <Child />
    </ParentWrapper>
  );
};
const ChildWrapper = styled.div`
  .nice-box {
    border: 3px solid green;
    height: 100px;
    width: 100px;
  }
`;
const ParentWrapper = styled.div`
  .nice-box {
    border: 3px solid black;
    height: 50px;
    width: 50px;
  }
`;
export default Parent;
BUT if you put "ChildWrapper" after "ParentWrapper" it works and the child will have its own
style!
How to prevent the parent's style to be used for the child?

To achieve expected result, add &&&(to get higher specificity) to the nice-box class of ChildWrapper to avoid overwrite
const ChildWrapper = styled.div`
&&& .nice-box {
border: 3px solid green;
height: 100px;
width: 100px;
}
`;
Sample working code for reference - https://codesandbox.io/s/kind-blackwell-gk6tfx?file=/src/Child.js
Reference link for more details from official documentation - https://styled-components.com/docs/faqs#how-can-i-override-styles-with-higher-specificity

Related

Screen resolution set using react responsive doesn't work

Using 'react-responsive' how can I make responsive for multiple resolution, I have tried below, but not getting desired output: Could someone suggest how to use it ?
1920px
1536px
import React from 'react';
import { useNavigate } from "react-router-dom";
import { useMediaQuery } from 'react-responsive';
const Admin = () => {
const navigate = useNavigate();
const isPcOrLaptop = useMediaQuery({
query: '(max-width: 1920px)'
});
const handleLogout = () => {
localStorage.removeItem('loginEmail');
navigate("/login");
};
return (
<div id="App">
{
isPcOrLaptop &&
<div className='adminSection'>
<div className='row'>
<h1>Hello Admin</h1>
<div className="logout">
<img src="/images/logout.png" alt="Logout" onClick={handleLogout}>
</img>
</div>
</div>
</div>
}
</div>
)
}
export default Admin;
// admin.css
.adminSection{
width: 100%;
}
h1{
margin: 25PX !important;
color: black !important;
}
.logout img {
height: 30px;
width: 30px;
margin: -70px 50px 0px 0px;
float: right;
}

React: how to make an input only as wide as the amount of text provided?

Simple enough question:
I am trying to create inputs that are as large as the text supplied to them.
Sandbox: https://codesandbox.io/s/long-snowflake-6u13n?file=/src/Test.jsx
My design intention is to generate inputs dynamically and then allow the user to have styles specific to each input that visually help break up each sentence based on outside events. But before I can move forward, it's really important that my input container is only as large as the text within.
why not use a textarea? -- I have data that is particular to each sentence that I want to create unique styles for.
Any thoughts?
Here is an approach from plain HTML/CSS and a working snippet , hidding the value typed inside a span behind the input set in an absolute position. CSS can make both span and input matching the same lenght/width. Stretching/collapsing a parent (label) will finish the job.
In the courtesy of #silvenon you may also find a react sample below the snippet
var val = document.querySelector('#test');
let tpl = document.querySelector('#tpl');
let text = val.value;
tpl.textContent= text;
val.addEventListener("input", function () {// onchange ...
let text= val.value;
//console.log(text);
tpl.textContent= text;
});
label {
display: inline-block;
position: relative;
min-width: 2em;
min-height: 1.4em;
}
#tpl {
white-space: pre;
/* max-width : could be wised to set a maximum width and overflow:hidden; */
}
#test {
font-family: inherit;
font-size: inherit;
position: absolute;
vertical-align: top;
top: 0;
left: 0;
width: 100%;
background: white;
}
<label><span id="tpl"></span><input id='test' value="Some test to try" ></label>
In the courtesy of #silvenon, you may find a react sample of that code.
const SentenceInput = styled.input`
padding: 0;
margin: 0;
border: none;
border: 1px solid black;
/* added styles */
font-family: inherit;
font-size: inherit;
position: absolute;
vertical-align: top;
top: 0;
left: 0;
width: 100%;
background: white;
`
const Label = styled.label`
display: inline-block;
position: relative;
min-width: 2em;
min-height: 1.4em;
`
const Template = styled.span`
white-space: pre;
/* max-width : could be wised to set a maximum width and overflow:hidden; */
`
const Sentence = ({ initialValue }) => {
const [value, setValue] = React.useState(initialValue)
return (
<Label>
<Template>{value}</Template>
<SentenceInput
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value)
}}
/>
</Label>
)
}
Using ch unit would work if the typeface was monospace, otherwise character width varies. I would approach this problem by rendering an inline element holding the same text, measuring it and hiding it instantly every time the input field value changes.
To do this it's best to create a separate component for rendering sentences, let's call it Sentence:
const Test = () => {
return (
<div className="App">
{value.map(({ sentence }, i) => {
return (
<Sentence
initialValue={sentence}
key={i}
/>
);
})}
</div>
);
};
Test would pass the initial value, then Sentence will continue maintaining its own state:
const Sentence = ({ initialValue }) => {
const [value, setValue] = React.useState(initialValue)
return (
<SentenceInput
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value)
}}
/>
)
}
Next, I'd add a span element that will serve as a measurer element, where the text should be styled the same way as in input elements, so the measurements turn out accurate. In your example in Chrome that would mean setting the font size to 13.3333px.
Now for the trickiest part, we need to combine useEffect and useLayoutEffect; useEffect will make the measurer visible, then useLayoutEffect will measure it and hide it
This is the result:
const Sentence = ({ initialValue }) => {
const [value, setValue] = React.useState(initialValue)
const [visible, setVisible] = React.useState(false)
const [width, setWidth] = React.useState('auto')
const measurer = React.useRef(null)
React.useEffect(() => {
setVisible(true)
}, [value])
React.useLayoutEffect(() => {
if (visible) {
const rect = measurer.current.getBoundingClientRect()
setWidth(rect.width)
setVisible(false)
}
}, [visible])
return (
<>
<span
ref={measurer}
style={{ fontSize: '13.3333px' }}
>
{visible && value}
</span>
<SentenceInput
type="text"
value={value}
style={{ width: width + 1 }}
onChange={(event) => {
setValue(event.target.value)
}}
/>
</>
)
}
I added 1px to the computed width because it seems to remove a small horizontal scroll in the input fields.
This is for you to tweak further the way you want, for example how it should behave when it reaches the viewport width.
The <input> size attribute can be used to set the width. It works in the same way as ch units e.g. <input size="10"> has a width of 10ch. As with ch units, it works perfectly with monospaced fonts but is only an approximation when it comes to proportionally-spaced fonts.
To get a more accurate measurement with proportionally-spaced fonts, you can use the scrollWidth property of the <input> element itself to calculate the width. The trick is to set the value to 'auto' first to capture the content width before setting the value. Set the input padding to zero if you want to fit the content exactly.
import React, { useEffect, useRef } from "react";
const data = [
  { sentence: "Hello world, how are you?" },
  { sentence: "Sometimes in rains in the summer." },
  {
    sentence:
      "Depending where you currently live, it my even rain in the winter."
  }
];
const SentenceInput = (props) => {
  const { value, onChange } = props;
  const inputRef = useRef(null);
  useEffect(() => {
    const input = inputRef.current;
    input.style.width = 'auto';
    input.style.width = `${input.scrollWidth}px`;
  });
  return <input ref={inputRef} type="text" value={value} onChange={onChange} />;
};
const Test = () => {
  const handleChange = () => {};
  return (
    <div className="App">
      {data.map(({ sentence }, i) => {
        return (
          <SentenceInput key={i} value={sentence} onChange={handleChange} />
        );
      })}
    </div>
  );
};
export default Test;

css not rendering in react component

When i apply the below CSS in App.css they render perfectly, however when I apply the same styles directly in my component (below) the CSS does not render.
const getStyles = () => {
const BLACK = "#000000";
const VW = "vw";
const VH = "vh";
const REM = "rem";
return {
editor: {
width: `70${VW}`,
height: `50${VH}`,
margin: `2${REM}`,
padding: `1${REM}`,
fontSize: `1.2${REM}`,
boxShadow: `0 .1${REM} .4rem ${BLACK}`,
border: `1px solid ${BLACK}`,
overflowY: `auto`
}
};
};
const styles = getStyles();
return (
<>
<div className="center">
<div className={styles.editor} contentEditable={true} suppressContentEditableWarning={true}>
<h1>{introText}</h1>
<p>{subText}</p>
</div>
</div>
</>
)
}
To get the effect you desire you should do something like this.
const getStyles = () => {
const BLACK = "#000000";
const VW = "vw";
const VH = "vh";
const REM = "rem";
return {
width: `70${VW}`,
height: `50${VH}`,
margin: `2${REM}`,
padding: `1${REM}`,
fontSize: `1.2${REM}`,
boxShadow: `0 .1${REM} .4rem ${BLACK}`,
border: `1px solid ${BLACK}`,
overflowY: `auto`
};
};
const styles = getStyles();
return (
<>
<div className="center">
<div style={styles.editor} contentEditable={true} suppressContentEditableWarning={true}>
<h1>{introText}</h1>
<p>{subText}</p>
</div>
</div>
</>
)
}
More info at this link:
https://reactjs.org/docs/dom-elements.html#style

Convert image to byte array without using SwingFXUtils

In the answer of the question getPublicStorage(“Pictures”) lists no files we have tried solution 1 and 2 but in the byte array process seems the array does not represent the actual image.
Used code:
Services.get(PicturesService.class).ifPresent(service -> {
            service.takePhoto(false).ifPresent(image -> {
                imageView.setImage(image);
 
                PixelReader pixelReader = image.getPixelReader();
                int width = (int) image.getWidth();
                int height = (int) image.getHeight();
                byte[] buffer = new byte[width * height * 4];
                pixelReader.getPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), buffer, 0, width * 4);
 
                //Test
                ByteArrayInputStream in = new ByteArrayInputStream(buffer);
                Image image2 = new Image(in);
 
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
 
                imageView.setImage(image2);
                //Test
 
                proceedImage(buffer);
            });
        });
 
private void proceedImage(byte[] arrayImage) {
        this.arrayImage = arrayImage;
 
        enableZoom(true);
 
        apply = true;
    }
Exception:
W/System.err(15096): com.sun.javafx.iio.ImageStorageException: No loader for image data
W/System.err(15096):    at com.sun.javafx.iio.ImageStorage.loadAll(ImageStorage.java:276)
W/System.err(15096):    at com.sun.javafx.tk.quantum.PrismImageLoader2.loadAll(PrismImageLoader2.java:142)
W/System.err(15096):    at com.sun.javafx.tk.quantum.PrismImageLoader2.<init>(PrismImageLoader2.java:77)
W/System.err(15096):    at com.sun.javafx.tk.quantum.QuantumToolkit.loadImage(QuantumToolkit.java:740)
W/System.err(15096):    at javafx.scene.image.Image.loadImage(Image.java:1073)
W/System.err(15096):    at javafx.scene.image.Image.initialize(Image.java:804)
W/System.err(15096):    at javafx.scene.image.Image.<init>(Image.java:707)
W/System.err(15096):    at org.openjanela.dialog.ImageViewDialog.lambda$null$5(ImageViewDialog.java:260)
Any idea for a fix \ tweak \ workaround would be much appreciated.

How to bind DOM property in directive

Given this example:
var SomeApp = angular.module('SomeApp', [])
.controller('SomeController', function($scope){
$scope.items = [0,1,2,3]
})
.directive('gridResize', function(){
return {
scope: true,
link: function(scope, elem) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
}
}
})
.parent {
width: 80%;
position: relative;
left: 50%;
transform: translateX(-50%);
border: 1px solid red;
}
.parent > * {
background-color: rgba(0,0,0,.14);
margin-bottom: 1px;
padding: 20px;
box-sizing: border-box;
}
.parent > *:last-child {
margin-bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="SomeApp">
<div class="parent" ng-controller="SomeController" grid-resize>
<div ng-style="{'min-height':($parent.gridResize.width/8) + 'px'}"
ng-repeat="item in items"
>
height: {{$parent.gridResize.height}} | width: {{$parent.gridResize.width}}
</div>
</div>
</div>
Can anyone tell me how I could bind the height and width of the grid-resize directive to the DOM element? I want the angular properties to change when the DOM element changes.
In your directive use the window "resize" event to update the sizes on the scope:
var SomeApp = angular.module('SomeApp', [])
.controller('SomeController', function($scope){
$scope.items = [0,1,2,3]
})
.directive('gridResize', function(){
return {
scope: false,
link: function(scope, elem) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
angular.element(window).on('resize', function(e) {
scope.gridResize = {
width: $(elem).width(),
height: $(elem).height()
};
scope.$apply();
});
}
}
})
Also notice that I changed the directive's scope to 'false'. You already have a scope on that element created by the ng-controller directive.

Resources