Open noushad-pp opened 5 years ago
Hi, @noushad-pp. That's the right place to ask.
By default, responsive props in Atomic Layout use Bootstrap 4 breakpoints (xs, sm, md, lg, xl). As you may already know, those are described using media queries targeting a device's width. This means that by default there is no behavior to target a device's orientation.
Basically, out of the box the library can handle
abc
,abcSm
,abcMd
,abcLg
andabcXl
responsive props suffixes in accordance to Bootstrap breakpoints. What happens when you writeabcPortrait
is that the library doesn't know what isPortrait
and what breakpoint it represents. You need to explicitly describe thePortrait
breakpoint.
In order to assign responsive props based on device's orientation, you would have to create such breakpoint manually using Layout.configure()
and its breakpoints
property:
// src/index.js
import Layout, { defaultOptions } from 'atomic-layout'
// Make sure to call this once, ideally on the root level of your app.
// This can be called before "ReactDOM.render()", for example.
Layout.configure({
breakpoints: {
// Include the default breakpoints to still apply
...defaultOptions.breakpoints,
// A custom breakpoint name.
// Naming it "portrait" to be intuitive, but can be about any string.
portrait: {
// Media query definition. In our case just device's orientation.
orientation: 'portrait'
}
}
})
See the running Codesandbox example.
By doing so, you create your custom responsive props suffix called portrait
. Calling Layout.configure()
makes all Atomic Layout's components aware of your suffix automatically.
Whenever you suffix a prop name with that suffix a prop value will be applied only when the media query is met (in our case when orientation
is portrait
):
// src/MyComponent.jsx
import React from 'react'
import { Composition } from 'atomic-layout'
const MyComponent = () => (
<Composition areas="left right" areasPortrait="left center right">
{(Areas) => (
<React.Fragment>
<Areas.Left>I am rendered always.</Areas.Left>
<Areas.Center>I should be rendered only on portrait orientation.</Areas.Center>
<Areas.Right>I am rendered always as well.</Areas.Right>
</React.Fragment>
)}
</Composition>
)
I hope this explanation helps. Let me know if you still have any questions.
Please update to atomic-layout@0.9.9
to be able to import defaultOptions
from the package. This would dramatically simplify the declaration of custom breakpoints. Thanks.
Awesome. Thanks for your time and effort into this detailed explanation. This was the solution that I used before: 😓
const isPortrait = window.screen.orientation.type.includes('portrait') || window.innerHeight > window.innerWidth;
const areasLandscape = `
media text
`;
const areasPortrait = `
media
text
`;
const areas = isPortrait ? areasPortrait : areasLandscape;
Defining portrait breakpoint as mentioned above is causing areas
not to render unless I have to specify landscape as a breakpoint and render areasLandscape
instead of areas
. Even the code sandbox example is behaving the same way.
This doesnt work in landscape mode
const MyComponent = () => (
<Composition areas="left right" areasPortrait="left center right">
{(Areas) => (
<React.Fragment>
<Areas.Left>I am rendered always.</Areas.Left>
<Areas.Center>I should be rendered only on portrait orientation.</Areas.Center>
<Areas.Right>I am rendered always as well.</Areas.Right>
</React.Fragment>
)}
</Composition>
)
This works
import Layout, { defaultOptions } from 'atomic-layout'
Layout.configure({
breakpoints: {
...defaultOptions.breakpoints,
portrait: {
orientation: 'portrait'
},
landscape: {
orientation: 'landscape'
}
}
})
const MyComponent = () => (
<Composition areasLandscape="left right" areasPortrait="left center right">
{(Areas) => (
<React.Fragment>
<Areas.Left>I am rendered always.</Areas.Left>
<Areas.Center>I should be rendered only on portrait orientation.</Areas.Center>
<Areas.Right>I am rendered always as well.</Areas.Right>
</React.Fragment>
)}
</Composition>
)
@noushad-pp if you dont specify the suffix the default breakpoint name is used which is xs
explained in this section https://redd.gitbook.io/atomic-layout/fundamentals/breakpoints#default-breakpoint-name
Yes, thanks for mentioning that, @ruhaise.
@noushad-pp you are right, it appears to be an issue in the way Atomic Layout opens breakpoints. Let me elaborate below.
As Ruhaise has mentioned earlier, areas
(without a suffix) would use the default breakpoint name - xs
. So, if we extend the props assignment it would look as follows:
areasXs="left right"
areasPortrait="left center right"
As the next step, Atomic Layout determines when to render each of the areas. To do so, it first analyzes if two sibling responsive props declarations operate on the same breakpoint kind. For example, areasMd
and areasLg
operate on the same breakpoint kind, as their breakpoint is based on the viewport's dimensions. areasXs
and areasPortrait
, however, have different breakpoint kind:
areasXs
operates on the viewport's width;areasPortrait
works with the device's orientation.Since the breakpoint kind between two declarations is different, the library decides to wrap an area component (i.e. left
) in a Placeholder
. That placeholder provides conditional rendering of the area only when the media query predicate is met.
Now to the issue. It looks that the areasXs
declaration is not opened properly:
Expected behavior would be the Left
area rendered unconditionally, not having the Placeholder
wrapper. In other words, areas left
and right
must be rendered always. Since internally only the last breakpoint in a breakpoints set can be opened, areas
doesn't get opened being the first in a set (followed by areasPortrait
). This results into its Placeholder
expecting maxWidth: 576
to get the left
and right
areas rendered. That's why you don't see anything rendered at some point. When such viewport width kick in, it already satisfies the portrait orientation as well, so you can't tell the two.
I'd say we need to define how breakpoints should behave in such case.
Having though about this problem, I'm yet not sure what would be the expected behavior.
To illustrate, let's replace areas
with areasLg
.
<Composition areasLg="left" />
In isolation that means "render the left
area only on large devices and up". So it won't get rendered on xs, sm, and md screen sizes. Now let's add areasPortrait
to the mix:
<Composition areasLg="left" areasPortrait="left right" />
areasPortrait
imply that left
and right
areas will get rendered only on portrait orientation of the device. Which, in most cases, would be a smaller screen size than lg
breakpoint describes. This makes two responsive props declaring contradictory behavior toward the same area.
@noushad-pp since this behavior needs to be carefully designed, and I don't wish to block your development, I can suggest another way to tackle portrait orientation. Instead of declaring the areasPortrait
prop, use the useResponsiveValue
hook:
import { useResponsiveValue, Composition } from 'atomic-layout'
const MyComponent = () => {
const shouldRenderWidget = useResponsiveValue({
portrait: true // whenever your custom "portrait" breakpoint is met, return the value (true)
}, false)
return (
<Composition areas="left right">
{Areas => (
<React.Fragment>
<Areas.Left>I should render always</Areas.Left>
<Areas.Right>I should render always</Areas.Right>
{shouldRenderWidget && <div>Visible on portrait orientation</div>}
</React.Fragment>
)}
</Composition>
)
}
That means to leave out the portrait from the areas declaration and treat it as an extra scenario during rendering. I understand this is not ideal, as you may not be able to target the conditional widget with CSS Grid properties in its fullest extent.
Also, please, feel free to voice what your expectations would be, so we could shape the behavior together. Thanks!
I want to define a template-area for a component for portrait mode. Creating an issue as I dont know where to ask for help. I can't understand how to define the template area for this kind of breakpoint from the documentation. I tried both
areasPortrait
andareasOrientationPortrait
Is it possible this way of do I have to manage it programmatically? eg: