feat: remove color validation in appearance admin page & add color indicator (#3140)

* Remove color validation in basics admin page & add color indicator

* Create ColorInput common component

* Revert 'formGroupAttrs' addition

* Rename component CSS classes

* Fix input type in ColorInput from AdminPage#buildSettingComponent

* Rename component to ColorPreviewInput, remove aliases in admin & export in compat

* Remove leftovers from rebase on master

* feat: add global type definition for a vnode element tag

* fix(a11y): add aria roles to color input

* chore: use new type

* chore: format

Co-authored-by: David Wheatley <hi@davwheat.dev>
This commit is contained in:
David Sevilla Martin 2021-11-23 16:38:46 -05:00 committed by GitHub
parent 9dbe86a22c
commit f418b84aca
8 changed files with 77 additions and 25 deletions

View File

@ -21,6 +21,8 @@ declare type KeysOfType<Type extends object, Match> = {
*/
declare type KeyOfType<Type extends object, Match> = KeysOfType<Type, Match>[keyof Type];
declare type VnodeElementTag<Attrs = Record<string, unknown>, State = Record<string, unknown>> = string | ComponentTypes<Attrs, State>;
/**
* @deprecated Please import `app` from a namespace instead of using it as a global variable.
*

View File

@ -10,6 +10,7 @@ import Stream from '../../common/utils/Stream';
import saveSettings from '../utils/saveSettings';
import AdminHeader from './AdminHeader';
import generateElementId from '../utils/generateElementId';
import ColorPreviewInput from '../../common/components/ColorPreviewInput';
export interface AdminHeaderOptions {
title: string;
@ -76,6 +77,7 @@ export interface HTMLInputSettingsComponentOptions extends CommonSettingsItemOpt
const BooleanSettingTypes = ['bool', 'checkbox', 'switch', 'boolean'] as const;
const SelectSettingTypes = ['select', 'dropdown', 'selectdropdown'] as const;
const TextareaSettingTypes = ['textarea'] as const;
const ColorPreviewSettingType = 'color-preview';
/**
* Valid options for the setting component builder to generate a Switch.
@ -103,6 +105,13 @@ export interface TextareaSettingComponentOptions extends CommonSettingsItemOptio
type: typeof TextareaSettingTypes[number];
}
/**
* Valid options for the setting component builder to generate a ColorPreviewInput.
*/
export interface ColorPreviewSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof ColorPreviewSettingType;
}
/**
* All valid options for the setting component builder.
*/
@ -110,7 +119,8 @@ export type SettingsComponentOptions =
| HTMLInputSettingsComponentOptions
| SwitchSettingComponentOptions
| SelectSettingComponentOptions
| TextareaSettingComponentOptions;
| TextareaSettingComponentOptions
| ColorPreviewSettingComponentOptions;
/**
* Valid attrs that can be returned by the `headerInfo` function
@ -258,7 +268,15 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
if ((TextareaSettingTypes as readonly string[]).includes(type)) {
settingElement = <textarea id={inputId} aria-describedby={helpTextId} bidi={this.setting(setting)} {...componentAttrs} />;
} else {
settingElement = <input id={inputId} aria-describedby={helpTextId} type={type} bidi={this.setting(setting)} {...componentAttrs} />;
let Tag: VnodeElementTag = 'input';
if (type === ColorPreviewSettingType) {
Tag = ColorPreviewInput;
} else {
componentAttrs.type = type;
}
settingElement = <Tag id={inputId} aria-describedby={helpTextId} bidi={this.setting(setting)} {...componentAttrs} />;
}
}

View File

@ -25,12 +25,12 @@ export default class AppearancePage extends AdminPage {
<div className="AppearancePage-colors-input">
{this.buildSettingComponent({
type: 'text',
type: 'color-preview',
setting: 'theme_primary_color',
placeholder: '#aaaaaa',
})}
{this.buildSettingComponent({
type: 'text',
type: 'color-preview',
setting: 'theme_secondary_color',
placeholder: '#aaaaaa',
})}
@ -105,17 +105,4 @@ export default class AppearancePage extends AdminPage {
onsaved() {
window.location.reload();
}
saveSettings(e) {
e.preventDefault();
const hex = /^#[0-9a-f]{3}([0-9a-f]{3})?$/i;
if (!hex.test(this.settings['theme_primary_color']()) || !hex.test(this.settings['theme_secondary_color']())) {
alert(app.translator.trans('core.admin.appearance.enter_hex_message'));
return;
}
super.saveSettings(e);
}
}

View File

@ -57,6 +57,7 @@ import Alert from './components/Alert';
import Link from './components/Link';
import LinkButton from './components/LinkButton';
import Checkbox from './components/Checkbox';
import ColorPreviewInput from './components/ColorPreviewInput';
import SelectDropdown from './components/SelectDropdown';
import ModalManager from './components/ModalManager';
import Button from './components/Button';
@ -144,6 +145,7 @@ export default {
'components/Link': Link,
'components/LinkButton': LinkButton,
'components/Checkbox': Checkbox,
'components/ColorPreviewInput': ColorPreviewInput,
'components/SelectDropdown': SelectDropdown,
'components/ModalManager': ModalManager,
'components/Button': Button,

View File

@ -0,0 +1,28 @@
import type Mithril from 'mithril';
import Component, { ComponentAttrs } from '../Component';
import classList from '../utils/classList';
import icon from '../helpers/icon';
export default class ColorPreviewInput extends Component {
value?: string;
view(vnode: Mithril.Vnode<ComponentAttrs, this>) {
const { className, ...attrs } = this.attrs;
const value = attrs.bidi?.() || attrs.value;
attrs.type ||= 'text';
return (
<div className="ColorInput">
<input className={classList('FormControl', className)} {...attrs} />
<span className="ColorInput-icon" role="presentation">
{icon('fas fa-exclamation-circle')}
</span>
<div className="ColorInput-preview" style={{ '--input-value': value }} role="presentation" />
</div>
);
}
}

View File

@ -23,14 +23,6 @@
margin-bottom: 24px !important;
margin-left: 10px;
}
input {
float: left;
&:first-child {
margin-right: 2%;
}
}
}
.AppearancePage-colors .Checkbox {

View File

@ -0,0 +1,22 @@
.ColorInput {
position: relative;
&-preview, &-icon {
position: absolute;
right: 8px;
bottom: 8px;
width: 20px;
height: 20px;
pointer-events: none;
}
&-preview {
background-color: var(--input-value);
border-radius: 15%;
}
&-icon {
text-align: center;
color: @validation-error-color;
}
}

View File

@ -17,6 +17,7 @@
@import "Badge";
@import "Button";
@import "Checkbox";
@import "ColorInput";
@import "Dropdown";
@import "EditUserModal";
@import "Form";