Skip to content

Commit

Permalink
Merge branch 'feature/location-search-field' into staging
Browse files Browse the repository at this point in the history
  • Loading branch information
meneman committed Sep 14, 2023
2 parents 9503b98 + 21d5f1b commit d5b6b03
Show file tree
Hide file tree
Showing 27 changed files with 5,680 additions and 3,420 deletions.
14 changes: 2 additions & 12 deletions apps/cra/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
import {
WktLiteralTextControlTester,
WktLiteralTextControlRenderer,
LocationToolSettings,
} from '@formswizard/experimental-renderers'
import { locationTools } from '@formswizard/experimental-renderers'
import { WizardProvider, MainLayout } from '@formswizard/forms-designer'

const renderers = [
{
tester: WktLiteralTextControlTester,
renderer: WktLiteralTextControlRenderer,
},
]
export const App = () => {
return (
<WizardProvider>
<MainLayout additionalToolSettings={[LocationToolSettings]} renderers={renderers} />
<MainLayout additionalToolSettings={locationTools.toolSettings} renderers={locationTools.rendererRegistry} />
</WizardProvider>
)
}
4 changes: 3 additions & 1 deletion apps/web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import { useWizard } from '@formswizard/forms-designer'
import { Container } from '@mui/material'
import { useEffect } from 'react'
import { WizardMainLayout } from './WizardMainLayoutNoSSR'

import { WizardMainLayout } from './WizardMainLayout'


const SomeComponentUsingTheWizardState = () => {
const { jsonSchema, uiSchema, getState } = useWizard()
Expand Down
9 changes: 6 additions & 3 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"serve": "serve -s ./out",
"lint": "next lint"
},
"dependencies": {
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
"@emotion/server": "^11.11.0",
"@formswizard/experimental-renderers_next": "workspace:*",
"@formswizard/forms-designer": "workspace:*",
"@formswizard/state": "workspace:*",
"@jsonforms/material-renderers": "^3.1.0",
"@jsonforms/react": "^3.1.0",
"@mui/icons-material": "^5.14.1",
Expand All @@ -21,8 +25,6 @@
"@reduxjs/toolkit": "^1.9.5",
"ajv": "^8.12.0",
"dayjs": "^1.11.9",
"@formswizard/forms-designer": "workspace:*",
"@formswizard/state": "workspace:*",
"@formswizard/experimental-renderers": "workspace:*",
"next": "~13.4.20-canary.6",
"next-redux-wrapper": "^8.1.0",
Expand All @@ -34,11 +36,12 @@
"react-redux": "^8.1.1"
},
"devDependencies": {
"@formswizard/designer-tsconfig": "workspace:*",
"@types/node": "^17.0.12",
"@types/react": "^18.2.17",
"@types/react-dom": "^18.2.0",
"eslint-config-formsdesigner": "workspace:*",
"@formswizard/designer-tsconfig": "workspace:*",
"serve": "^14.2.1",
"typescript": "^4.5.3"
}
}
94 changes: 46 additions & 48 deletions packages/experimental-renderers/LocationSearchCombined.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,63 @@ import 'leaflet/dist/leaflet.css'

import * as L from 'leaflet'
import * as React from 'react'
import {useCallback, useState} from 'react'
import { useCallback, useState } from 'react'

import {LocationSearchField} from './LocationSearchField'
import {InputAdornment, IconButton, TextField, Grid} from "@mui/material";
import * as Icons from "@mui/icons-material";
import {NominatimResponse} from "./nominatim";
import {LocationSearchMap, LocationSearchMapProps} from "./LocationSearchMap";
import {NoSSR} from "next/dist/shared/lib/lazy-dynamic/dynamic-no-ssr";
import { LocationSearchField } from './LocationSearchField'
import { InputAdornment, IconButton, TextField, Grid } from '@mui/material'
import * as Icons from '@mui/icons-material'
import { NominatimResponse } from './nominatim'
import { LocationSearchMap } from './LocationSearchMap'
import { NoSSR } from 'next/dist/shared/lib/lazy-dynamic/dynamic-no-ssr'
import { LocationSearchMapProps } from './types'

export function LocationSearchCombined(props: LocationSearchMapProps ) {
const {
markerPosition,
onChangeMarkerPosition,
readonly,
label
} = props
export function LocationSearchCombined(props: LocationSearchMapProps) {
const { markerPosition, onChangeMarkerPosition, readonly, label } = props

const showMarker = true
const [showMap, setShowMap] = useState(false);
const [showMap, setShowMap] = useState(false)
const updateLocation = useCallback(
(lat: number, lng: number, result?: NominatimResponse) => {
onChangeMarkerPosition && onChangeMarkerPosition(lat, lng, result)
},
[onChangeMarkerPosition]
(lat: number, lng: number, result?: NominatimResponse) => {
onChangeMarkerPosition && onChangeMarkerPosition(lat, lng, result)
},
[onChangeMarkerPosition]
)


//const LocationSearchMap = useMemo( () => dynamic(() => import('./LocationSearchMap').then((mod) => mod.LocationSearchMap), { ssr: false }) , [])
return (<Grid container direction={'column'}>
return (
<Grid container direction={'column'}>
<Grid item>
<LocationSearchField
readOnly={readonly}
onLocationFound={updateLocation}
renderInput={(params) => (
<TextField
{...params}
label={label || 'Address'}
InputProps={{
...params.InputProps,
endAdornment: (
<>
{params.InputProps.endAdornment || null}
<InputAdornment position="end">
<IconButton aria-label="toggle map visibility" onClick={() => setShowMap(!showMap)} edge="end">
{showMap ? <Icons.MapRounded /> : <Icons.MapSharp />}
</IconButton>
</InputAdornment>
</>
),
}}
/>
)}
/>
</Grid>
{showMap && (
<Grid item>
<LocationSearchField
readOnly={readonly}
onLocationFound={updateLocation}
renderInput={(params) => <TextField
{...params}
label={label || 'Address'}
InputProps={{
...params.InputProps,
endAdornment: <>
{params.InputProps.endAdornment || null}
<InputAdornment position="end">
<IconButton
aria-label="toggle map visibility"
onClick={() => setShowMap(!showMap)}
edge="end"
>
{showMap ? <Icons.MapRounded/> : <Icons.MapSharp/>}
</IconButton>
</InputAdornment>
</>
}}
/>}/>
</Grid>
{showMap && <Grid item>
<NoSSR>
<LocationSearchMap {...props} />
</NoSSR>
</Grid>}
</Grid>
</Grid>
)}
</Grid>
)
}

9 changes: 1 addition & 8 deletions packages/experimental-renderers/LocationSearchMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@ import 'leaflet'
import 'leaflet/dist/leaflet.css'

import * as L from 'leaflet'

import { useEffect, useMemo } from 'react'
import { LayersControl, Marker, Popup, TileLayer, MapContainer, useMapEvents } from 'react-leaflet'

import { NominatimResponse } from './nominatim'

export interface LocationSearchMapProps {
markerPosition?: L.LatLngExpression
onChangeMarkerPosition?: (lat: number, lng: number, result?: NominatimResponse) => void
readonly?: boolean
label?: string
}
import { LocationSearchMapProps } from './types'

type LocationSearchMapContentProps = LocationSearchMapProps & {
color: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,7 @@ import { pathToPathSegments, pathSegmentsToPath, splitLastPath, filterNullOrUnde
import { NominatimResponse } from './nominatim'
import { LocationSearchCombined } from './LocationSearchCombined'

/**
* parses a WKT string into a lat/lng object
*
* @param wkt
* @returns {{lat: number, lng: number}|undefined} undefined if parsing failed
*/
const wktToLatLng = (wkt: string): { lat: number; lng: number } | undefined => {
const testerRegEx = /^POINT\s*\(([0-9\.]+)\s+([0-9\.]+)\)$/
const match = wkt.match(testerRegEx)
if (match) {
const [, lngS, latS] = match,
lat = parseFloat(latS),
lng = parseFloat(lngS)
if (!isNaN(lat) && !isNaN(lng)) return { lat, lng }
}
return undefined
}
export const WktLiteralInputControl = (props: ControlProps) => {
const { uischema, handleChange, path, data } = props

const position = useMemo(() => {
if (data) {
const parsed = wktToLatLng(data)
if (parsed) return parsed
}
}, [data]) || { lat: 51.0833, lng: 13.73126 }

const handleLocationFound = useCallback(
(lat: number, lng: number, result?: NominatimResponse) => {
const [first, rest] = splitLastPath(path)
// @ts-ignore
const buildPath = (key: string) =>
pathSegmentsToPath([
...filterNullOrUndef<string>(pathToPathSegments(rest || '')).filter((p) => p.length > 0),
key,
])
handleChange(props.path, `POINT(${lng} ${lat})`)
if (!result) return
if (uischema.options?.mapNominatimFields) {
if ((result as any).name) {
const path = buildPath('name')
// @ts-ignore
handleChange(path, result.name)
}
}
},
[path, handleChange, uischema]
)
export const LocationSearchTextFieldRenderer = (props: ControlProps) => {
return (
<LocationSearchCombined
readonly={props.enabled === false}
Expand All @@ -64,4 +17,4 @@ export const WktLiteralInputControl = (props: ControlProps) => {
}

export const WktLiteralTextControlTester: RankedTester = rankWith(10, and(isStringControl, formatIs('wktLiteral')))
export const WktLiteralTextControlRenderer = withJsonFormsControlProps(WktLiteralInputControl)
export const LocationSearchTextControlRenderer = withJsonFormsControlProps(LocationSearchTextFieldRenderer)
52 changes: 52 additions & 0 deletions packages/experimental-renderers/WktLiteralInputControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useCallback, useMemo } from 'react'
import { ControlProps } from '@jsonforms/core'
import { pathToPathSegments, pathSegmentsToPath, splitLastPath, filterNullOrUndef } from '@formswizard/utils'
import { NominatimResponse } from './nominatim'
import { LocationSearchCombined } from './LocationSearchCombined'
import { wktToLatLng } from './wktToLantLng'
import { LocationSearchMapProps } from './types'

type WktLiteralInputControlProps = ControlProps & {
mapElement?: (props: LocationSearchMapProps) => React.ReactNode
}
export const WktLiteralInputControl = (props: WktLiteralInputControlProps) => {
const { uischema, handleChange, path, data, mapElement } = props

const position = useMemo(() => {
if (data) {
const parsed = wktToLatLng(data)
if (parsed) return parsed
}
}, [data]) || { lat: 51.0833, lng: 13.73126 }

const handleLocationFound = useCallback(
(lat: number, lng: number, result?: NominatimResponse) => {
const [first, rest] = splitLastPath(path)
// @ts-ignore
const buildPath = (key: string) =>
pathSegmentsToPath([
...filterNullOrUndef<string>(pathToPathSegments(rest || '')).filter((p) => p.length > 0),
key,
])
handleChange(props.path, `POINT(${lng} ${lat})`)
if (!result) return
if (uischema.options?.mapNominatimFields) {
if ((result as any).name) {
const path = buildPath('name')
// @ts-ignore
handleChange(path, result.name)
}
}
},
[path, handleChange, uischema]
)
return (
<LocationSearchCombined
readonly={props.enabled === false}
label={data || props.label}
markerPosition={position}
onChangeMarkerPosition={handleLocationFound}
mapElement={mapElement}
/>
)
}
6 changes: 6 additions & 0 deletions packages/experimental-renderers/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export * from './LocationSearchField'
export * from './LocationSearchTextFieldRenderer'
export * from './LocationSearchMap'
export * from './LocationToolSettings'
export * from './locationTools'

export * from './WktLiteralInputControl'
export * from './LocationToolSettings'
export * from './locationTools'
export * from './wktToLantLng'
export * from './types'
12 changes: 12 additions & 0 deletions packages/experimental-renderers/locationToolElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { DraggableElement } from '@formswizard/types'

export const locationToolElements: DraggableElement[] = [
{
name: 'Location',
ToolIconName: 'LocationOn',
jsonSchemaElement: {
type: 'string',
format: 'wktLiteral',
},
},
]
21 changes: 11 additions & 10 deletions packages/experimental-renderers/locationTools.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import {DraggableElement, PluggableToolDefinition} from "@formswizard/types";
import {WktLiteralTextControlRenderer, WktLiteralTextControlTester} from "./LocationSearchTextFieldRenderer";
import {LocationToolSettings} from "./LocationToolSettings";
import { DraggableElement, PluggableToolDefinition } from '@formswizard/types'
import { LocationSearchTextControlRenderer, WktLiteralTextControlTester } from './LocationSearchTextFieldRenderer'
import { LocationToolSettings } from './LocationToolSettings'

export const locationToolElements: DraggableElement[] = [
{
name: 'Location',
ToolIconName: 'LocationOn',
jsonSchemaElement: {
type: 'string',
format: 'wktLiteral'
}
}]

format: 'wktLiteral',
},
},
]

export const locationTools: PluggableToolDefinition = {
dropRendererRegistry: [],
rendererRegistry: [
{
tester: WktLiteralTextControlTester,
renderer: WktLiteralTextControlRenderer
}
// renderer: WktLiteralTextControlRenderer,
renderer: LocationSearchTextControlRenderer,
},
],
toolSettings: [LocationToolSettings],
toolBoxElements: locationToolElements
toolBoxElements: locationToolElements,
}
2 changes: 0 additions & 2 deletions packages/experimental-renderers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,10 @@
"react-redux": "^8.1.1"
},
"dependencies": {
"@formswizard/designer-basic-renderer": "workspace",
"@formswizard/fieldsettings": "workspace:*",
"@formswizard/react-hooks": "workspace:*",
"@formswizard/state": "workspace:*",
"@formswizard/utils": "workspace:*",
"@icon/icofont": "1.0.1-alpha.1",
"ajv": "^8.12.0",
"classnames": "^2.3.2",
"leaflet": "^1.9.4",
Expand Down
Loading

0 comments on commit d5b6b03

Please sign in to comment.