mirror of
https://github.com/IT4Change/ohmyform-ui.git
synced 2025-12-12 17:25:51 +00:00
change default form layout to card
This commit is contained in:
parent
951dd2e5b4
commit
fe51c528d2
@ -25,6 +25,7 @@ module.exports = {
|
|||||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
'@typescript-eslint/no-unsafe-argument': 'off',
|
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
'react/prop-types': 'off',
|
'react/prop-types': 'off',
|
||||||
'@typescript-eslint/no-empty-interface': 'off',
|
'@typescript-eslint/no-empty-interface': 'off',
|
||||||
'jsx-a11y/no-autofocus': 'off',
|
'jsx-a11y/no-autofocus': 'off',
|
||||||
|
|||||||
@ -16,11 +16,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- update translations (https://github.com/ohmyform/ui/pull/70)
|
- update translations (https://github.com/ohmyform/ui/pull/70)
|
||||||
- show warning icon in form list if not public
|
- show warning icon in form list if not public
|
||||||
- default form layout is now "card"
|
- default form layout is now "card"
|
||||||
|
- creating of new fields combined in new field types
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- locale scripts were missing dependency
|
- locale scripts were missing dependency
|
||||||
- edit user shows now email in title
|
- edit user shows now email in title
|
||||||
|
- focus is now passed also do slide layout fields
|
||||||
|
- empty fields are no longer submitted
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useCallback, useState } from 'react'
|
|||||||
import { SubmissionFragment } from '../../../graphql/fragment/submission.fragment'
|
import { SubmissionFragment } from '../../../graphql/fragment/submission.fragment'
|
||||||
import { useFormQuery } from '../../../graphql/query/form.query'
|
import { useFormQuery } from '../../../graphql/query/form.query'
|
||||||
import { useSubmissionPagerImperativeQuery } from '../../../graphql/query/submission.pager.query'
|
import { useSubmissionPagerImperativeQuery } from '../../../graphql/query/submission.pager.query'
|
||||||
|
import { fieldTypes } from '../types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
form: string
|
form: string
|
||||||
@ -66,9 +67,9 @@ export const ExportSubmissionAction: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
data.fields.forEach((field) => {
|
data.fields.forEach((field) => {
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
fieldTypes[field.type]?.stringifyValue(field.value)
|
||||||
const decoded: { value: CellValue } = JSON.parse(field.value)
|
|
||||||
row.push(decoded.value || '')
|
row.push(fieldTypes[field.type]?.stringifyValue(field.value))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
row.push('')
|
row.push('')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,9 +6,8 @@ import { FieldData } from 'rc-field-form/lib/interface'
|
|||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FormFieldFragment, FormFieldLogicFragment } from '../../../graphql/fragment/form.fragment'
|
import { FormFieldFragment, FormFieldLogicFragment } from '../../../graphql/fragment/form.fragment'
|
||||||
|
import { fieldTypes } from '../types'
|
||||||
import { LogicBlock } from './logic.block'
|
import { LogicBlock } from './logic.block'
|
||||||
import { adminTypes } from './types'
|
|
||||||
import { TextType } from './types/text.type'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
form: FormInstance
|
form: FormInstance
|
||||||
@ -34,7 +33,7 @@ export const FieldCard: React.FC<Props> = ({
|
|||||||
const type = form.getFieldValue([
|
const type = form.getFieldValue([
|
||||||
'form', 'fields', field.name as string, 'type',
|
'form', 'fields', field.name as string, 'type',
|
||||||
]) as string
|
]) as string
|
||||||
const TypeComponent = adminTypes[type] || TextType
|
const TypeComponent = (fieldTypes[type] || fieldTypes['textfield']).adminFormField()
|
||||||
|
|
||||||
const [shouldUpdate, setShouldUpdate] = useState(false)
|
const [shouldUpdate, setShouldUpdate] = useState(false)
|
||||||
const [nextTitle, setNextTitle] = useState<string>(
|
const [nextTitle, setNextTitle] = useState<string>(
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import { FieldData } from 'rc-field-form/lib/interface'
|
|||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FormFieldFragment } from '../../../graphql/fragment/form.fragment'
|
import { FormFieldFragment } from '../../../graphql/fragment/form.fragment'
|
||||||
|
import { fieldTypes } from '../types'
|
||||||
import { FieldCard } from './field.card'
|
import { FieldCard } from './field.card'
|
||||||
import { adminTypes } from './types'
|
|
||||||
|
|
||||||
const logger = debug('FieldsTab')
|
const logger = debug('FieldsTab')
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Select value={nextType} onChange={(e) => setNextType(e)} style={{ minWidth: 200 }}>
|
<Select value={nextType} onChange={(e) => setNextType(e)} style={{ minWidth: 200 }}>
|
||||||
{Object.keys(adminTypes).map((type) => (
|
{Object.keys(fieldTypes).map((type) => (
|
||||||
<Select.Option value={type} key={type}>
|
<Select.Option value={type} key={type}>
|
||||||
{t(`type:${type}.name`)}
|
{t(`type:${type}.name`)}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
SubmissionFragment,
|
SubmissionFragment,
|
||||||
SubmissionFragmentField,
|
SubmissionFragmentField,
|
||||||
} from '../../../graphql/fragment/submission.fragment'
|
} from '../../../graphql/fragment/submission.fragment'
|
||||||
|
import { fieldTypes } from '../types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
form: FormPagerFragment
|
form: FormPagerFragment
|
||||||
@ -30,21 +31,8 @@ export const SubmissionValues: React.FC<Props> = (props) => {
|
|||||||
{
|
{
|
||||||
title: t('submission:value'),
|
title: t('submission:value'),
|
||||||
render(_, row) {
|
render(_, row) {
|
||||||
console.log('row.value', row.value)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(row.value) as { value: string }
|
return fieldTypes[row.type]?.displayValue(row.value)
|
||||||
|
|
||||||
if (Array.isArray(data.value)) {
|
|
||||||
return (
|
|
||||||
<ul>
|
|
||||||
{data.value.map(r => (
|
|
||||||
<li key={r}>{JSON.stringify(r)}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return data.value
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return row.value
|
return row.value
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import dynamic from 'next/dynamic'
|
|
||||||
import { ComponentType } from 'react'
|
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
|
||||||
|
|
||||||
export const adminTypes: {
|
|
||||||
[key: string]: ComponentType<AdminFieldTypeProps>
|
|
||||||
} = {
|
|
||||||
checkbox: dynamic<AdminFieldTypeProps>(() => import('./checkbox.type').then(c => c.CheckboxType )),
|
|
||||||
date: dynamic<AdminFieldTypeProps>(() => import('./date.type').then(c => c.DateType )),
|
|
||||||
dropdown: dynamic<AdminFieldTypeProps>(() => import('./dropdown.type').then(c => c.DropdownType )),
|
|
||||||
email: dynamic<AdminFieldTypeProps>(() => import('./email.type').then(c => c.EmailType )),
|
|
||||||
hidden: dynamic<AdminFieldTypeProps>(() => import('./hidden.type').then(c => c.HiddenType )),
|
|
||||||
link: dynamic<AdminFieldTypeProps>(() => import('./link.type').then(c => c.LinkType )),
|
|
||||||
location: dynamic<AdminFieldTypeProps>(() => import('./location.type').then(c => c.LocationType ), { ssr: false }),
|
|
||||||
number: dynamic<AdminFieldTypeProps>(() => import('./number.type').then(c => c.NumberType )),
|
|
||||||
radio: dynamic<AdminFieldTypeProps>(() => import('./radio.type').then(c => c.RadioType )),
|
|
||||||
rating: dynamic<AdminFieldTypeProps>(() => import('./rating.type').then(c => c.RatingType )),
|
|
||||||
slider: dynamic<AdminFieldTypeProps>(() => import('./slider.type').then(c => c.SliderType )),
|
|
||||||
textarea: dynamic<AdminFieldTypeProps>(() => import('./textarea.type').then(c => c.TextareaType )),
|
|
||||||
textfield: dynamic<AdminFieldTypeProps>(() => import('./text.type').then(c => c.TextType )),
|
|
||||||
yes_no: dynamic<AdminFieldTypeProps>(() => import('./yes_no.type').then(c => c.YesNoType )),
|
|
||||||
}
|
|
||||||
@ -7,11 +7,9 @@ import { StyledH1 } from '../../../styled/h1'
|
|||||||
import { StyledMarkdown } from '../../../styled/markdown'
|
import { StyledMarkdown } from '../../../styled/markdown'
|
||||||
import { useRouter } from '../../../use.router'
|
import { useRouter } from '../../../use.router'
|
||||||
import { fieldTypes } from '../../types'
|
import { fieldTypes } from '../../types'
|
||||||
import { TextType } from '../../types/text.type'
|
|
||||||
import { FieldTypeProps } from '../../types/type.props'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
focus: boolean
|
focus?: boolean
|
||||||
field: FormPublicFieldFragment
|
field: FormPublicFieldFragment
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
}
|
}
|
||||||
@ -19,7 +17,7 @@ interface Props {
|
|||||||
export const Field: React.FC<Props> = ({ field, design, focus, ...props }) => {
|
export const Field: React.FC<Props> = ({ field, design, focus, ...props }) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const FieldInput: React.FC<FieldTypeProps> = fieldTypes[field.type] || TextType
|
const FieldInput = (fieldTypes[field.type] || fieldTypes['text']).inputFormField()
|
||||||
|
|
||||||
const getUrlDefault = (): string => {
|
const getUrlDefault = (): string => {
|
||||||
if (router.query[field.id]) {
|
if (router.query[field.id]) {
|
||||||
@ -57,7 +55,12 @@ export const Field: React.FC<Props> = ({ field, design, focus, ...props }) => {
|
|||||||
<StyledMarkdown design={design} type={'question'} >{field.description}</StyledMarkdown>
|
<StyledMarkdown design={design} type={'question'} >{field.description}</StyledMarkdown>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FieldInput design={design} field={field} urlValue={getUrlDefault()} focus={focus} />
|
<FieldInput
|
||||||
|
design={design}
|
||||||
|
field={field}
|
||||||
|
urlValue={getUrlDefault()}
|
||||||
|
focus={false}/* cannot use this in card layout! try with 2 input fields */
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -139,7 +139,14 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Field key={field.id} field={field} design={design} focus={i === 0} />
|
return (
|
||||||
|
<Field
|
||||||
|
key={field.id}
|
||||||
|
field={field}
|
||||||
|
design={design}
|
||||||
|
focus={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -11,10 +11,9 @@ import { StyledH1 } from '../../../styled/h1'
|
|||||||
import { StyledMarkdown } from '../../../styled/markdown'
|
import { StyledMarkdown } from '../../../styled/markdown'
|
||||||
import { useRouter } from '../../../use.router'
|
import { useRouter } from '../../../use.router'
|
||||||
import { fieldTypes } from '../../types'
|
import { fieldTypes } from '../../types'
|
||||||
import { TextType } from '../../types/text.type'
|
|
||||||
import { FieldTypeProps } from '../../types/type.props'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
focus: boolean
|
||||||
field: FormPublicFieldFragment
|
field: FormPublicFieldFragment
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const FieldInput: React.FC<FieldTypeProps> = fieldTypes[field.type] || TextType
|
const FieldInput = (fieldTypes[field.type] || fieldTypes[field.type]).inputFormField()
|
||||||
|
|
||||||
const finish = (data) => {
|
const finish = (data) => {
|
||||||
console.log('received field data', data)
|
console.log('received field data', data)
|
||||||
@ -81,7 +80,12 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
|
|||||||
<StyledMarkdown design={design} type={'question'}>{field.description}</StyledMarkdown>
|
<StyledMarkdown design={design} type={'question'}>{field.description}</StyledMarkdown>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FieldInput design={design} field={field} urlValue={getUrlDefault()} />
|
<FieldInput
|
||||||
|
design={design}
|
||||||
|
field={field}
|
||||||
|
focus={props.focus}
|
||||||
|
urlValue={getUrlDefault()}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -77,6 +77,7 @@ export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
<SwiperSlide key={field.id}>
|
<SwiperSlide key={field.id}>
|
||||||
<Field
|
<Field
|
||||||
field={field}
|
field={field}
|
||||||
|
focus={swiper?.activeIndex === (startPage.show ? 1 : 0) + i}
|
||||||
design={design}
|
design={design}
|
||||||
save={async (values: { [key: string]: unknown }) => {
|
save={async (values: { [key: string]: unknown }) => {
|
||||||
await setField(field.id, values[field.id])
|
await setField(field.id, values[field.id])
|
||||||
|
|||||||
38
components/form/types/abstract.type.tsx
Normal file
38
components/form/types/abstract.type.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React, { ComponentType } from 'react'
|
||||||
|
import { FieldAdminProps } from './field.admin.props'
|
||||||
|
import { FieldInputProps } from './field.input.props'
|
||||||
|
|
||||||
|
export abstract class AbstractType<A = any> {
|
||||||
|
public parseValue(raw: string): A {
|
||||||
|
return JSON.parse(raw) as A
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseUrlValue(raw: string): A {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
|
return raw as any
|
||||||
|
}
|
||||||
|
|
||||||
|
public stringifyValue(raw: string): string {
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
|
public displayValue(raw: string): JSX.Element {
|
||||||
|
const data = this.parseValue(raw)
|
||||||
|
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
{data.map(r => (
|
||||||
|
<li key={r}>{JSON.stringify(r)}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>{this.stringifyValue(raw)}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract adminFormField(): ComponentType<FieldAdminProps>
|
||||||
|
|
||||||
|
public abstract inputFormField(): ComponentType<FieldInputProps>
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Button, Col, Form, Input, Row } from 'antd'
|
import { Button, Col, Form, Input, Row } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const CheckboxType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const CheckboxAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,32 +2,40 @@ import { Checkbox, Form } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledCheckbox } from '../../styled/checkbox'
|
import { StyledCheckbox } from '../../../styled/checkbox'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/checkbox')
|
const logger = debug('checkbox.input')
|
||||||
|
|
||||||
export const CheckboxType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function CheckboxInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: string = undefined
|
let initialValue: string = undefined
|
||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={field.options
|
initialValue={field.options
|
||||||
.map((option) => option.value)
|
.map((option) => option.value)
|
||||||
@ -36,8 +44,13 @@ export const CheckboxType: React.FC<FieldTypeProps> = ({ field, design, urlValue
|
|||||||
<Checkbox.Group>
|
<Checkbox.Group>
|
||||||
{field.options
|
{field.options
|
||||||
.filter((option) => option.key === null)
|
.filter((option) => option.key === null)
|
||||||
.map((option) => (
|
.map((option, i) => (
|
||||||
<StyledCheckbox design={design} value={option.value} key={option.value}>
|
<StyledCheckbox
|
||||||
|
design={design}
|
||||||
|
value={option.value}
|
||||||
|
key={option.value}
|
||||||
|
autoFocus={i === 0 && focus}
|
||||||
|
>
|
||||||
{option.title || option.value}
|
{option.title || option.value}
|
||||||
</StyledCheckbox>
|
</StyledCheckbox>
|
||||||
))}
|
))}
|
||||||
15
components/form/types/checkbox/index.ts
Normal file
15
components/form/types/checkbox/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class CheckboxType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./checkbox.admin').then(c => c.CheckboxAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./checkbox.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,9 +2,9 @@ import { DatePicker, Form } from 'antd'
|
|||||||
import moment, { Moment } from 'moment'
|
import moment, { Moment } from 'moment'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const DateType: React.FC<AdminFieldTypeProps> = ({ field }) => {
|
export const DateAdmin: React.FC<FieldAdminProps> = ({ field }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -4,12 +4,20 @@ import debug from 'debug'
|
|||||||
import moment, { Moment } from 'moment'
|
import moment, { Moment } from 'moment'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledDateInput } from '../../styled/date.input'
|
import { StyledDateInput } from '../../../styled/date.input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/date')
|
const logger = debug('date.input')
|
||||||
|
|
||||||
export const DateType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function DateInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const [min, setMin] = useState<Dayjs>()
|
const [min, setMin] = useState<Dayjs>()
|
||||||
const [max, setMax] = useState<Dayjs>()
|
const [max, setMax] = useState<Dayjs>()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -29,20 +37,20 @@ export const DateType: React.FC<FieldTypeProps> = ({ field, design, urlValue, fo
|
|||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = moment(JSON.parse(field.defaultValue))
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = moment(urlValue)
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
||||||
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
||||||
@ -56,9 +64,11 @@ export const DateType: React.FC<FieldTypeProps> = ({ field, design, urlValue, fo
|
|||||||
if (min && min.isAfter(d.toDate())) {
|
if (min && min.isAfter(d.toDate())) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max && max.isBefore(d.toDate())) {
|
if (max && max.isBefore(d.toDate())) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
24
components/form/types/date/index.ts
Normal file
24
components/form/types/date/index.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import moment, { Moment } from 'moment'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class DateType extends AbstractType<Moment> {
|
||||||
|
parseValue(raw: string): Moment {
|
||||||
|
return moment(JSON.parse(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
parseUrlValue(raw: string): Moment {
|
||||||
|
return moment(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./date.admin').then(c => c.DateAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./date.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Button, Col, Form, Input, Row } from 'antd'
|
import { Button, Col, Form, Input, Row } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const DropdownType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const DropdownAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,12 +2,20 @@ import { Form, Select } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledSelect } from '../../styled/select'
|
import { StyledSelect } from '../../../styled/select'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/dropdown')
|
const logger = debug('field/dropdown')
|
||||||
|
|
||||||
export const DropdownType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function DateInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
@ -15,20 +23,20 @@ export const DropdownType: React.FC<FieldTypeProps> = ({ field, design, urlValue
|
|||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
15
components/form/types/dropdown/index.ts
Normal file
15
components/form/types/dropdown/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class DropdownType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./dropdown.admin').then(c => c.DropdownAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./dropdown.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, Input } from 'antd'
|
import { Form, Input } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const EmailType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const EmailAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,32 +2,40 @@ import { Form } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledInput } from '../../styled/input'
|
import { StyledInput } from '../../../styled/input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/email')
|
const logger = debug('email.input')
|
||||||
|
|
||||||
export const EmailType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function EmailInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue = null
|
let initialValue = null
|
||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[
|
rules={[
|
||||||
{ required: field.required, message: t('validation:valueRequired') },
|
{ required: field.required, message: t('validation:valueRequired') },
|
||||||
{ type: 'email', message: t('validation:invalidEmail') },
|
{ type: 'email', message: t('validation:invalidEmail') },
|
||||||
15
components/form/types/email/index.ts
Normal file
15
components/form/types/email/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class EmailType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./email.admin').then(c => c.EmailAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./email.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { FormInstance } from 'antd/lib/form'
|
import { FormInstance } from 'antd/lib/form'
|
||||||
import { FieldData } from 'rc-field-form/lib/interface'
|
import { FieldData } from 'rc-field-form/lib/interface'
|
||||||
|
|
||||||
export interface AdminFieldTypeProps {
|
export interface FieldAdminProps {
|
||||||
form: FormInstance
|
form: FormInstance
|
||||||
field: FieldData
|
field: FieldData
|
||||||
}
|
}
|
||||||
6
components/form/types/field.input.builder.type.ts
Normal file
6
components/form/types/field.input.builder.type.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { ComponentType } from 'react'
|
||||||
|
|
||||||
|
import { AbstractType } from './abstract.type'
|
||||||
|
import { FieldInputProps } from './field.input.props'
|
||||||
|
|
||||||
|
export type FieldInputBuilderType<A = AbstractType> = (type: A) => ComponentType<FieldInputProps>
|
||||||
@ -3,7 +3,7 @@ import {
|
|||||||
FormPublicFieldFragment,
|
FormPublicFieldFragment,
|
||||||
} from '../../../graphql/fragment/form.public.fragment'
|
} from '../../../graphql/fragment/form.public.fragment'
|
||||||
|
|
||||||
export interface FieldTypeProps {
|
export interface FieldInputProps {
|
||||||
field: FormPublicFieldFragment
|
field: FormPublicFieldFragment
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
focus?: boolean
|
focus?: boolean
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, Input } from 'antd'
|
import { Form, Input } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const HiddenType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const HiddenAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
15
components/form/types/hidden/index.ts
Normal file
15
components/form/types/hidden/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class HiddenType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./hidden.admin').then(c => c.HiddenAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
components/form/types/image/index.ts
Normal file
15
components/form/types/image/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class ImageType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./dropdown.admin').then(c => c.DropdownAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./dropdown.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,31 +1,34 @@
|
|||||||
import React from 'react'
|
import { AbstractType } from './abstract.type'
|
||||||
import { CheckboxType } from './checkbox.type'
|
import { CheckboxType } from './checkbox'
|
||||||
import { DateType } from './date.type'
|
import { DateType } from './date'
|
||||||
import { DropdownType } from './dropdown.type'
|
import { DropdownType } from './dropdown'
|
||||||
import { EmailType } from './email.type'
|
import { EmailType } from './email'
|
||||||
import { LinkType } from './link.type'
|
import { HiddenType } from './hidden'
|
||||||
import { NumberType } from './number.type'
|
import { LinkType } from './link'
|
||||||
import { RadioType } from './radio.type'
|
import { LocationType } from './location'
|
||||||
import { RatingType } from './rating.type'
|
import { NumberType } from './number'
|
||||||
import { SliderType } from './slider.type'
|
import { RadioType } from './radio'
|
||||||
import { TextType } from './text.type'
|
import { RatingType } from './rating'
|
||||||
import { TextareaType } from './textarea.type'
|
import { SliderType } from './slider'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { TextareaType } from './textarea'
|
||||||
import { YesNoType } from './yes_no.type'
|
import { TextfieldType } from './textfield'
|
||||||
|
import { YesNoType } from './yes_no'
|
||||||
|
|
||||||
export const fieldTypes: {
|
export const fieldTypes: {
|
||||||
[key: string]: React.FC<FieldTypeProps>
|
[key: string]: AbstractType
|
||||||
} = {
|
} = {
|
||||||
date: DateType,
|
checkbox: new CheckboxType(),
|
||||||
dropdown: DropdownType,
|
date: new DateType(),
|
||||||
checkbox: CheckboxType,
|
dropdown: new DropdownType(),
|
||||||
email: EmailType,
|
email: new EmailType(),
|
||||||
link: LinkType,
|
hidden: new HiddenType(),
|
||||||
number: NumberType,
|
link: new LinkType(),
|
||||||
radio: RadioType,
|
location: new LocationType(),
|
||||||
rating: RatingType,
|
number: new NumberType(),
|
||||||
slider: SliderType,
|
radio: new RadioType(),
|
||||||
textarea: TextareaType,
|
rating: new RatingType(),
|
||||||
textfield: TextType,
|
slider: new SliderType(),
|
||||||
yes_no: YesNoType,
|
textarea: new TextareaType(),
|
||||||
|
textfield: new TextfieldType(),
|
||||||
|
yes_no: new YesNoType(),
|
||||||
}
|
}
|
||||||
|
|||||||
15
components/form/types/link/index.ts
Normal file
15
components/form/types/link/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class LinkType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./link.admin').then(c => c.LinkAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./link.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, Input } from 'antd'
|
import { Form, Input } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const LinkType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const LinkAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,32 +2,40 @@ import { Form } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledInput } from '../../styled/input'
|
import { StyledInput } from '../../../styled/input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/link')
|
const logger = debug('link.input')
|
||||||
|
|
||||||
export const LinkType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function LinkInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue = null
|
let initialValue = null
|
||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[
|
rules={[
|
||||||
{ required: field.required, message: t('validation:valueRequired') },
|
{ required: field.required, message: t('validation:valueRequired') },
|
||||||
{ type: 'url', message: t('validation:invalidUrl') },
|
{ type: 'url', message: t('validation:invalidUrl') },
|
||||||
34
components/form/types/location/index.ts
Normal file
34
components/form/types/location/index.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class LocationType extends AbstractType<{ lat: number, lng: number }> {
|
||||||
|
parseUrlValue(raw: string): { lat: number; lng: number } {
|
||||||
|
if (raw.includes(',')) {
|
||||||
|
const [lat, lng] = raw.split(',')
|
||||||
|
|
||||||
|
return {
|
||||||
|
lat: parseFloat(lat),
|
||||||
|
lng: parseFloat(lng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('no separator found')
|
||||||
|
}
|
||||||
|
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./location.admin').then(c => c.LocationAdmin), { ssr: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./location.input').then(c => c.builder(this)), { ssr: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
stringifyValue(raw: string): string {
|
||||||
|
const data = this.parseValue(raw)
|
||||||
|
|
||||||
|
return `${data.lat}, ${data.lng}`
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,9 +3,9 @@ import React from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { MapContainer, TileLayer } from 'react-leaflet'
|
import { MapContainer, TileLayer } from 'react-leaflet'
|
||||||
import { DraggableMarker } from '../../../map/draggable.marker'
|
import { DraggableMarker } from '../../../map/draggable.marker'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const LocationType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const LocationAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -105,7 +105,7 @@ export const LocationType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
url={tiles}
|
url={tiles}
|
||||||
/>
|
/>
|
||||||
{center.lat && center.lng && (
|
{center?.lat && center?.lng && (
|
||||||
<DraggableMarker
|
<DraggableMarker
|
||||||
value={center}
|
value={center}
|
||||||
onChange={next => {
|
onChange={next => {
|
||||||
153
components/form/types/location/location.input.tsx
Normal file
153
components/form/types/location/location.input.tsx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { Alert, Form, InputNumber, Space, Spin } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { MapContainer, TileLayer } from 'react-leaflet'
|
||||||
|
import { DraggableMarker } from '../../../map/draggable.marker'
|
||||||
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
|
const logger = debug('location.number')
|
||||||
|
|
||||||
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function LocationInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
|
const [initialZoom, setInitialZoom] = useState<number>(13)
|
||||||
|
const [tiles, setTiles] = useState<string>()
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
field.options.forEach((option) => {
|
||||||
|
if (option.key === 'initialZoom') {
|
||||||
|
try {
|
||||||
|
setInitialZoom(JSON.parse(option.value))
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid initialZoom value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.key === 'tiles') {
|
||||||
|
try {
|
||||||
|
setTiles(JSON.parse(option.value))
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid tiles value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
}, [field])
|
||||||
|
|
||||||
|
let initialValue: { lat: number, lng: number } = undefined
|
||||||
|
|
||||||
|
if (field.defaultValue) {
|
||||||
|
try {
|
||||||
|
initialValue = parseValue(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlValue) {
|
||||||
|
try {
|
||||||
|
initialValue = parseUrlValue(urlValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid url value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Spin />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tiles) {
|
||||||
|
return <Alert message={'Tiles missing!'} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form.Item>
|
||||||
|
<Space>
|
||||||
|
<Form.Item
|
||||||
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
|
name={[
|
||||||
|
field.id,
|
||||||
|
'lat',
|
||||||
|
]}
|
||||||
|
initialValue={initialValue?.lat}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<InputNumber addonAfter={'LAT'} precision={7} step={0.00001} max={90} min={-90} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
|
name={[
|
||||||
|
field.id,
|
||||||
|
'lng',
|
||||||
|
]}
|
||||||
|
initialValue={initialValue?.lng}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<InputNumber addonAfter={'LNG'} precision={7} step={0.00001} max={180} min={-180} />
|
||||||
|
</Form.Item>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item dependencies={[[field.id, 'lat'], [field.id, 'lng']]}>
|
||||||
|
{(form) => {
|
||||||
|
const center = form.getFieldValue([field.id])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<MapContainer
|
||||||
|
center={initialValue}
|
||||||
|
zoom={initialZoom}
|
||||||
|
style={{ height: 300, width: '100%' }}
|
||||||
|
>
|
||||||
|
<TileLayer
|
||||||
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
url={tiles}
|
||||||
|
/>
|
||||||
|
{center.lat && center.lng && (
|
||||||
|
<DraggableMarker
|
||||||
|
value={center}
|
||||||
|
onChange={next => {
|
||||||
|
form.setFields([
|
||||||
|
{
|
||||||
|
name: [
|
||||||
|
field.id,
|
||||||
|
'lng',
|
||||||
|
],
|
||||||
|
value: next.lng,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: [
|
||||||
|
field.id,
|
||||||
|
'lat',
|
||||||
|
],
|
||||||
|
value: next.lat,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MapContainer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
19
components/form/types/number/index.ts
Normal file
19
components/form/types/number/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class NumberType extends AbstractType<number> {
|
||||||
|
parseUrlValue(raw: string): number {
|
||||||
|
return parseFloat(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./number.admin').then(c => c.NumberAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./number.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, InputNumber } from 'antd'
|
import { Form, InputNumber } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const NumberType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const NumberAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,32 +2,40 @@ import { Form } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledNumberInput } from '../../styled/number.input'
|
import { StyledNumberInput } from '../../../styled/number.input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/number')
|
const logger = debug('number.input')
|
||||||
|
|
||||||
export const NumberType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function NumberInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: number = undefined
|
let initialValue: number = undefined
|
||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = parseFloat(urlValue)
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[
|
rules={[
|
||||||
{ type: 'number', message: t('validation:invalidNumber') },
|
{ type: 'number', message: t('validation:invalidNumber') },
|
||||||
{ required: field.required, message: t('validation:valueRequired') },
|
{ required: field.required, message: t('validation:valueRequired') },
|
||||||
15
components/form/types/radio/index.ts
Normal file
15
components/form/types/radio/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class RadioType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./radio.admin').then(c => c.RadioAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./radio.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Button, Col, Form, Input, Row } from 'antd'
|
import { Button, Col, Form, Input, Row } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const RadioType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const RadioAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,32 +2,40 @@ import { Form, Radio } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledRadio } from '../../styled/radio'
|
import { StyledRadio } from '../../../styled/radio'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/radio')
|
const logger = debug('radio.input')
|
||||||
|
|
||||||
export const RadioType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function RadioInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: string = undefined
|
let initialValue: string = undefined
|
||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={field.options
|
initialValue={field.options
|
||||||
.map((option) => option.value)
|
.map((option) => option.value)
|
||||||
19
components/form/types/rating/index.ts
Normal file
19
components/form/types/rating/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class RatingType extends AbstractType<number> {
|
||||||
|
parseUrlValue(raw: string): number {
|
||||||
|
return parseFloat(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./rating.admin').then(c => c.RatingAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./rating.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, Rate } from 'antd'
|
import { Form, Rate } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const RatingType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const RatingAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,31 +2,39 @@ import { Form, Rate } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/rating')
|
const logger = debug('rating.input')
|
||||||
|
|
||||||
export const RatingType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function RatingInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: number = undefined
|
let initialValue: number = undefined
|
||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = parseFloat(urlValue)
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
19
components/form/types/slider/index.ts
Normal file
19
components/form/types/slider/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class SliderType extends AbstractType<number> {
|
||||||
|
parseUrlValue(raw: string): number {
|
||||||
|
return parseFloat(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./slider.admin').then(c => c.SliderAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./slider.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, InputNumber, Slider } from 'antd'
|
import { Form, InputNumber, Slider } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const SliderAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,11 +2,19 @@ import { Form, Slider, Spin } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/slider')
|
const logger = debug('slider.input')
|
||||||
|
|
||||||
export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function SliderInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const [min, setMin] = useState<number>()
|
const [min, setMin] = useState<number>()
|
||||||
const [max, setMax] = useState<number>()
|
const [max, setMax] = useState<number>()
|
||||||
const [step, setStep] = useState<number>()
|
const [step, setStep] = useState<number>()
|
||||||
@ -46,14 +54,14 @@ export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
|||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = parseFloat(urlValue)
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@ -67,11 +75,12 @@ export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
<Slider
|
<Slider
|
||||||
|
autoFocus={focus}
|
||||||
min={min}
|
min={min}
|
||||||
max={max}
|
max={max}
|
||||||
step={step}
|
step={step}
|
||||||
15
components/form/types/textarea/index.ts
Normal file
15
components/form/types/textarea/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class TextareaType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./textarea.admin').then(c => c.TextareaAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./textarea.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, Input } from 'antd'
|
import { Form, Input } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const TextareaType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const TextareaAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,32 +2,40 @@ import { Form } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledTextareaInput } from '../../styled/textarea.input'
|
import { StyledTextareaInput } from '../../../styled/textarea.input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/textarea')
|
const logger = debug('textarea.input')
|
||||||
|
|
||||||
export const TextareaType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function TextareaInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue = undefined
|
let initialValue = undefined
|
||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
15
components/form/types/textfield/index.ts
Normal file
15
components/form/types/textfield/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class TextfieldType extends AbstractType<string> {
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./textfield.admin').then(c => c.TextfieldAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./textfield.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, Input } from 'antd'
|
import { Form, Input } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const TextType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const TextfieldAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,12 +2,20 @@ import { Form } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledInput } from '../../styled/input'
|
import { StyledInput } from '../../../styled/input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/text')
|
const logger = debug('textfield.input')
|
||||||
|
|
||||||
export const TextType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function TextfieldInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
// TODO focus when becomes visible
|
// TODO focus when becomes visible
|
||||||
|
|
||||||
@ -15,24 +23,29 @@ export const TextType: React.FC<FieldTypeProps> = ({ field, design, urlValue, fo
|
|||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
<StyledInput autoFocus={focus} design={design} allowClear size={'large'} />
|
<StyledInput
|
||||||
|
autoFocus={focus}
|
||||||
|
design={design}
|
||||||
|
allowClear
|
||||||
|
size={'large'}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
36
components/form/types/yes_no/index.tsx
Normal file
36
components/form/types/yes_no/index.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Tag } from 'antd'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import React, { ComponentType } from 'react'
|
||||||
|
import { AbstractType } from '../abstract.type'
|
||||||
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
import { FieldInputProps } from '../field.input.props'
|
||||||
|
|
||||||
|
export class YesNoType extends AbstractType<boolean> {
|
||||||
|
parseUrlValue(raw: string): boolean {
|
||||||
|
return !!raw
|
||||||
|
}
|
||||||
|
|
||||||
|
adminFormField(): ComponentType<FieldAdminProps> {
|
||||||
|
return dynamic(() => import('./yes_no.admin').then(c => c.YesNoAdmin));
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFormField(): ComponentType<FieldInputProps> {
|
||||||
|
return dynamic(() => import('./yes_no.input').then(c => c.builder(this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
stringifyValue(raw: string): string {
|
||||||
|
if (this.parseValue(raw)) {
|
||||||
|
return 'YES'
|
||||||
|
} else {
|
||||||
|
return 'NO'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayValue(raw: string): JSX.Element {
|
||||||
|
if (this.parseValue(raw)) {
|
||||||
|
return <Tag color={'green'}>YES</Tag>
|
||||||
|
} else {
|
||||||
|
return <Tag color={'red'}>NO</Tag>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { Form, Switch } from 'antd'
|
import { Form, Switch } from 'antd'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { AdminFieldTypeProps } from './type.props'
|
import { FieldAdminProps } from '../field.admin.props'
|
||||||
|
|
||||||
export const YesNoType: React.FC<AdminFieldTypeProps> = (props) => {
|
export const YesNoAdmin: React.FC<FieldAdminProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -2,11 +2,19 @@ import { Form, Switch } from 'antd'
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldInputBuilderType } from '../field.input.builder.type'
|
||||||
|
|
||||||
const logger = debug('field/link')
|
const logger = debug('yes_no.input')
|
||||||
|
|
||||||
export const YesNoType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
export const builder: FieldInputBuilderType = ({
|
||||||
|
parseUrlValue,
|
||||||
|
parseValue,
|
||||||
|
}) => function YesNoInput ({
|
||||||
|
field,
|
||||||
|
design,
|
||||||
|
urlValue,
|
||||||
|
focus,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|
||||||
@ -14,20 +22,20 @@ export const YesNoType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
|||||||
|
|
||||||
if (field.defaultValue) {
|
if (field.defaultValue) {
|
||||||
try {
|
try {
|
||||||
initialValue = JSON.parse(field.defaultValue)
|
initialValue = parseValue(field.defaultValue)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger('invalid default value %O', e)
|
logger('invalid default value %O', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue !== undefined) {
|
if (urlValue !== undefined) {
|
||||||
initialValue = !!urlValue
|
initialValue = parseUrlValue(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id]}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
valuePropName={'checked'}
|
valuePropName={'checked'}
|
||||||
@ -1,6 +1,11 @@
|
|||||||
import { useMutation } from '@apollo/client'
|
import { useMutation } from '@apollo/client'
|
||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
import {
|
||||||
|
SUBMISSION_FINISH_MUTATION,
|
||||||
|
SubmissionFinishMutationData,
|
||||||
|
SubmissionFinishMutationVariables,
|
||||||
|
} from '../graphql/mutation/submission.finish.mutation'
|
||||||
import {
|
import {
|
||||||
SUBMISSION_SET_FIELD_MUTATION,
|
SUBMISSION_SET_FIELD_MUTATION,
|
||||||
SubmissionSetFieldMutationData,
|
SubmissionSetFieldMutationData,
|
||||||
@ -11,11 +16,6 @@ import {
|
|||||||
SubmissionStartMutationData,
|
SubmissionStartMutationData,
|
||||||
SubmissionStartMutationVariables,
|
SubmissionStartMutationVariables,
|
||||||
} from '../graphql/mutation/submission.start.mutation'
|
} from '../graphql/mutation/submission.start.mutation'
|
||||||
import {
|
|
||||||
SUBMISSION_FINISH_MUTATION,
|
|
||||||
SubmissionFinishMutationData,
|
|
||||||
SubmissionFinishMutationVariables,
|
|
||||||
} from '../graphql/mutation/submission.finish.mutation'
|
|
||||||
|
|
||||||
const logger = debug('useSubmission')
|
const logger = debug('useSubmission')
|
||||||
|
|
||||||
@ -65,8 +65,12 @@ export const useSubmission = (formId: string): Submission => {
|
|||||||
}, [formId])
|
}, [formId])
|
||||||
|
|
||||||
const setField = useCallback(
|
const setField = useCallback(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
async (fieldId: string, data: unknown) => {
|
||||||
async (fieldId: string, data: any) => {
|
if (data === undefined || data === null) {
|
||||||
|
logger('skip save field id=%O %O', fieldId, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger('save field id=%O %O', fieldId, data)
|
logger('save field id=%O %O', fieldId, data)
|
||||||
await save({
|
await save({
|
||||||
variables: {
|
variables: {
|
||||||
|
|||||||
@ -82,7 +82,6 @@ const Create: NextPage = () => {
|
|||||||
noStyle
|
noStyle
|
||||||
name={[
|
name={[
|
||||||
'form',
|
'form',
|
||||||
'design',
|
|
||||||
'layout',
|
'layout',
|
||||||
]}
|
]}
|
||||||
initialValue={'card'}
|
initialValue={'card'}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user