mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 17:15:48 +00:00
Merge pull request #832 from Stremio/feat/intro-consent-checkbox
Some checks are pending
Build / build (push) Waiting to run
Some checks are pending
Build / build (push) Waiting to run
Intro: Consent items using checkbox instead toggle
This commit is contained in:
commit
a69c47a90f
9 changed files with 195 additions and 115 deletions
|
|
@ -8,6 +8,7 @@ import styles from './Button.less';
|
|||
type Props = {
|
||||
className?: string,
|
||||
href?: string,
|
||||
target?: string
|
||||
title?: string,
|
||||
disabled?: boolean,
|
||||
tabIndex?: number,
|
||||
|
|
|
|||
83
src/components/Checkbox/Checkbox.less
Normal file
83
src/components/Checkbox/Checkbox.less
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright (C) 2017-2025 Smart code 203358507
|
||||
|
||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||
|
||||
.checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: visible;
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
font-size: 0.9rem;
|
||||
color: var(--primary-foreground-color);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 0.9rem;
|
||||
color: var(--primary-accent-color);
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
position: relative;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 0.3rem;
|
||||
background-color: var(--overlay-color);
|
||||
padding: 0.1rem;
|
||||
display: flex;
|
||||
flex: none;
|
||||
margin: 0 1rem 0 0.3rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease-in-out;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
outline-width: var(--focus-outline-size);
|
||||
outline-color: @color-surface-light5;
|
||||
outline-offset: 2px;
|
||||
|
||||
input[type='checkbox'] {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--primary-foreground-color);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-color: var(--color-trakt);
|
||||
}
|
||||
|
||||
&.checked {
|
||||
background-color: var(--primary-accent-color);
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
outline-style: solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
src/components/Checkbox/Checkbox.tsx
Normal file
97
src/components/Checkbox/Checkbox.tsx
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (C) 2017-2025 Smart code 203358507
|
||||
|
||||
import React, { useCallback, ChangeEvent, KeyboardEvent, RefCallback } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import styles from './Checkbox.less';
|
||||
import Button from '../Button';
|
||||
import Icon from '@stremio/stremio-icons/react';
|
||||
|
||||
type Props = {
|
||||
ref?: RefCallback<HTMLInputElement>;
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
className?: string;
|
||||
label?: string;
|
||||
link?: string;
|
||||
href?: string;
|
||||
onChange?: (props: {
|
||||
type: string;
|
||||
checked: boolean;
|
||||
reactEvent: KeyboardEvent<HTMLInputElement> | ChangeEvent<HTMLInputElement>;
|
||||
nativeEvent: Event;
|
||||
}) => void;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
const Checkbox = React.forwardRef<HTMLInputElement, Props>(({ name, disabled, className, label, href, link, onChange, error, checked }, ref) => {
|
||||
|
||||
const handleSelect = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!disabled && onChange) {
|
||||
onChange({
|
||||
type: 'select',
|
||||
checked: event.target.checked,
|
||||
reactEvent: event,
|
||||
nativeEvent: event.nativeEvent,
|
||||
});
|
||||
}
|
||||
}, [disabled, onChange]);
|
||||
|
||||
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {
|
||||
if ((event.key === 'Enter' || event.key === ' ') && !disabled) {
|
||||
onChange && onChange({
|
||||
type: 'select',
|
||||
checked: !checked,
|
||||
reactEvent: event as KeyboardEvent<HTMLInputElement>,
|
||||
nativeEvent: event.nativeEvent,
|
||||
});
|
||||
}
|
||||
}, [disabled, checked, onChange]);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles['checkbox'], className)}>
|
||||
<label className={styles['label']} htmlFor={name}>
|
||||
<div
|
||||
className={classNames(
|
||||
styles['checkbox-container'],
|
||||
{ [styles['checked']]: checked },
|
||||
{ [styles['disabled']]: disabled },
|
||||
{ [styles['error']]: error }
|
||||
)}
|
||||
role={'checkbox'}
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
aria-checked={checked}
|
||||
onKeyDown={onKeyDown}
|
||||
>
|
||||
<input
|
||||
ref={ref}
|
||||
id={name}
|
||||
type={'checkbox'}
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
onChange={handleSelect}
|
||||
className={styles['input']}
|
||||
/>
|
||||
{
|
||||
checked ?
|
||||
<Icon name={'checkmark'} className={styles['checkbox-icon']} />
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<span>{label}</span>
|
||||
{' '}
|
||||
{
|
||||
href && link ?
|
||||
<Button className={styles['link']} href={href} target={'_blank'} tabIndex={-1}>
|
||||
{link}
|
||||
</Button>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Checkbox;
|
||||
5
src/components/Checkbox/index.ts
Normal file
5
src/components/Checkbox/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (C) 2017-2025 Smart code 203358507
|
||||
|
||||
import Checkbox from './Checkbox';
|
||||
|
||||
export default Checkbox;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import AddonDetailsModal from './AddonDetailsModal';
|
||||
import BottomSheet from './BottomSheet';
|
||||
import Button from './Button';
|
||||
import Checkbox from './Checkbox';
|
||||
import Chips from './Chips';
|
||||
import ColorInput from './ColorInput';
|
||||
import ContinueWatchingItem from './ContinueWatchingItem';
|
||||
|
|
@ -31,6 +32,7 @@ export {
|
|||
AddonDetailsModal,
|
||||
BottomSheet,
|
||||
Button,
|
||||
Checkbox,
|
||||
Chips,
|
||||
ColorInput,
|
||||
ContinueWatchingItem,
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
const classnames = require('classnames');
|
||||
const { Button, Toggle } = require('stremio/components');
|
||||
const styles = require('./styles');
|
||||
|
||||
const ConsentToggle = React.forwardRef(({ className, label, link, href, onToggle, ...props }, ref) => {
|
||||
const toggleOnClick = React.useCallback((event) => {
|
||||
if (typeof props.onClick === 'function') {
|
||||
props.onClick(event);
|
||||
}
|
||||
|
||||
if (!event.nativeEvent.togglePrevented && typeof onToggle === 'function') {
|
||||
onToggle({
|
||||
type: 'toggle',
|
||||
reactEvent: event,
|
||||
nativeEvent: event.nativeEvent
|
||||
});
|
||||
}
|
||||
}, [onToggle, props.onClick]);
|
||||
const linkOnClick = React.useCallback((event) => {
|
||||
event.nativeEvent.togglePrevented = true;
|
||||
}, []);
|
||||
return (
|
||||
<Toggle {...props} ref={ref} className={classnames(className, styles['consent-toggle-container'])} onClick={toggleOnClick}>
|
||||
<div className={styles['label']}>
|
||||
{label}
|
||||
{' '}
|
||||
{
|
||||
typeof link === 'string' && link.length > 0 && typeof href === 'string' && href.length > 0 ?
|
||||
<Button className={styles['link']} href={href} target={'_blank'} tabIndex={-1} onClick={linkOnClick}>
|
||||
{link}
|
||||
</Button>
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</Toggle>
|
||||
);
|
||||
});
|
||||
|
||||
ConsentToggle.displayName = 'ConsentToggle';
|
||||
|
||||
ConsentToggle.propTypes = {
|
||||
className: PropTypes.string,
|
||||
checked: PropTypes.bool,
|
||||
label: PropTypes.string,
|
||||
link: PropTypes.string,
|
||||
href: PropTypes.string,
|
||||
onToggle: PropTypes.func,
|
||||
onClick: PropTypes.func
|
||||
};
|
||||
|
||||
module.exports = ConsentToggle;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const ConsentToggle = require('./ConsentToggle');
|
||||
|
||||
module.exports = ConsentToggle;
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
@import (reference) '~@stremio/stremio-colors/less/stremio-colors.less';
|
||||
|
||||
:import('~stremio/components/Toggle/styles.less') {
|
||||
checkbox-icon: icon;
|
||||
}
|
||||
|
||||
.consent-toggle-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0;
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
background-color: var(--overlay-color);
|
||||
}
|
||||
|
||||
&:global(.checked) {
|
||||
.label {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--primary-foreground-color);
|
||||
opacity: 0.6;
|
||||
|
||||
.link {
|
||||
font-size: 0.9rem;
|
||||
color: var(--primary-accent-color);
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,9 +8,8 @@ const { default: Icon } = require('@stremio/stremio-icons/react');
|
|||
const { Modal, useRouteFocused } = require('stremio-router');
|
||||
const { useServices } = require('stremio/services');
|
||||
const { useBinaryState } = require('stremio/common');
|
||||
const { Button, Image } = require('stremio/components');
|
||||
const { Button, Image, Checkbox } = require('stremio/components');
|
||||
const CredentialsTextInput = require('./CredentialsTextInput');
|
||||
const ConsentToggle = require('./ConsentToggle');
|
||||
const PasswordResetModal = require('./PasswordResetModal');
|
||||
const useFacebookLogin = require('./useFacebookLogin');
|
||||
const styles = require('./styles');
|
||||
|
|
@ -308,30 +307,27 @@ const Intro = ({ queryParams }) => {
|
|||
onChange={confirmPasswordOnChange}
|
||||
onSubmit={confirmPasswordOnSubmit}
|
||||
/>
|
||||
<ConsentToggle
|
||||
<Checkbox
|
||||
ref={termsRef}
|
||||
className={styles['consent-toggle']}
|
||||
label={'I have read and agree with the Stremio'}
|
||||
link={'Terms and conditions'}
|
||||
href={'https://www.stremio.com/tos'}
|
||||
checked={state.termsAccepted}
|
||||
onToggle={toggleTermsAccepted}
|
||||
onChange={toggleTermsAccepted}
|
||||
/>
|
||||
<ConsentToggle
|
||||
<Checkbox
|
||||
ref={privacyPolicyRef}
|
||||
className={styles['consent-toggle']}
|
||||
label={'I have read and agree with the Stremio'}
|
||||
link={'Privacy Policy'}
|
||||
href={'https://www.stremio.com/privacy'}
|
||||
checked={state.privacyPolicyAccepted}
|
||||
onToggle={togglePrivacyPolicyAccepted}
|
||||
onChange={togglePrivacyPolicyAccepted}
|
||||
/>
|
||||
<ConsentToggle
|
||||
<Checkbox
|
||||
ref={marketingRef}
|
||||
className={styles['consent-toggle']}
|
||||
label={'I agree to receive marketing communications from Stremio'}
|
||||
checked={state.marketingAccepted}
|
||||
onToggle={toggleMarketingAccepted}
|
||||
onChange={toggleMarketingAccepted}
|
||||
/>
|
||||
</React.Fragment>
|
||||
:
|
||||
|
|
|
|||
Loading…
Reference in a new issue