From 579a0a883857dbf4e1c6522c79d670eeab3a17c5 Mon Sep 17 00:00:00 2001 From: gingervitis Date: Sat, 30 Jan 2021 12:27:07 -0800 Subject: [PATCH 1/4] make basic form field wit no submit, so a submit text field can compose it --- web/pages/components/config/edit-tags.tsx | 21 +++++++++++++++++---- web/pages/index.tsx | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/web/pages/components/config/edit-tags.tsx b/web/pages/components/config/edit-tags.tsx index c496c624e..7b05347b9 100644 --- a/web/pages/components/config/edit-tags.tsx +++ b/web/pages/components/config/edit-tags.tsx @@ -4,6 +4,8 @@ import { Typography, Tag, Input } from 'antd'; import { ServerStatusContext } from '../../../utils/server-status-context'; import { FIELD_PROPS_TAGS, RESET_TIMEOUT, SUCCESS_STATES, postConfigUpdateToAPI } from './constants'; +import TextField from './form-textfield-nosubmit'; +import { UpdateArgs } from '../../../types/config-section'; const { Title } = Typography; @@ -59,11 +61,11 @@ export default function EditInstanceTags() { }); }; - const handleInputChange = e => { + const handleInputChange = ({ value }: UpdateArgs) => { if (submitStatusMessage !== '') { setSubmitStatusMessage(''); } - setNewTagInput(e.target.value); + setNewTagInput(value); }; // send to api and do stuff @@ -115,7 +117,18 @@ export default function EditInstanceTags() { {newStatusIcon} {newStatusMessage} {submitStatusMessage}
- + {/* + /> */}
); diff --git a/web/pages/index.tsx b/web/pages/index.tsx index 13adcd505..c8ab68adb 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -16,7 +16,7 @@ import StatisticItem from "./components/statistic" import LogTable from "./components/log-table"; import Offline from './offline-notice'; import TextField from './components/config/form-textfield'; -import { API_STREAM_TITLE, postConfigUpdateToAPI, TEXTFIELD_PROPS_STREAM_TITLE } from './components/config/constants'; +import { TEXTFIELD_PROPS_STREAM_TITLE } from './components/config/constants'; import { LOGS_WARN, From dfa3d28b55abb1ad1a52cea5b9efd3a0115aec4c Mon Sep 17 00:00:00 2001 From: gingervitis Date: Sat, 30 Jan 2021 19:33:01 -0800 Subject: [PATCH 2/4] add input status utils --- web/utils/input-statuses.tsx | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 web/utils/input-statuses.tsx diff --git a/web/utils/input-statuses.tsx b/web/utils/input-statuses.tsx new file mode 100644 index 000000000..63a59aff5 --- /dev/null +++ b/web/utils/input-statuses.tsx @@ -0,0 +1,58 @@ +import { CheckCircleFilled, ExclamationCircleFilled, LoadingOutlined, WarningOutlined } from '@ant-design/icons'; + +export const STATUS_RESET_TIMEOUT = 3000; + +export const STATUS_ERROR = 'error'; +export const STATUS_INVALID = 'invalid'; +export const STATUS_PROCESSING = 'proessing'; +export const STATUS_SUCCESS = 'success'; +export const STATUS_WARNING = 'warning'; + +export type InputStatusTypes = + typeof STATUS_ERROR | + typeof STATUS_INVALID | + typeof STATUS_PROCESSING | + typeof STATUS_SUCCESS | + typeof STATUS_WARNING; + +export type StatusState = { + icon: any; // Element type of sorts? + message: string; +}; + +export const INPUT_STATES = { + [STATUS_SUCCESS]: { + icon: , + message: 'Success!', + }, + [STATUS_ERROR]: { + icon: , + message: 'An error occurred.', + }, + [STATUS_INVALID]: { + icon: , + message: 'An error occurred.', + }, + [STATUS_PROCESSING]: { + icon: , + message: '', + }, + [STATUS_WARNING]: { + icon: , + message: '', + }, +}; + +// Don't like any of the default messages in INPUT_STATES? Create a state with custom message by providing an icon style with your message. +export function createInputStatus(type: InputStatusTypes, message: string): StatusState { + if (!type || !INPUT_STATES[type]) { + return null; + } + if (message === null) { + return INPUT_STATES[type]; + } + return { + icon: INPUT_STATES[type].icon, + message, + }; +} From c255b81093ffdca3606377bafe7f62d79024228f Mon Sep 17 00:00:00 2001 From: gingervitis Date: Sun, 31 Jan 2021 00:48:39 -0800 Subject: [PATCH 3/4] rename formfield files for clarity --- .../config/edit-instance-details.tsx | 14 +- .../components/config/edit-server-details.tsx | 13 +- .../config/form-textfield-with-submit.tsx | 137 +++++++++++ .../components/config/form-textfield.tsx | 214 ------------------ ....tsx => form-toggleswitch-with-submit.tsx} | 0 web/pages/index.tsx | 4 +- web/utils/input-statuses.tsx | 2 +- 7 files changed, 154 insertions(+), 230 deletions(-) create mode 100644 web/pages/components/config/form-textfield-with-submit.tsx delete mode 100644 web/pages/components/config/form-textfield.tsx rename web/pages/components/config/{form-toggleswitch.tsx => form-toggleswitch-with-submit.tsx} (100%) diff --git a/web/pages/components/config/edit-instance-details.tsx b/web/pages/components/config/edit-instance-details.tsx index 3ec5129d2..af2821836 100644 --- a/web/pages/components/config/edit-instance-details.tsx +++ b/web/pages/components/config/edit-instance-details.tsx @@ -1,5 +1,5 @@ import React, { useState, useContext, useEffect } from 'react'; -import TextField, { TEXTFIELD_TYPE_TEXTAREA, TEXTFIELD_TYPE_URL } from './form-textfield'; +import TextFieldWithSubmit, { TEXTFIELD_TYPE_TEXTAREA, TEXTFIELD_TYPE_URL } from './form-textfield-with-submit'; import { ServerStatusContext } from '../../../utils/server-status-context'; import { postConfigUpdateToAPI, TEXTFIELD_PROPS_USERNAME, TEXTFIELD_PROPS_INSTANCE_URL, TEXTFIELD_PROPS_SERVER_TITLE, TEXTFIELD_PROPS_STREAM_TITLE, TEXTFIELD_PROPS_SERVER_SUMMARY, TEXTFIELD_PROPS_LOGO, API_YP_SWITCH } from './constants'; @@ -47,7 +47,7 @@ export default function EditInstanceDetails() { return (
- - - - - -
-
- - - (null); + + const [hasChanged, setHasChanged] = useState(false); + const [fieldValueForSubmit, setFieldValueForSubmit] = useState(''); + + const serverStatusData = useContext(ServerStatusContext); + const { setFieldInConfigState } = serverStatusData || {}; + + let resetTimer = null; + + const { + apiPath, + configPath = '', + initialValue, + ...textFieldProps // rest of props + } = props; + + const { + fieldName, + required, + status, + // type, + value, + onChange, + // onBlur, + onSubmit, + } = textFieldProps; + + // Clear out any validation states and messaging + const resetStates = () => { + setFieldStatus(null); + setHasChanged(false); + clearTimeout(resetTimer); + resetTimer = null; + }; + + useEffect(() => { + // TODO: Add native validity checks here, somehow + // https://developer.mozilla.org/en-US/docs/Web/API/ValidityState + // const hasValidity = (type !== TEXTFIELD_TYPE_NUMBER && e.target.validity.valid) || type === TEXTFIELD_TYPE_NUMBER ; + if ((required && (value === '' || value === null)) || value === initialValue) { + setHasChanged(false); + } else { + // show submit button + resetStates(); + setHasChanged(true); + setFieldValueForSubmit(value); + } + }, [value]); + + // if field is required but value is empty, or equals initial value, then don't show submit/update button. otherwise clear out any result messaging and display button. + const handleChange = ({ fieldName: changedFieldName, value: changedValue }: UpdateArgs) => { + if (onChange) { + onChange({ fieldName: changedFieldName, value: changedValue }); + } + }; + + // if you blur a required field with an empty value, restore its original value in state (parent's state), if an onChange from parent is available. + const handleBlur = ({ value: changedValue }: UpdateArgs) => { + if (onChange && required && changedValue === '') { + onChange({ fieldName, value: initialValue }); + } + }; + + // how to get current value of input + const handleSubmit = async () => { + if ((required && fieldValueForSubmit !== '') || fieldValueForSubmit !== initialValue) { + setFieldStatus(createInputStatus(STATUS_PROCESSING)); + + // setSubmitStatus('validating'); + + await postConfigUpdateToAPI({ + apiPath, + data: { value: fieldValueForSubmit }, + onSuccess: () => { + setFieldInConfigState({ fieldName, value: fieldValueForSubmit, path: configPath }); + setFieldStatus(createInputStatus(STATUS_SUCCESS)); + // setSubmitStatus('success'); + }, + onError: (message: string) => { + setFieldStatus(createInputStatus(STATUS_ERROR, `There was an error: ${message}`)); + + // setSubmitStatus('error'); + // setSubmitStatusMessage(`There was an error: ${message}`); + }, + }); + resetTimer = setTimeout(resetStates, RESET_TIMEOUT); + + // if an extra onSubmit handler was sent in as a prop, let's run that too. + if (onSubmit) { + onSubmit(); + } + } + } + + return ( +
+ + + { hasChanged ? : null } +
+ ); +} + +TextFieldWithSubmit.defaultProps = { + configPath: '', + initialValue: '', +}; diff --git a/web/pages/components/config/form-textfield.tsx b/web/pages/components/config/form-textfield.tsx deleted file mode 100644 index 95c82cae0..000000000 --- a/web/pages/components/config/form-textfield.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import React, { useEffect, useState, useContext } from 'react'; -import { Button, Input, InputNumber } from 'antd'; -import { FormItemProps } from 'antd/es/form'; - -import { RESET_TIMEOUT, postConfigUpdateToAPI } from './constants'; - -import { FieldUpdaterFunc } from '../../../types/config-section'; -import { ServerStatusContext } from '../../../utils/server-status-context'; -import InfoTip from '../info-tip'; - -export const TEXTFIELD_TYPE_TEXT = 'default'; -export const TEXTFIELD_TYPE_PASSWORD = 'password'; // Input.Password -export const TEXTFIELD_TYPE_NUMBER = 'numeric'; -export const TEXTFIELD_TYPE_TEXTAREA = 'textarea'; -export const TEXTFIELD_TYPE_URL = 'url'; - -interface TextFieldProps { - apiPath: string; - fieldName: string; - - configPath?: string; - disabled?: boolean; - initialValue?: string; - label?: string; - maxLength?: number; - placeholder?: string; - required?: boolean; - tip?: string; - type?: string; - value?: string | number; - onSubmit?: () => void; - onBlur?: () => void; - onChange?: FieldUpdaterFunc; -} - - -export default function TextField(props: TextFieldProps) { - const [submitStatus, setSubmitStatus] = useState(''); - const [submitStatusMessage, setSubmitStatusMessage] = useState(''); - const [hasChanged, setHasChanged] = useState(false); - const [fieldValueForSubmit, setFieldValueForSubmit] = useState(''); - - const serverStatusData = useContext(ServerStatusContext); - const { setFieldInConfigState } = serverStatusData || {}; - - let resetTimer = null; - - const { - apiPath, - configPath = '', - disabled = false, - fieldName, - initialValue, - label, - maxLength, - onBlur, - onChange, - onSubmit, - placeholder, - required, - tip, - type, - value, - } = props; - - // Clear out any validation states and messaging - const resetStates = () => { - setSubmitStatus(''); - setHasChanged(false); - clearTimeout(resetTimer); - resetTimer = null; - }; - - useEffect(() => { - // TODO: Add native validity checks here, somehow - // https://developer.mozilla.org/en-US/docs/Web/API/ValidityState - // const hasValidity = (type !== TEXTFIELD_TYPE_NUMBER && e.target.validity.valid) || type === TEXTFIELD_TYPE_NUMBER ; - if ((required && (value === '' || value === null)) || value === initialValue) { - setHasChanged(false); - } else { - // show submit button - resetStates(); - setHasChanged(true); - setFieldValueForSubmit(value); - } - }, [value]); - - // if field is required but value is empty, or equals initial value, then don't show submit/update button. otherwise clear out any result messaging and display button. - const handleChange = (e: any) => { - const val = type === TEXTFIELD_TYPE_NUMBER ? e : e.target.value; - - // if an extra onChange handler was sent in as a prop, let's run that too. - if (onChange) { - onChange({ fieldName, value: val }); - } - }; - - // if you blur a required field with an empty value, restore its original value in state (parent's state), if an onChange from parent is available. - const handleBlur = e => { - if (!onChange) { - return; - } - const val = e.target.value; - if (required && val === '') { - onChange({ fieldName, value: initialValue }); - } - // if an extra onBlur handler was sent in as a prop, let's run that too. - if (onBlur) { - onBlur(); - } - }; - - // how to get current value of input - const handleSubmit = async () => { - if ((required && fieldValueForSubmit !== '') || fieldValueForSubmit !== initialValue) { - setSubmitStatus('validating'); - - await postConfigUpdateToAPI({ - apiPath, - data: { value: fieldValueForSubmit }, - onSuccess: () => { - setFieldInConfigState({ fieldName, value: fieldValueForSubmit, path: configPath }); - setSubmitStatus('success'); - }, - onError: (message: string) => { - setSubmitStatus('error'); - setSubmitStatusMessage(`There was an error: ${message}`); - }, - }); - resetTimer = setTimeout(resetStates, RESET_TIMEOUT); - - // if an extra onSubmit handler was sent in as a prop, let's run that too. - if (onSubmit) { - onSubmit(); - } - } - } - - // display the appropriate Ant text field - let Field = Input as typeof Input | typeof InputNumber | typeof Input.TextArea | typeof Input.Password; - let fieldProps = {}; - if (type === TEXTFIELD_TYPE_TEXTAREA) { - Field = Input.TextArea; - fieldProps = { - autoSize: true, - }; - } else if (type === TEXTFIELD_TYPE_PASSWORD) { - Field = Input.Password; - fieldProps = { - visibilityToggle: true, - }; - } else if (type === TEXTFIELD_TYPE_NUMBER) { - Field = InputNumber; - fieldProps = { - type: 'number', - min: 1, - max: (10**maxLength) - 1, - onKeyDown: (e: React.KeyboardEvent) => { - if (e.target.value.length > maxLength - 1 ) - e.preventDefault() - return false; - } - }; - } else if (type === TEXTFIELD_TYPE_URL) { - fieldProps = { - type: 'url', - }; - } - - const fieldId = `field-${fieldName}`; - - return ( -
- { required ? * : null } - -
- -
- - {submitStatus} - {submitStatusMessage} - - { hasChanged ? : null } - -
- ); -} - -TextField.defaultProps = { - configPath: '', - disabled: false, - initialValue: '', - label: '', - maxLength: null, - placeholder: '', - required: false, - tip: '', - type: TEXTFIELD_TYPE_TEXT, - value: '', - onSubmit: () => {}, - onBlur: () => {}, - onChange: () => {}, -}; diff --git a/web/pages/components/config/form-toggleswitch.tsx b/web/pages/components/config/form-toggleswitch-with-submit.tsx similarity index 100% rename from web/pages/components/config/form-toggleswitch.tsx rename to web/pages/components/config/form-toggleswitch-with-submit.tsx diff --git a/web/pages/index.tsx b/web/pages/index.tsx index c8ab68adb..9df1805e2 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -15,7 +15,7 @@ import { ServerStatusContext } from "../utils/server-status-context"; import StatisticItem from "./components/statistic" import LogTable from "./components/log-table"; import Offline from './offline-notice'; -import TextField from './components/config/form-textfield'; +import TextFieldWithSubmit from './components/config/form-textfield-with-submit'; import { TEXTFIELD_PROPS_STREAM_TITLE } from './components/config/constants'; import { @@ -158,7 +158,7 @@ export default function Home() {
- Date: Sun, 31 Jan 2021 00:58:27 -0800 Subject: [PATCH 4/4] add regular form field comp with no submit button --- .../components/config/edit-directory.tsx | 2 +- .../components/config/edit-server-details.tsx | 2 +- web/pages/components/config/edit-tags.tsx | 70 ++++---- .../config/form-textfield-with-submit.tsx | 2 +- .../components/config/form-textfield.tsx | 155 ++++++++++++++++++ 5 files changed, 196 insertions(+), 35 deletions(-) create mode 100644 web/pages/components/config/form-textfield.tsx diff --git a/web/pages/components/config/edit-directory.tsx b/web/pages/components/config/edit-directory.tsx index fcd4664a7..08608cf10 100644 --- a/web/pages/components/config/edit-directory.tsx +++ b/web/pages/components/config/edit-directory.tsx @@ -2,7 +2,7 @@ import React, { useState, useContext, useEffect } from 'react'; import { Typography } from 'antd'; -import ToggleSwitch from './form-toggleswitch'; +import ToggleSwitch from './form-toggleswitch-with-submit'; import { ServerStatusContext } from '../../../utils/server-status-context'; import { FIELD_PROPS_NSFW, FIELD_PROPS_YP } from './constants'; diff --git a/web/pages/components/config/edit-server-details.tsx b/web/pages/components/config/edit-server-details.tsx index f4593fbb7..d052c7bb1 100644 --- a/web/pages/components/config/edit-server-details.tsx +++ b/web/pages/components/config/edit-server-details.tsx @@ -2,7 +2,7 @@ import React, { useState, useContext, useEffect } from 'react'; import { Button, Tooltip } from 'antd'; import { CopyOutlined, RedoOutlined } from '@ant-design/icons'; -import { TEXTFIELD_TYPE_NUMBER, TEXTFIELD_TYPE_PASSWORD } from './form-textfield-nosubmit'; +import { TEXTFIELD_TYPE_NUMBER, TEXTFIELD_TYPE_PASSWORD } from './form-textfield'; import TextFieldWithSubmit from './form-textfield-with-submit'; import { ServerStatusContext } from '../../../utils/server-status-context'; diff --git a/web/pages/components/config/edit-tags.tsx b/web/pages/components/config/edit-tags.tsx index 7b05347b9..d1382bfef 100644 --- a/web/pages/components/config/edit-tags.tsx +++ b/web/pages/components/config/edit-tags.tsx @@ -3,16 +3,18 @@ import React, { useContext, useState, useEffect } from 'react'; import { Typography, Tag, Input } from 'antd'; import { ServerStatusContext } from '../../../utils/server-status-context'; -import { FIELD_PROPS_TAGS, RESET_TIMEOUT, SUCCESS_STATES, postConfigUpdateToAPI } from './constants'; -import TextField from './form-textfield-nosubmit'; +import { FIELD_PROPS_TAGS, RESET_TIMEOUT, postConfigUpdateToAPI } from './constants'; +import TextField from './form-textfield'; import { UpdateArgs } from '../../../types/config-section'; +import { createInputStatus, StatusState, STATUS_ERROR, STATUS_PROCESSING, STATUS_SUCCESS, STATUS_WARNING } from '../../../utils/input-statuses'; const { Title } = Typography; export default function EditInstanceTags() { const [newTagInput, setNewTagInput] = useState(''); - const [submitStatus, setSubmitStatus] = useState(null); - const [submitStatusMessage, setSubmitStatusMessage] = useState(''); + const [fieldStatus, setFieldStatus] = useState(null); + // const [submitStatus, setSubmitStatus] = useState(null); + // const [submitStatusMessage, setSubmitStatusMessage] = useState(''); const serverStatusData = useContext(ServerStatusContext); const { serverConfig, setFieldInConfigState } = serverStatusData || {}; @@ -35,36 +37,46 @@ export default function EditInstanceTags() { }, []); const resetStates = () => { - setSubmitStatus(null); - setSubmitStatusMessage(''); + // setSubmitStatus(null); + // setSubmitStatusMessage(''); + setFieldStatus(null); resetTimer = null; clearTimeout(resetTimer); } // posts all the tags at once as an array obj const postUpdateToAPI = async (postValue: any) => { + setFieldStatus(createInputStatus(STATUS_PROCESSING)); + await postConfigUpdateToAPI({ apiPath, data: { value: postValue }, onSuccess: () => { setFieldInConfigState({ fieldName: 'tags', value: postValue, path: configPath }); - setSubmitStatus('success'); - setSubmitStatusMessage('Tags updated.'); + setFieldStatus(createInputStatus(STATUS_SUCCESS, 'Tags updated.')); + + // setSubmitStatus('success'); + // setSubmitStatusMessage('Tags updated.'); setNewTagInput(''); resetTimer = setTimeout(resetStates, RESET_TIMEOUT); }, onError: (message: string) => { - setSubmitStatus('error'); - setSubmitStatusMessage(message); + setFieldStatus(createInputStatus(STATUS_ERROR, message)); + + // setSubmitStatus('error'); + // setSubmitStatusMessage(message); resetTimer = setTimeout(resetStates, RESET_TIMEOUT); }, }); }; const handleInputChange = ({ value }: UpdateArgs) => { - if (submitStatusMessage !== '') { - setSubmitStatusMessage(''); + if (!fieldStatus) { + setFieldStatus(null); } + // if (submitStatusMessage !== '') { + // setSubmitStatusMessage(''); + // } setNewTagInput(value); }; @@ -73,11 +85,15 @@ export default function EditInstanceTags() { resetStates(); const newTag = newTagInput.trim(); if (newTag === '') { - setSubmitStatusMessage('Please enter a tag'); + setFieldStatus(createInputStatus(STATUS_WARNING, 'Please enter a tag')); + + // setSubmitStatusMessage('Please enter a tag'); return; } if (tags.some(tag => tag.toLowerCase() === newTag.toLowerCase())) { - setSubmitStatusMessage('This tag is already used!'); + setFieldStatus(createInputStatus(STATUS_WARNING, 'This tag is already used!')); + + // setSubmitStatusMessage('This tag is already used!'); return; } @@ -92,10 +108,10 @@ export default function EditInstanceTags() { postUpdateToAPI(updatedTags); } - const { - icon: newStatusIcon = null, - message: newStatusMessage = '', - } = SUCCESS_STATES[submitStatus] || {}; + // const { + // icon: newStatusIcon = null, + // message: newStatusMessage = '', + // } = fieldStatus || {}; return (
@@ -113,9 +129,9 @@ export default function EditInstanceTags() { ); })}
-
+ {/*
{newStatusIcon} {newStatusMessage} {submitStatusMessage} -
+
*/}
- {/* */}
); diff --git a/web/pages/components/config/form-textfield-with-submit.tsx b/web/pages/components/config/form-textfield-with-submit.tsx index 4d9ef80ad..5044225b3 100644 --- a/web/pages/components/config/form-textfield-with-submit.tsx +++ b/web/pages/components/config/form-textfield-with-submit.tsx @@ -4,7 +4,7 @@ import { Button } from 'antd'; import { RESET_TIMEOUT, postConfigUpdateToAPI } from './constants'; import { ServerStatusContext } from '../../../utils/server-status-context'; -import TextField, { TextFieldProps } from './form-textfield-nosubmit'; +import TextField, { TextFieldProps } from './form-textfield'; import { createInputStatus, StatusState, STATUS_ERROR, STATUS_PROCESSING, STATUS_SUCCESS } from '../../../utils/input-statuses'; import { UpdateArgs } from '../../../types/config-section'; diff --git a/web/pages/components/config/form-textfield.tsx b/web/pages/components/config/form-textfield.tsx new file mode 100644 index 000000000..cdb6e6ca1 --- /dev/null +++ b/web/pages/components/config/form-textfield.tsx @@ -0,0 +1,155 @@ +import React from 'react'; +import { Input, InputNumber } from 'antd'; +import { FieldUpdaterFunc } from '../../../types/config-section'; +import InfoTip from '../info-tip'; +import { StatusState } from '../../../utils/input-statuses'; + +export const TEXTFIELD_TYPE_TEXT = 'default'; +export const TEXTFIELD_TYPE_PASSWORD = 'password'; // Input.Password +export const TEXTFIELD_TYPE_NUMBER = 'numeric'; // InputNumber +export const TEXTFIELD_TYPE_TEXTAREA = 'textarea'; // Input.TextArea +export const TEXTFIELD_TYPE_URL = 'url'; + +export interface TextFieldProps { + fieldName: string; + + onSubmit?: () => void; + onPressEnter?: () => void; + + className?: string; + disabled?: boolean; + label?: string; + maxLength?: number; + placeholder?: string; + required?: boolean; + status?: StatusState; + tip?: string; + type?: string; + value?: string | number; + onBlur?: FieldUpdaterFunc; + onChange?: FieldUpdaterFunc; +} + + +export default function TextField(props: TextFieldProps) { + const { + className, + disabled, + fieldName, + label, + maxLength, + onBlur, + onChange, + onPressEnter, + placeholder, + required, + status, + tip, + type, + value, + } = props; + + // if field is required but value is empty, or equals initial value, then don't show submit/update button. otherwise clear out any result messaging and display button. + const handleChange = (e: any) => { + const val = type === TEXTFIELD_TYPE_NUMBER ? e : e.target.value; + // if an extra onChange handler was sent in as a prop, let's run that too. + if (onChange) { + onChange({ fieldName, value: val }); + } + }; + + // if you blur a required field with an empty value, restore its original value in state (parent's state), if an onChange from parent is available. + const handleBlur = (e: any) => { + const val = e.target.value; + if (onBlur) { + onBlur({ value: val }); + } + }; + + const handlePressEnter = () => { + if (onPressEnter) { + onPressEnter(); + } + } + + + // display the appropriate Ant text field + let Field = Input as typeof Input | typeof InputNumber | typeof Input.TextArea | typeof Input.Password; + let fieldProps = {}; + if (type === TEXTFIELD_TYPE_TEXTAREA) { + Field = Input.TextArea; + fieldProps = { + autoSize: true, + }; + } else if (type === TEXTFIELD_TYPE_PASSWORD) { + Field = Input.Password; + fieldProps = { + visibilityToggle: true, + }; + } else if (type === TEXTFIELD_TYPE_NUMBER) { + Field = InputNumber; + fieldProps = { + type: 'number', + min: 1, + max: (10**maxLength) - 1, + onKeyDown: (e: React.KeyboardEvent) => { + if (e.target.value.length > maxLength - 1 ) + e.preventDefault(); + return false; + } + }; + } else if (type === TEXTFIELD_TYPE_URL) { + fieldProps = { + type: 'url', + }; + } + + const fieldId = `field-${fieldName}`; + + const { icon: statusIcon, message: statusMessage } = status || {}; + + return ( +
+ { required ? * : null } + +
+ +
+ + { status ? statusMessage : null } + { status ? statusIcon : null } +
+ ); +} + +TextField.defaultProps = { + className: '', + // configPath: '', + disabled: false, + // initialValue: '', + label: '', + maxLength: null, + + placeholder: '', + required: false, + status: null, + tip: '', + type: TEXTFIELD_TYPE_TEXT, + value: '', + onSubmit: () => {}, + onBlur: () => {}, + onChange: () => {}, + onPressEnter: () => {}, +};