Skip to content

Commit

Permalink
[form-builder] Refactor gradientPatchAdapter (#589)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge authored Feb 14, 2018
1 parent b4fbcf9 commit cc039fe
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 124 deletions.
Original file line number Diff line number Diff line change
@@ -1,56 +1,39 @@
/**
* An example of how to sync focus path through document.location.hash
*
*/
*/

// @flow
import React from 'react'
import {arrayToJSONMatchPath} from '../../../../mutator/lib/index'
import type {Path, PathSegment} from '../../typedefs/path'
import type {Path} from '../../typedefs/path'
import {toFormBuilder, toGradient} from '../utils/convertPath'

type ChildArgs = {
onFocus: (path: Path) => void,
onBlur: () => void,
focusPath: Path
}

type Props = {
focusPath: ?any,
onFocus: () => {},
onBlur: () => {},
children: () => any
children: (
ChildArgs
) => any
}

type State = {
focusPath: Array<*>
}
const IS_NUMERIC = /^\d+$/

function unquote(str) {
return str.replace(/^['"]/, '').replace(/['"]$/, '')
}

function splitAttr(segment) {
const [attr, key] = segment.split('==')
return {[attr]: unquote(key)}
}

function coerce(segment: string): PathSegment {
return IS_NUMERIC.test(segment) ? Number(segment) : segment
}

function parseSimplePath(focusPathStr): Path {
return focusPathStr
.split(/[[.\]]/g)
.filter(Boolean)
.map(seg => (seg.includes('==') ? splitAttr(seg) : coerce(seg)))
}

function formatPath(focusPath) {
return arrayToJSONMatchPath(focusPath)
}

function getHash() {
return decodeURIComponent(document.location.hash.substring(1))
}

function getPathFromHash() {
const hash = getHash()
return hash ? parseSimplePath(hash) : []
return hash ? toFormBuilder(hash) : []
}

export default class HashFocusManager extends React.Component<Props, State> {
Expand All @@ -71,7 +54,7 @@ export default class HashFocusManager extends React.Component<Props, State> {
}

handleFocus = (focusPath: Path) => {
document.location.hash = formatPath(focusPath)
document.location.hash = toGradient(focusPath)
}

handleBlur = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow weak
import documentStore from 'part:@sanity/base/datastore/document'
import gradientPatchAdapter from './utils/gradientPatchAdapter'
import * as gradientPatchAdapter from './utils/gradientPatchAdapter'
import type {Patch} from '../typedefs/patch'

export function checkout(documentId) {

Expand Down Expand Up @@ -35,7 +36,7 @@ export function checkout(documentId) {
...document,
events: events$,
patch(patches: Array<Patch>) {
document.patch(gradientPatchAdapter.fromFormBuilder(patches))
document.patch(gradientPatchAdapter.toGradient(patches))
}
}
}
33 changes: 33 additions & 0 deletions packages/@sanity/form-builder/src/sanity/utils/convertPath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// @flow
import {arrayToJSONMatchPath} from '@sanity/mutator'
import type {Path, PathSegment} from '../../typedefs/path'

const IS_NUMERIC = /^\d+$/

function unquote(str) {
return str.replace(/^['"]/, '').replace(/['"]$/, '')
}

function splitAttr(segment) {
const [attr, key] = segment.split('==')
return {[attr]: unquote(key)}
}

function coerce(segment: string): PathSegment {
return IS_NUMERIC.test(segment) ? Number(segment) : segment
}

function parseGradientPath(focusPathStr): Path {
return focusPathStr
.split(/[[.\]]/g)
.filter(Boolean)
.map(seg => (seg.includes('==') ? splitAttr(seg) : coerce(seg)))
}

export function toGradient(formBuilderPath: Path): string {
return arrayToJSONMatchPath(formBuilderPath)
}

export function toFormBuilder(gradientPath: string) {
return parseGradientPath(gradientPath)
}
106 changes: 53 additions & 53 deletions packages/@sanity/form-builder/src/sanity/utils/gradientPatchAdapter.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,79 @@
// @flow

import {arrayToJSONMatchPath} from '@sanity/mutator'
import assert from 'assert'
import {flatten} from 'lodash'
import type {Patch} from '../../utils/patches'
import type {Origin, Patch} from '../../typedefs/patch'
import * as convertPath from './convertPath'

type GradientPatch = Object

type Adapter = {
fromFormBuilder: (patches: Array<Patch>) => Array<GradientPatch>,
toFormBuilder: (origin: string, patches: Array<GradientPatch>) => Array<Patch>
export function toGradient(patches: Patch[]): GradientPatch[] {
return patches.map(toGradientPatch)
}

const adapter: Adapter = {
fromFormBuilder(patches) {
return patches.map(fromFormBuilder)
},
toFormBuilder
export function toFormBuilder(origin: Origin, patches: GradientPatch[]): Patch[] {
return flatten(patches.map(patch => toFormBuilderPatch(origin, patch)))
}

export default adapter


/**
*
* *** WARNING ***
*
* This function is *EXPERIMENTAL* and very likely to have bugs. It is not in real use yet, and needs
* to be revised.
*/

function toFormBuilder(origin, patches: Array<GradientPatch>): Array<Patch> {
return flatten(patches.map(patch => {
return flatten(Object.keys(patch)
function toFormBuilderPatch(origin: Origin, patch: GradientPatch): Patch {
return flatten(
Object.keys(patch)
.filter(key => key !== 'id')
.map((type): Array<Patch> => {
.map(type => {
if (type === 'unset') {
return patch.unset.map(path => {
return {
type: 'unset',
path: path.split('.'),
path: convertPath.toFormBuilder(path),
origin
}
})
}
return Object.keys(patch[type]).map(path => {
if (type === 'insert') {
const position = 'before' in patch.insert ? 'before' : 'after'
return {
type: 'insert',
position: position,
path: path.split('.'),
items: patch[type][path],
origin
return Object.keys(patch[type])
.map(gradientPath => {
if (type === 'insert') {
const position = 'before' in patch.insert ? 'before' : 'after'
return {
type: 'insert',
position: position,
path: convertPath.toFormBuilder(patch.insert[position]),
items: patch.insert.items,
origin
}
}
}
if (type === 'set') {
return {
type: 'set',
path: path.split('.'),
value: patch[type][path],
origin
if (type === 'set') {
return {
type: 'set',
path: convertPath.toFormBuilder(gradientPath),
value: patch[type][gradientPath],
origin
}
}
}
return {
type,
path: path.split('.'),
value: patch[type][path],
origin
}
})
}))
}))
if (type === 'setIfMissing') {
return {
type: 'setIfMissing',
path: convertPath.toFormBuilder(gradientPath),
value: patch[type][gradientPath],
origin
}
}
if (type === 'diffMatchPatch') {
return {
type: 'diffMatchPatch',
path: convertPath.toFormBuilder(gradientPath),
value: patch[type][gradientPath],
origin
}
}
console.warn(new Error(`Unsupported patch type: ${type}`))
return null
})
.filter(Boolean)
})
)
}

function fromFormBuilder(patch: Patch): GradientPatch {
function toGradientPatch(patch: Patch): GradientPatch {
const matchPath = arrayToJSONMatchPath(patch.path || [])
if (patch.type === 'insert') {
const {position, items} = patch
Expand Down
42 changes: 42 additions & 0 deletions packages/@sanity/form-builder/src/typedefs/patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// @flow
type KeyedSegment = {
_key: string
}

type JSONValue = number | string | boolean | {[string]: JSONValue} | JSONValue[]

export type PathSegment = string | number | KeyedSegment

export type Path = Array<PathSegment>

export type Origin = 'remote' | 'local'

type SetPatch = {
path: Path,
type: 'set',
origin: Origin,
value: JSONValue
}

type SetIfMissingPatch = {
path: Path,
origin: Origin,
type: 'setIfMissing',
value: JSONValue
}

type UnsetPatch = {
path: Path,
origin: Origin,
type: 'unset'
}

type InsertPatch = {
path: Path,
origin: Origin,
type: 'insert',
position: 'before' | 'after',
items: JSONValue[]
}

export type Patch = SetPatch | SetIfMissingPatch | UnsetPatch | InsertPatch
8 changes: 0 additions & 8 deletions packages/@sanity/form-builder/src/typedefs/path.js

This file was deleted.

30 changes: 0 additions & 30 deletions packages/@sanity/form-builder/src/utils/patches.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,5 @@
// @flow

import type {Path, PathSegment} from '../typedefs/path'

type HasPath = {
path: Path
}
type HasOrigin = {
origin?: 'remote' | 'local'
}
type SetPatch = HasPath & HasOrigin & {
type: 'set',
value: any
}

type SetIfMissingPatch = HasPath & HasOrigin & {
type: 'setIfMissing',
value: any
}

type UnsetPatch = HasPath & HasOrigin & {
type: 'unset',
}

type InsertPosition = 'before' | 'after'
type InsertPatch = HasPath & HasOrigin & {
type: 'insert',
position: InsertPosition,
items: any[]
}

export type Patch = SetPatch | SetIfMissingPatch | UnsetPatch | InsertPatch

export function setIfMissing(value : any, path : Path = []) : SetIfMissingPatch {
return {
Expand Down

0 comments on commit cc039fe

Please sign in to comment.