/** * Copyright (c) Tiny Technologies, Inc. All rights reserved. * Licensed under the LGPL or a commercial license. * For LGPL see License.txt in the project root for license information. * For commercial licenses see https://www.tiny.cloud/ */ import { AddEventsBehaviour, AlloyEvents, AlloyTriggers, Behaviour, Disabling, FormField as AlloyFormField, Input as AlloyInput, Invalidating, Keying, NativeEvents, Representing, SketchSpec, Tabstopping, SystemEvents } from '@ephox/alloy'; import { Types } from '@ephox/bridge'; import { Arr, Fun, Future, Option, Result } from '@ephox/katamari'; import { Traverse } from '@ephox/sugar'; import { renderFormFieldWith, renderLabel } from 'tinymce/themes/silver/ui/alien/FieldLabeller'; import { UiFactoryBackstageProviders } from '../../backstage/Backstage'; import { formChangeEvent, formSubmitEvent } from '../general/FormEvents'; import { Omit } from '../Omit'; const renderTextField = function (spec: TextField, providersBackstage: UiFactoryBackstageProviders) { const pLabel = spec.label.map((label) => renderLabel(label, providersBackstage)); const baseInputBehaviours = [ Disabling.config({ disabled: spec.disabled }), Keying.config({ mode: 'execution', useEnter: spec.multiline !== true, useControlEnter: spec.multiline === true, execute: (comp) => { AlloyTriggers.emit(comp, formSubmitEvent); return Option.some(true); }, }), AddEventsBehaviour.config('textfield-change', [ AlloyEvents.run(NativeEvents.input(), (component, _) => { AlloyTriggers.emitWith(component, formChangeEvent, { name: spec.name } ); }), AlloyEvents.run(SystemEvents.postPaste(), (component, _) => { AlloyTriggers.emitWith(component, formChangeEvent, { name: spec.name } ); }) ]), Tabstopping.config({}) ]; const validatingBehaviours = spec.validation.map((vl) => { return Invalidating.config({ getRoot(input) { return Traverse.parent(input.element()); }, invalidClass: 'tox-invalid', validator: { validate(input) { const v = Representing.getValue(input); const result = vl.validator(v); return Future.pure(result === true ? Result.value(v) : Result.error(result)); }, validateOnLoad: vl.validateOnLoad } }); }).toArray(); const placeholder = spec.placeholder.fold( Fun.constant({}), (p) => ({ placeholder: providersBackstage.translate(p) })); const inputAttributes = { ...placeholder }; const pField = AlloyFormField.parts().field({ tag: spec.multiline === true ? 'textarea' : 'input', inputAttributes, inputClasses: [spec.classname], inputBehaviours: Behaviour.derive( Arr.flatten>([ baseInputBehaviours, validatingBehaviours ]) ), selectOnFocus: false, factory: AlloyInput }); const extraClasses = spec.flex ? ['tox-form__group--stretched'] : []; const extraClasses2 = extraClasses.concat(spec.maximized ? ['tox-form-group--maximize'] : []); const extraBehaviours = [ Disabling.config({ disabled: spec.disabled, onDisabled: (comp) => { AlloyFormField.getField(comp).each(Disabling.disable); }, onEnabled: (comp) => { AlloyFormField.getField(comp).each(Disabling.enable); } }) ]; return renderFormFieldWith(pLabel, pField, extraClasses2, extraBehaviours); }; export type Validator = (v: string) => true | string; export interface TextField { multiline: boolean; name: string; classname: string; flex: boolean; label: Option; placeholder: Option; disabled: boolean; validation: Option<{ validator: Validator; validateOnLoad?: boolean }>; maximized: boolean; } type InputSpec = Omit; type TextAreaSpec = Omit; const renderInput = (spec: InputSpec, providersBackstage: UiFactoryBackstageProviders): SketchSpec => { return renderTextField({ name: spec.name, multiline: false, label: spec.label, placeholder: spec.placeholder, flex: false, disabled: spec.disabled, classname: 'tox-textfield', validation: Option.none(), maximized: spec.maximized }, providersBackstage); }; const renderTextarea = (spec: TextAreaSpec, providersBackstage: UiFactoryBackstageProviders): SketchSpec => { return renderTextField({ name: spec.name, multiline: true, label: spec.label, placeholder: spec.placeholder, flex: true, disabled: spec.disabled, classname: 'tox-textarea', validation: Option.none(), maximized: spec.maximized }, providersBackstage); }; export { renderInput, renderTextarea };