Merge pull request #6729 from thornbill/terite-less-dangerous

This commit is contained in:
Bill Thornton
2025-04-09 07:53:51 -04:00
committed by GitHub
9 changed files with 79 additions and 189 deletions
+3 -3
View File
@@ -6,7 +6,7 @@ import loading from '../../../../components/loading/loading';
import globalize from '../../../../lib/globalize';
import toast from '../../../../components/toast/toast';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import ButtonElement from '../../../../elements/ButtonElement';
import Button from '../../../../elements/emby-button/Button';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import AccessContainer from '../../../../components/dashboard/users/AccessContainer';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
@@ -316,10 +316,10 @@ const UserLibraryAccess = () => {
</AccessContainer>
<br />
<div>
<ButtonElement
<Button
type='submit'
className='raised button-submit block'
title='Save'
title={globalize.translate('Save')}
/>
</div>
</form>
+11 -11
View File
@@ -6,8 +6,8 @@ import globalize from '../../../../lib/globalize';
import loading from '../../../../components/loading/loading';
import toast from '../../../../components/toast/toast';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import InputElement from '../../../../elements/InputElement';
import ButtonElement from '../../../../elements/ButtonElement';
import Input from '../../../../elements/emby-input/Input';
import Button from '../../../../elements/emby-button/Button';
import AccessContainer from '../../../../components/dashboard/users/AccessContainer';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import Page from '../../../../components/Page';
@@ -194,18 +194,18 @@ const UserNew = () => {
<form className='newUserProfileForm'>
<div className='inputContainer'>
<InputElement
<Input
type='text'
id='txtUsername'
label='LabelName'
options={'required'}
label={globalize.translate('LabelName')}
required
/>
</div>
<div className='inputContainer'>
<InputElement
<Input
type='password'
id='txtPassword'
label='LabelPassword'
label={globalize.translate('LabelPassword')}
/>
</div>
<AccessContainer
@@ -248,16 +248,16 @@ const UserNew = () => {
))}
</AccessContainer>
<div>
<ButtonElement
<Button
type='submit'
className='raised button-submit block'
title='Save'
title={globalize.translate('Save')}
/>
<ButtonElement
<Button
type='button'
id='btnCancel'
className='raised button-cancel block'
title='ButtonCancel'
title={globalize.translate('ButtonCancel')}
/>
</div>
</form>
@@ -8,7 +8,7 @@ import { useSearchParams } from 'react-router-dom';
import globalize from '../../../../lib/globalize';
import AccessScheduleList from '../../../../components/dashboard/users/AccessScheduleList';
import TagList from '../../../../components/dashboard/users/TagList';
import ButtonElement from '../../../../elements/ButtonElement';
import Button from '../../../../elements/emby-button/Button';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import loading from '../../../../components/loading/loading';
@@ -480,10 +480,10 @@ const UserParentalControl = () => {
</div>
</div>
<div>
<ButtonElement
<Button
type='submit'
className='raised button-submit block'
title='Save'
title={globalize.translate('Save')}
/>
</div>
</form>
+21 -18
View File
@@ -5,10 +5,10 @@ import { useSearchParams } from 'react-router-dom';
import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../lib/globalize';
import ButtonElement from '../../../../elements/ButtonElement';
import Button from '../../../../elements/emby-button/Button';
import CheckBoxElement from '../../../../elements/CheckBoxElement';
import InputElement from '../../../../elements/InputElement';
import LinkButton from '../../../../elements/emby-button/LinkButton';
import Input from '../../../../elements/emby-input/Input';
import SectionTitleContainer from '../../../../elements/SectionTitleContainer';
import SectionTabs from '../../../../components/dashboard/users/SectionTabs';
import loading from '../../../../components/loading/loading';
@@ -314,11 +314,11 @@ const UserEdit = () => {
</div>
</div>
<div id='fldUserName' className='inputContainer'>
<InputElement
<Input
type='text'
id='txtUserName'
label='LabelName'
options={'required'}
label={globalize.translate('LabelName')}
required
/>
</div>
<div className='selectContainer fldSelectLoginProvider hide'>
@@ -416,11 +416,14 @@ const UserEdit = () => {
<br />
<div className='verticalSection'>
<div className='inputContainer'>
<InputElement
<Input
type='number'
id='txtRemoteClientBitrateLimit'
label='LabelRemoteClientBitrateLimit'
options={'inputMode="decimal" pattern="[0-9]*(.[0-9]+)?" min="{0}" step=".25"'}
label={globalize.translate('LabelRemoteClientBitrateLimit')}
inputMode='decimal'
pattern='[0-9]*(.[0-9]+)?'
min='0'
step='.25'
/>
<div className='fieldDescription'>
{globalize.translate('LabelRemoteClientBitrateLimitHelp')}
@@ -517,11 +520,11 @@ const UserEdit = () => {
<br />
<div className='verticalSection'>
<div className='inputContainer' id='fldLoginAttemptsBeforeLockout'>
<InputElement
<Input
type='number'
id='txtLoginAttemptsBeforeLockout'
label='LabelUserLoginAttemptsBeforeLockout'
options={'min={-1} step={1}'}
label={globalize.translate('LabelUserLoginAttemptsBeforeLockout')}
min={-1} step={1}
/>
<div className='fieldDescription'>
{globalize.translate('OptionLoginAttemptsBeforeLockout')}
@@ -534,11 +537,11 @@ const UserEdit = () => {
<br />
<div className='verticalSection'>
<div className='inputContainer' id='fldMaxActiveSessions'>
<InputElement
<Input
type='number'
id='txtMaxActiveSessions'
label='LabelUserMaxActiveSessions'
options={'min={0} step={1}'}
label={globalize.translate('LabelUserMaxActiveSessions')}
min={0} step={1}
/>
<div className='fieldDescription'>
{globalize.translate('OptionMaxActiveSessions')}
@@ -550,16 +553,16 @@ const UserEdit = () => {
</div>
<br />
<div>
<ButtonElement
<Button
type='submit'
className='raised button-submit block'
title='Save'
title={globalize.translate('Save')}
/>
<ButtonElement
<Button
type='button'
id='btnCancel'
className='raised button-cancel block'
title='ButtonCancel'
title={globalize.translate('ButtonCancel')}
/>
</div>
</form>
+21 -18
View File
@@ -1,11 +1,11 @@
import { getQuickConnectApi } from '@jellyfin/sdk/lib/utils/api/quick-connect-api';
import React, { FC, FormEvent, useCallback, useMemo, useState } from 'react';
import React, { FC, FormEvent, useCallback, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom';
import Page from 'components/Page';
import globalize from 'lib/globalize';
import InputElement from 'elements/InputElement';
import ButtonElement from 'elements/ButtonElement';
import Input from 'elements/emby-input/Input';
import Button from 'elements/emby-button/Button';
import { useApi } from 'hooks/useApi';
import './quickConnect.scss';
@@ -13,14 +13,12 @@ import './quickConnect.scss';
const QuickConnectPage: FC = () => {
const { api, user } = useApi();
const [ searchParams ] = useSearchParams();
// eslint-disable-next-line react-hooks/exhaustive-deps
const initialValue = useMemo(() => searchParams.get('code') ?? '', []);
const [ code, setCode ] = useState(initialValue);
const [ code, setCode ] = useState(searchParams.get('code') ?? '');
const [ error, setError ] = useState<string>();
const [ success, setSuccess ] = useState(false);
const onCodeChange = useCallback((value: string) => {
setCode(value);
const onCodeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setCode(event.currentTarget.value);
}, []);
const onSubmitCode = useCallback((e: FormEvent<HTMLFormElement>) => {
@@ -90,16 +88,21 @@ const QuickConnectPage: FC = () => {
</div>
) : (
<>
<InputElement
containerClassName='inputContainer'
initialValue={initialValue}
onChange={onCodeChange}
id='txtQuickConnectCode'
label='LabelQuickConnectCode'
type='text'
options="inputmode='numeric' pattern='[0-9\s]*' minlength='6' required autocomplete='off'"
/>
<ButtonElement
<div className='inputContainer'>
<Input
value={code}
onChange={onCodeChange}
id='txtQuickConnectCode'
label={globalize.translate('LabelQuickConnectCode')}
type='text'
inputMode='numeric'
pattern='[0-9\s]*'
minLength={6}
required
autoComplete='off'
/>
</div>
<Button
type='submit'
className='raised button-submit block'
title={globalize.translate('Authorize')}
+5 -5
View File
@@ -7,7 +7,7 @@ import Dashboard from '../../../../utils/dashboard';
import globalize from '../../../../lib/globalize';
import { appHost } from '../../../../components/apphost';
import confirm from '../../../../components/confirm/confirm';
import ButtonElement from '../../../../elements/ButtonElement';
import Button from '../../../../elements/emby-button/Button';
import UserPasswordForm from '../../../../components/dashboard/users/UserPasswordForm';
import loading from '../../../../components/loading/loading';
import toast from '../../../../components/toast/toast';
@@ -197,17 +197,17 @@ const UserProfile: FunctionComponent = () => {
{userName}
</h2>
<br />
<ButtonElement
<Button
type='button'
id='btnAddImage'
className='raised button-submit hide'
title='ButtonAddImage'
title={globalize.translate('ButtonAddImage')}
/>
<ButtonElement
<Button
type='button'
id='btnDeleteImage'
className='raised hide'
title='DeleteImage'
title={globalize.translate('DeleteImage')}
/>
</div>
</div>
@@ -5,8 +5,8 @@ import globalize from '../../../lib/globalize';
import confirm from '../../confirm/confirm';
import loading from '../../loading/loading';
import toast from '../../toast/toast';
import ButtonElement from '../../../elements/ButtonElement';
import InputElement from '../../../elements/InputElement';
import Button from '../../../elements/emby-button/Button';
import Input from '../../../elements/emby-input/Input';
type IProps = {
userId: string | null;
@@ -158,41 +158,41 @@ const UserPasswordForm: FunctionComponent<IProps> = ({ userId }: IProps) => {
>
<div className='detailSection'>
<div id='fldCurrentPassword' className='inputContainer hide'>
<InputElement
<Input
type='password'
id='txtCurrentPassword'
label='LabelCurrentPassword'
options={'autoComplete="off"'}
label={globalize.translate('LabelCurrentPassword')}
autoComplete='off'
/>
</div>
<div className='inputContainer'>
<InputElement
<Input
type='password'
id='txtNewPassword'
label='LabelNewPassword'
options={'autoComplete="off"'}
label={globalize.translate('LabelNewPassword')}
autoComplete='off'
/>
</div>
<div className='inputContainer'>
<InputElement
<Input
type='password'
id='txtNewPasswordConfirm'
label='LabelNewPasswordConfirm'
options={'autoComplete="off"'}
label={globalize.translate('LabelNewPasswordConfirm')}
autoComplete='off'
/>
</div>
<br />
<div>
<ButtonElement
<Button
type='submit'
className='raised button-submit block'
title='SavePassword'
title={globalize.translate('SavePassword')}
/>
<ButtonElement
<Button
type='button'
id='btnResetPassword'
className='raised button-cancel block hide'
title='ResetPassword'
title={globalize.translate('ResetPassword')}
/>
</div>
</div>
-42
View File
@@ -1,42 +0,0 @@
import React, { FunctionComponent } from 'react';
import globalize from 'lib/globalize';
const createButtonElement = ({ type, id, className, title, leftIcon, rightIcon }: IProps) => ({
__html: `<button
is="emby-button"
type="${type}"
${id}
class="${className}"
>
${leftIcon}
<span>${title}</span>
${rightIcon}
</button>`
});
type IProps = {
type?: string;
id?: string;
className?: string;
title?: string;
leftIcon?: string;
rightIcon?: string;
};
const ButtonElement: FunctionComponent<IProps> = ({ type, id, className, title, leftIcon, rightIcon }: IProps) => {
return (
<div
dangerouslySetInnerHTML={createButtonElement({
type: type,
id: id ? `id="${id}"` : '',
className: className,
title: globalize.translate(title),
leftIcon: leftIcon ? `<span class="material-icons ${leftIcon}" aria-hidden="true"></span>` : '',
rightIcon: rightIcon ? `<span class="material-icons ${rightIcon}" aria-hidden="true"></span>` : ''
})}
/>
);
};
export default ButtonElement;
-74
View File
@@ -1,74 +0,0 @@
import React, { type FC, useCallback, useEffect, useMemo, useRef } from 'react';
import globalize from 'lib/globalize';
interface CreateInputElementParams {
type?: string
id?: string
label?: string
initialValue?: string
options?: string
}
const createInputElement = ({ type, id, label, initialValue, options }: CreateInputElementParams) => ({
__html: `<input
is="emby-input"
type="${type}"
id="${id}"
label="${label}"
value="${initialValue}"
${options}
/>`
});
type InputElementProps = {
containerClassName?: string
onChange?: (value: string) => void
} & CreateInputElementParams;
const InputElement: FC<InputElementProps> = ({
containerClassName,
initialValue,
onChange = () => { /* no-op */ },
type,
id,
label,
options = ''
}) => {
const container = useRef<HTMLDivElement>(null);
// NOTE: We need to memoize the input html because any re-render will break the webcomponent
const inputHtml = useMemo(() => (
createInputElement({
type,
id,
label: globalize.translate(label),
initialValue,
options
})
// eslint-disable-next-line react-hooks/exhaustive-deps
), []);
const onInput = useCallback((e: Event) => {
onChange((e.target as HTMLInputElement).value);
}, [ onChange ]);
useEffect(() => {
const inputElement = container?.current?.querySelector<HTMLInputElement>('input');
inputElement?.addEventListener('input', onInput);
return () => {
inputElement?.removeEventListener('input', onInput);
};
}, [ container, onInput ]);
return (
<div
ref={container}
className={containerClassName}
dangerouslySetInnerHTML={inputHtml}
/>
);
};
export default InputElement;