I am building an online course website.
When the user watches a lesson in full-screen mode, I want to remember that, so as to use full-screen mode when I mount react-player with the next lesson. I hoped there would be an onFullscreenMode callback, but the documentation does not list anything of the kind. How can I achieve this?
Edit 1: Based on the reply of #onkarruikar, I tried using screenfull. First, I was surprised that it was not installed although real-player was supposed to use it to enter full-screen mode. After installing the package and importing it, I get the compilation error:
.../node_modules/screenfull/index.js 11:44
Module parse failed: Unexpected token (11:44)
File was processed with these loaders:
.../node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
|
| for (const methodList of methodMap) {
> const exitFullscreenMethod = methodList?.[1];
|
| if (exitFullscreenMethod in document) {
Edit 2: I also don't get it why the demo uses a custom button for switching to full-screen mode, whereas I see a button () on the player itself:
The player doesn't have fullscreen inbuilt. It uses screenfull to go full-screen. As per their demo https://cookpete.com/react-player/ full-screen is handled externally by the component users.
You can use following screenfull features directly on your website:
screenfull.isFullscreen //<-- is the browser in fullscreen
screenfull.isEnabled //<-- is the facility available to use
screenfull.request(element);
screenfull.toggle(element);
etc.
Or you can use standard web apis like:
if(document.fullscreenElement) { //<-- is the browser in fullscreen
...
}
document.fullscreenEnabled //<-- is the facility available to use
Document.fullscreenElement / ShadowRoot.fullscreenElement
The fullscreenElement property tells you the Element that's currently being displayed in full-screen mode on the DOM (or shadow DOM). If this is null, the document (or shadow DOM) is not in full-screen mode.
ref
These apis should work even if you go fullscreen using controls inside player.
Here is a demo website using react: https://gizcx.csb.app/
Corresponding codesandbox code
Also, if you are not playing videos one by one then you can pass full course playlist to the player at once:
<ReactPlayer
url={[
'https://www.youtube.com/watch?v=oUFJJNQGwhk',
'https://www.youtube.com/watch?v=jNgP6d9HraI'
]}
/>
For the benefit of others, this is how it is achieved:
import { findDOMNode } from 'react-dom'
import { toast } from 'react-toastify';
const PlayerComponent = () => {
const [fullscreenMode, setFullscreenMode] = useState(false)
let player = null;
const ref = (p) => {player = p;}
const onStart = () => {
if (fullscreenMode)
findDOMNode(player).requestFullscreen().catch(
(err) =>
{toast.error("Could not activate full-screen mode :(")}
);
}
const onEnded = () => {
setFullscreenMode(document.fullscreenElement !== null);
}
return (
<ReactPlayer
ref={ref}
url="whatever url"
onStart={onStart}
onEnded={onEnded} />);
);
}
Related
I've devided my pages folder on 2 sections:
mobile
desktop
And I do some rewrote on the server
import { NextRequest, NextResponse } from 'next/server';
const isMobile = (userAgent: string) =>
/iPhone|iPad|iPod|Android/i.test(userAgent);
export function middleware(req: NextRequest) {
const userAgent = req.headers.get('user-agent');
const { pathname, origin } = req.nextUrl;
if (userAgent && !pathname.includes('favicon.ico')) {
if (isMobile(userAgent)) {
return NextResponse.rewrite(`${origin}/mobile${pathname}`);
} else {
return NextResponse.rewrite(`${origin}/desktop${pathname}`);
}
}
}
It is great , I hadnle 2 sizes - more 1280 and lower , and on those 2 I have some breakpoints. But what is user for example switch from <1280 to >1280, is there any way to detect it on client ( there is ) and rewrite it same way as on _middleware ?
One way to do this might be to use an observer that reports changes at an app level ( this kind of avoids the isDesktop like logic) - it unfortunately is doing isDesktop like logic but works more at a observer level.
i.e
// Modified from package example.
import { Observe } from '#envato/react-breakpoints';
const exampleBreakpoints = {
widths: {
0: 'mobile',
769: 'tablet',
1280: 'desktop',
}
};
export const ExampleComponent = () => (
<Observe breakpoints={exampleBreakpoints}>
{({ observedElementProps, widthMatch = 'ssr' }) => (
<div {...observedElementProps}>
<div className={widthMatch}>
<YourComponent />
</div>
)}
</Observe>
);
https://github.com/envato/react-breakpoints
The interesting thing about this package is on the server, you will get undefined, see https://github.com/envato/react-breakpoints/blob/main/docs/server-side-rendering.md
Instead of doing user agent matching, you can reflect where the changes are happening, this means you can do SSR accordingly like you were doing.
I see the flow happening like below:
User loads a page on Desktop - SSR gets undefined from so renders a Desktop page
User resizes, ResizeObserverProvider will trigger and since its a provider, it will cascade the props to children who can use it.
This is one of the many ways to do it but it involves client side calculations.
I'm trying to use tailwindCSS on angular 12, and it's works fine.
My question is about using flowbite or tailwindui for UI component - is it support on angular too?
because i'm trying to flow the instructions of flowbite and it's not working
https://flowbite.com/docs/getting-started/quickstart/
the css work's fine but not the script.
For example I try to build a dropdowns like this - and it's not working (i'm not getting any error on console)
https://flowbite.com/docs/components/dropdowns/#
What is wrong ?
Having the same issue, tried:
Using scripts:["../path/to/#themesberg/flowbite/dist/flowbite.bundle.js"] in angular.json
Adding <script src="../path/to/#themesberg/flowbite/dist/flowbite.bundle.js"></script> on the head of index.hmtl
Update:
Adding import '#themesberg/flowbite'; on either the main.ts or polyfills.ts correctly exports the functions to the vendor.js or polyfills.js respectfully. But even though they are present, the event listeners are not working:
() => {
const toggleCollapse = (elementId, show = true) => {
const collapseEl = document.getElementById(elementId);
if (show) {
collapseEl.classList.remove('hidden');
} else {
collapseEl.classList.add('hidden');
}
}; // Toggle target elements using [data-collapse-toggle]
document.querySelectorAll('[data-collapse-toggle]').forEach(function (collapseToggleEl) {
var collapseId = collapseToggleEl.getAttribute('data-collapse-toggle');
collapseToggleEl.addEventListener('click', function () {
toggleCollapse(collapseId, document.getElementById(collapseId).classList.contains('hidden'));
});
});
window.toggleCollapse = toggleCollapse;
/***/
},
I'm pretty new with Next.js and do not fully understand the cache functioning.
Given the following simplified example:
An index page that renders components Test1 or Test2, depending whether the current minute is even or odd:
import { Test2 } from '#src/components/test2'
import React from 'react'
const conditionallyChooseComponent = () => {
const d = new Date()
if (d.getMinutes() % 2 === 0) return <Test1 />
else return <Test2 />
}
export default function Home() {
return <div>{conditionallyChooseComponent()}</div>
}
And having the following components. Test1:
export const Test1 = () => {
const d = new Date()
return (
<div className={`${utilStyles.redContainer}`}>
<h1>It's {d.toISOString()} and I'm Test1 component. My background should be red</h1>
</div>
)
}
And Test2:
export const Test2 = () => {
const d = new Date()
return (
<div className={`${utilStyles.blueContainer}`}>
<h1>It's {d.toISOString()} and I'm Test2 component. My background should be blue</h1>
</div>
)
}
And this CSS:
.redContainer {
background-color: red;
}
.blueContainer {
background-color: blue;
}
The background color is being cached when the code is executed by building and serving from the transpiled code. When running with yarn dev it is working just fine.
Here is the unexpected result:
Screenshot with Test1 component being rendered with blue background
PS: I made this work with the workaround of using the getInitialProps to prevent Next.js from caching anything in that page but, for my real use case that option is not valid because I need the render condition to be calculated in the client side since it will depend on the local date of the browser.
Next will automatically cache all static pages that doesn't depends on external data, maybe you can implement useEffect to update your date variable or use a simple state, so it should work the way you expect
https://nextjs.org/docs/basic-features/pages#static-generation-without-data
To make that works, you will need to add some client-side code (via useEffect), so the React component updates every minute (or so). Funny enough, this is not as simple as it sounds, and even Dan Abramov has published a long post explaining why things such as setInterval may not work intuitively with React (specifically, with React Hooks).
Assuming you use the custom hook Dan explains in the article above, this should work:
export default function Home() {
const [date, setDate] = useState(new Date());
useInterval(() => {
// this will update the component's date every second
setDate(new Date());
}, 1000);
return <div>{date.getMinutes() % 2 === 0 ? <p>Test 1</p> : <p>Test 2</p>}</div>;
}
Observe that your code example only executes the conditionallyChooseComponent once, just when Next is trying to server-side rendering your page.
I need to conditionally render components based on screen size.
I use nextjs and getInitialProps for data fetching, the page is server-side rendered. I want to detect device screen size on the client-side, so I implement a customized hook to do it.
useWindowSize.js
import { useEffect, useState } from 'react';
export default function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: typeof window === 'undefined' ? 1200 : window.innerWidth, // default width 1200
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
//height: window.innerHeight,
});
}
// Add event listener
window.addEventListener('resize', handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener('resize', handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize.width <= 600;
}
then I use this hook to detect window size and conditional render components
export default function IndexPage() {
const isMobile = useWindowSize();
if (typeof window !== "undefined") {
// if you are running it on codesanbox, I don't know why log is not printed
console.log("client side re-render");
}
return (
<div>
{isMobile ? (
<div
style={{
color: "red",
fontSize: 40
}}
>
mobile
</div>
) : (
<div
style={{
color: "blue",
fontSize: 20
}}
>
desktop
</div>
)}
</div>
);
}
IndexPage.getInitialProps = () => {
return {
a: 1
};
};
when I load the page on mobile browser, you will see
text mobile is applied wrong CSS style. video demo: https://share.getcloudapp.com/nOuk08L0
how to reproduce:
https://codesandbox.io/s/thirsty-khayyam-npqpt
Can someone please help me out. Thank you in advance!
This is an issue that is related to how React patch up DOM from SSR. When there is a mismatch between client-side and server-side rendering, React will only patch/sync the text context for the node. The DOM attribute will not be automatically updated. In your case, the SSR result has the desktop style because there is no window object, and client side has the mobile result. After the mismatch, React update the text node from 'desktop' to mobile but not the style attributes.
In my opinion, you can use two different approaches. You can use Media Query to style your component based on the screen width instead of the hook. If you are doing SSR, not SSG, you can use user agent req.headers["user-agent"] to detect the device your device is being viewed on.
For the first approach, you might need to render more DOM node you might need to. For the second approach, you won't be able to know the actual viewport size, which can cause visual issue. You might be able to combine both approach to produce a good viewing experience for your user.
Reference
https://github.com/facebook/react/issues/11128#issuecomment-334882514
Thanks for #Andrew Zheng's detailed explanation! Today I learned.
I know that I can style the layout by using pure CSS media query, but my use case needs a variable like isMobile to
if (isMobile) {
doSomethingOnlyOnMobileWeb();
} else {
doSomethingOnlyForDesktopWeb();
}
So I combined two approaches you provided, and modify my hook this way:
export default function useWindowSize(userAgent) {
let isMobile = Boolean(
userAgent &&
userAgent.match(
/Android|BlackBerry|iPhone|iPod|Opera Mini|IEMobile|WPDesktop/i
)
);
const [windowSize, setWindowSize] = useState({
width: isServer
? isMobile
? BREAKPOINT_SMALL
: BREAKPOINT_LARGE
: window.innerWidth,
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
//height: window.innerHeight,
});
}
// Add event listener
window.addEventListener('resize', handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener('resize', handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize.width <= BREAKPOINT_SMALL;
}
diff: passing user-agent string to useWindowSize for server-side detection and use window.innerWidth for client-side detection. There won't be a mismatch between server and client.
Am teaching myself React and am starting on testing. Using the recommended stuff from the docs... create-react-app, jest, testing-library/react. I have a component that renders a dynamic style, something like this
const ScalingDiv = (props) => (
<StyledDiv size={props.size || 42}>
<OtherEl>
{props.text}
</OtherEl>
</StyledDiv>
);
I would like to verify that my logic in there is working correctly (that 42 is used as a fallback size). I cannot find any examples or docs of this. I had hoped that something like this would work-
test('check fallback size', async () => {
const {container} = render(<ScalingDiv/>);
expect(container.firstChild).toHaveAttribute('size', 42);
});
But I haven't found any combination of matchers and queries that returns ANY attributes. Then I tried to just check the style directly using jest-dom, but toHaveStyle('this_isnt_valid_css: 199') passes- I couldn't stick anything in there to get it to fail. So... what is the right way to do this?
It's not attribute but prop. So we can refer to prop() and props() methods like
expect(container.firstChild.prop('size')).toEqual(42);
or
expect(container.firstChild.props()).toEqual({
size: 42
});