diff --git a/framework/core/js/src/@types/global.d.ts b/framework/core/js/src/@types/global.d.ts index 382d32d5d..76b9f3e5a 100644 --- a/framework/core/js/src/@types/global.d.ts +++ b/framework/core/js/src/@types/global.d.ts @@ -21,6 +21,8 @@ declare type KeysOfType = { */ declare type KeyOfType = KeysOfType[keyof Type]; +declare type VnodeElementTag, State = Record> = string | ComponentTypes; + /** * @deprecated Please import `app` from a namespace instead of using it as a global variable. * diff --git a/framework/core/js/src/admin/components/AdminPage.tsx b/framework/core/js/src/admin/components/AdminPage.tsx index 3170270bb..981a246cc 100644 --- a/framework/core/js/src/admin/components/AdminPage.tsx +++ b/framework/core/js/src/admin/components/AdminPage.tsx @@ -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; } else { - settingElement = ; + let Tag: VnodeElementTag = 'input'; + + if (type === ColorPreviewSettingType) { + Tag = ColorPreviewInput; + } else { + componentAttrs.type = type; + } + + settingElement = ; } } diff --git a/framework/core/js/src/admin/components/AppearancePage.js b/framework/core/js/src/admin/components/AppearancePage.js index 727cf8deb..e6a95c52c 100644 --- a/framework/core/js/src/admin/components/AppearancePage.js +++ b/framework/core/js/src/admin/components/AppearancePage.js @@ -25,12 +25,12 @@ export default class AppearancePage extends AdminPage {
{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); - } } diff --git a/framework/core/js/src/common/compat.js b/framework/core/js/src/common/compat.js index 9111a642a..47cd56cb0 100644 --- a/framework/core/js/src/common/compat.js +++ b/framework/core/js/src/common/compat.js @@ -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, diff --git a/framework/core/js/src/common/components/ColorPreviewInput.tsx b/framework/core/js/src/common/components/ColorPreviewInput.tsx new file mode 100644 index 000000000..c626205ac --- /dev/null +++ b/framework/core/js/src/common/components/ColorPreviewInput.tsx @@ -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) { + const { className, ...attrs } = this.attrs; + const value = attrs.bidi?.() || attrs.value; + + attrs.type ||= 'text'; + + return ( +
+ + + + {icon('fas fa-exclamation-circle')} + + +
+
+ ); + } +} diff --git a/framework/core/less/admin/AppearancePage.less b/framework/core/less/admin/AppearancePage.less index 0e7ba3471..14c2e5ceb 100644 --- a/framework/core/less/admin/AppearancePage.less +++ b/framework/core/less/admin/AppearancePage.less @@ -23,14 +23,6 @@ margin-bottom: 24px !important; margin-left: 10px; } - - input { - float: left; - - &:first-child { - margin-right: 2%; - } - } } .AppearancePage-colors .Checkbox { diff --git a/framework/core/less/common/ColorInput.less b/framework/core/less/common/ColorInput.less new file mode 100644 index 000000000..ec93ee6ef --- /dev/null +++ b/framework/core/less/common/ColorInput.less @@ -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; + } +} diff --git a/framework/core/less/common/common.less b/framework/core/less/common/common.less index b141aaec1..53a93d1e3 100644 --- a/framework/core/less/common/common.less +++ b/framework/core/less/common/common.less @@ -17,6 +17,7 @@ @import "Badge"; @import "Button"; @import "Checkbox"; +@import "ColorInput"; @import "Dropdown"; @import "EditUserModal"; @import "Form";