react-mapfilter
Version:
These components are designed for viewing data in Mapeo. They share a common interface:
255 lines (235 loc) • 6.13 kB
JavaScript
// @flow
import * as valueTypes from '../../constants/value_types'
import { guessValueType } from './value_types'
import { flatObjectEntries } from '../../utils/flat_object_entries'
import { get } from '../../utils/get_set'
import type {
JSONObject,
FieldStatistic,
Statistics,
MediaArray,
Field,
TextField,
LinkField,
NumberField,
DateField,
DateTimeField,
SelectOptions,
SelectOneField,
SelectMultipleField
} from '../../types'
export { default as createMemoizedStats } from './statistics'
const mediaTypes = [
valueTypes.VIDEO_URL,
valueTypes.AUDIO_URL,
valueTypes.IMAGE_URL
]
function compareKeys(a, b) {
return JSON.stringify(a[0]).localeCompare(JSON.stringify(b[0]))
}
/**
* Takes a JSON object and optional array of JSON objects and returns an array
* of field definitions. The field definitions are guessed from the arguments
* passed. The returned field definitions can be used to render fields for
* editing the properties of the object.
*/
export function getFields(
cur?: JSONObject = {},
stats?: Statistics
): Array<Field> {
const entries = stats ? flatStatsEntries(stats) : flatObjectEntries(cur)
return entries.sort(compareKeys).reduce((acc, [keyArray]) => {
const key = JSON.stringify(keyArray)
const value = get(cur, keyArray)
const fieldStats = stats && stats[key]
const field = getField(keyArray, value, fieldStats)
if (field) acc.push(field)
return acc
}, [])
}
export function getMedia(cur: JSONObject = {}): MediaArray {
return flatObjectEntries(cur).reduce((acc, [keyArray, value]) => {
const type = guessValueType(value)
if (mediaTypes.includes(type)) {
// $FlowFixMe flow does not understand type is only media type here
acc.push({ src: value, type })
}
return acc
}, [])
}
export function getField(
keyArray: Array<string | number>,
value: any,
fieldStats?: FieldStatistic
): Field {
const valueType = guessValueType(value)
// Initial implementation does not try to guess from statistics
switch (valueType) {
case valueTypes.BOOLEAN:
return createSelectOneField(keyArray, [true, false])
case valueTypes.STRING: {
const options = getOptions(fieldStats)
if (options.length) return createSelectOneField(keyArray, options)
else return createTextField(keyArray)
}
case valueTypes.NUMBER:
return createNumberField(keyArray)
case valueTypes.ARRAY: {
const options = getOptions(fieldStats)
return createSelectMultipleField(
keyArray,
options.length ? options : value,
{ readonly: true }
)
}
case valueTypes.DATE:
return createDateField(keyArray, {
min: fieldStats && fieldStats.date.min,
max: fieldStats && fieldStats.date.max
})
case valueTypes.DATETIME:
return createDateTimeField(keyArray, {
min: fieldStats && fieldStats.date.min,
max: fieldStats && fieldStats.date.max
})
case valueTypes.URL:
case valueTypes.IMAGE_URL:
case valueTypes.AUDIO_URL:
case valueTypes.VIDEO_URL:
return createLinkField(keyArray)
default:
return createTextField(keyArray, { readonly: true })
}
}
// Don't include long strings in the options that can be selected
const MAX_OPTION_LENGTH = 30
function getOptions(fieldStats?: FieldStatistic): SelectOptions {
const options = []
if (!fieldStats) return options
for (const value of fieldStats.string.values.keys()) {
if (value.length > MAX_OPTION_LENGTH) continue
options.push(value)
}
return options
}
function createTextField(
keyArray: Array<string | number>,
{
readonly = false,
appearance = 'single',
snakeCase = false
}: {
readonly?: boolean,
appearance?: 'single' | 'multiline',
snakeCase?: boolean
} = {}
): TextField {
return {
id: JSON.stringify([...arguments]),
key: keyArray,
readonly: readonly,
appearance: appearance,
type: 'text'
}
}
function createLinkField(keyArray: Array<string | number>): LinkField {
return {
id: JSON.stringify([...arguments]),
key: keyArray,
readonly: true,
type: 'link'
}
}
function createNumberField(
keyArray: Array<string | number>,
{ readonly = false }: { readonly?: boolean } = {}
): NumberField {
return {
id: JSON.stringify([...arguments]),
key: keyArray,
readonly: readonly,
type: 'number'
}
}
function createSelectOneField(
keyArray: Array<string | number>,
options: SelectOptions,
{
readonly = false,
snakeCase = false
}: { readonly?: boolean, snakeCase?: boolean } = {}
): SelectOneField {
return {
id: JSON.stringify([...arguments]),
key: keyArray,
readonly: readonly,
type: 'select_one',
options: options
}
}
function createSelectMultipleField(
keyArray: Array<string | number>,
options: SelectOptions,
{
readonly = false,
snakeCase = false
}: { readonly?: boolean, snakeCase?: boolean } = {}
): SelectMultipleField {
return {
id: JSON.stringify([...arguments]),
key: keyArray,
readonly: readonly,
type: 'select_multiple',
options: options
}
}
function createDateField(
keyArray: Array<string | number>,
{
readonly = false,
min,
max
}: {
readonly?: boolean,
snakeCase?: boolean,
min?: string,
max?: string
} = {}
): DateField {
return {
id: JSON.stringify([...arguments]),
key: keyArray,
readonly: readonly,
type: 'date',
min_value: min,
max_value: max
}
}
function createDateTimeField(
keyArray: Array<string | number>,
{
readonly = false,
min,
max
}: {
readonly?: boolean,
snakeCase?: boolean,
min?: string,
max?: string
} = {}
): DateTimeField {
return {
id: JSON.stringify([...arguments]),
key: keyArray,
readonly: readonly,
type: 'datetime',
min_value: min,
max_value: max
}
}
function flatStatsEntries(
stats: Statistics
): Array<[Array<string | number>, FieldStatistic]> {
// $FlowFixMe
return Object.entries(stats).map(([key, value]) => [JSON.parse(key), value])
}