Skip to content

Commit

Permalink
[form-builder] Move pathUtils to util package (#1181)
Browse files Browse the repository at this point in the history
  • Loading branch information
rexxars authored and bjoerge committed Feb 1, 2019
1 parent 374c332 commit 9ea15cd
Show file tree
Hide file tree
Showing 17 changed files with 299 additions and 17 deletions.
1 change: 1 addition & 0 deletions packages/@sanity/form-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@sanity/imagetool": "0.140.0",
"@sanity/mutator": "0.140.0",
"@sanity/schema": "0.140.0",
"@sanity/util": "0.140.0",
"attr-accept": "^1.1.0",
"canvas-to-blob": "^0.0.0",
"classnames": "^2.2.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/@sanity/form-builder/src/FormBuilderInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react'
import type {Path} from './typedefs/path'
import PatchEvent from './PatchEvent'
import generateHelpUrl from '@sanity/generate-help-url'
import * as PathUtils from './utils/pathUtils'
import * as PathUtils from '@sanity/util/paths'
import type {Type} from './typedefs'

type Props = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {Marker, Type} from '../../typedefs'
import type {Path} from '../../typedefs/path'
import type {Subscription} from '../../typedefs/observable'
import {resolveTypeName} from '../../utils/resolveTypeName'
import {FOCUS_TERMINATOR, startsWith} from '../../utils/pathUtils'
import {FOCUS_TERMINATOR, startsWith} from '@sanity/util/paths'
import UploadTargetFieldset from '../../utils/UploadTargetFieldset'
import {insert, PatchEvent, set, setIfMissing, unset} from '../../PatchEvent'
import styles from './styles/ArrayInput.css'
Expand Down Expand Up @@ -296,9 +296,7 @@ export default class ArrayInput extends React.Component<Props, State> {
Some items in this list are missing their keys. We need to fix this before the list can
be edited.
<div className={styles.fixMissingKeysButtonWrapper}>
<Button onClick={this.handleFixMissingKeys}>
Fix missing keys
</Button>
<Button onClick={this.handleFixMissingKeys}>Fix missing keys</Button>
</div>
<Details title={<b>Why is this happening?</b>}>
This usually happens when items are created through the API client from outside the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Preview from '../../Preview'
import {resolveTypeName} from '../../utils/resolveTypeName'
import type {Path} from '../../typedefs/path'
import type {Marker, Type} from '../../typedefs'
import * as PathUtils from '../../utils/pathUtils'
import * as PathUtils from '@sanity/util/paths'
import ConfirmButton from './ConfirmButton'
import styles from './styles/ItemValue.css'
import type {ArrayType, ItemValue} from './typedefs'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {Item as SortableItem, List as SortableList} from 'part:@sanity/component
import ArrayFunctions from 'part:@sanity/form-builder/input/array/functions'
import Fieldset from 'part:@sanity/components/fieldsets/default'
import {PatchEvent, set, unset} from '../../PatchEvent'
import {startsWith} from '../../utils/pathUtils'
import {startsWith} from '@sanity/util/paths'
import {resolveTypeName} from '../../utils/resolveTypeName'
import type {Path} from '../../typedefs/path'
import type {Type, Marker} from '../../typedefs'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import LinkIcon from 'part:@sanity/base/link-icon'
import SanityLogoIcon from 'part:@sanity/base/sanity-logo-icon'
import ToggleButton from 'part:@sanity/components/toggles/button'
import type {BlockContentFeature, BlockContentFeatures, Path, SlateEditor} from '../typeDefs'
import {FOCUS_TERMINATOR} from '../../../utils/pathUtils'
import {FOCUS_TERMINATOR} from '@sanity/util/paths'
import CustomIcon from './CustomIcon'
import ToolbarClickAction from './ToolbarClickAction'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import BlockObjectIcon from 'part:@sanity/base/block-object-icon'
import InlineObjectIcon from 'part:@sanity/base/inline-object-icon'

import type {Type, SlateValue, SlateEditor, Path} from '../typeDefs'
import {FOCUS_TERMINATOR} from '../../../utils/pathUtils'
import {FOCUS_TERMINATOR} from '@sanity/util/paths'
import styles from './styles/InsertMenu.css'

type Props = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type {
} from '../typeDefs'

import {PatchEvent} from '../../../PatchEvent'
import {FOCUS_TERMINATOR} from '../../../utils/pathUtils'
import {FOCUS_TERMINATOR} from '@sanity/util/paths'
import {resolveTypeName} from '../../../utils/resolveTypeName'

import InvalidValue from '../../InvalidValueInput'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type {

import {resolveTypeName} from '../../../utils/resolveTypeName'
import {PatchEvent} from '../../../PatchEvent'
import {FOCUS_TERMINATOR} from '../../../utils/pathUtils'
import {FOCUS_TERMINATOR} from '@sanity/util/paths'

import InvalidValue from '../../InvalidValueInput'
import Preview from '../../../Preview'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type {Node} from 'react'
import React from 'react'
import type {BlockContentFeatures, Type, Marker, Path, SlateEditor} from '../typeDefs'
import {FOCUS_TERMINATOR} from '../../../utils/pathUtils'
import {FOCUS_TERMINATOR} from '@sanity/util/paths'
import styles from './styles/Span.css'
import {Inline} from 'slate'

Expand Down
3 changes: 2 additions & 1 deletion packages/@sanity/util/.babelrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = {
extends: '../../../.babelrc.js'
extends: '../../../.babelrc.js',
presets: ['@babel/preset-flow']
}
7 changes: 7 additions & 0 deletions packages/@sanity/util/.flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[include]
./src
../../../
[libs]
defs
[ignore]
.*/examples/.*
1 change: 1 addition & 0 deletions packages/@sanity/util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"resolve-from": "^4.0.0"
},
"devDependencies": {
"@babel/preset-flow": "^7.0.0",
"jest": "^23.6.0",
"rimraf": "^2.6.2"
}
Expand Down
1 change: 1 addition & 0 deletions packages/@sanity/util/paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/pathUtils')
Original file line number Diff line number Diff line change
@@ -1,15 +1,57 @@
/* eslint-disable max-depth */
// @flow
import type {Path, PathSegment} from '../typedefs/path'

const rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g
const reKeySegment = /_key\s*==\s*['"](.*)['"]/

export const FOCUS_TERMINATOR = '$'

// eslint-disable-next-line complexity
export function get(obj: mixed, path: Path | string, defaultVal: mixed) {
const select = typeof path === 'string' ? fromString(path) : path
if (!Array.isArray(select)) {
throw new Error('Path must be an array or a string')
}

let acc = obj
for (let i = 0; i < select.length; i++) {
const segment = select[i]
if (isIndexSegment(segment)) {
if (!Array.isArray(acc)) {
return defaultVal
}

acc = acc[segment]
}

if (isKeySegment(segment)) {
if (!Array.isArray(acc)) {
return defaultVal
}

acc = acc.find(item => item._key === segment._key)
}

if (typeof segment === 'string') {
acc = typeof acc === 'object' && acc !== null ? acc[segment] : undefined
}

if (typeof acc === 'undefined') {
return defaultVal
}
}

return acc
}

export function isEqual(path: Path, otherPath: Path) {
return (
path.length === otherPath.length &&
path.every((segment, i) => isSegmentEqual(segment, otherPath[i]))
)
}

export const FOCUS_TERMINATOR = '$'

export function isSegmentEqual(pathSegment: PathSegment, otherPathSegment: PathSegment) {
const pathSegmentType = typeof pathSegment
const otherPathSegmentType = typeof otherPathSegment
Expand Down Expand Up @@ -56,7 +98,7 @@ export function trimLeft(prefix: Path, path: Path): Path {
return trimLeft(prefixTail, pathTail)
}

export function trimRight(suffix, path) {
export function trimRight(suffix: Path, path: Path): Path {
const sufLen = suffix.length
const pathLen = path.length
if (sufLen === 0 || pathLen === 0) {
Expand All @@ -75,7 +117,11 @@ export function trimRight(suffix, path) {
return path.slice(0, pathLen - i)
}

export function toString(path) {
export function toString(path: Path): string {
if (!Array.isArray(path)) {
throw new Error('Path is not an array')
}

return path.reduce((target, segment, i) => {
const segmentType = typeof segment
if (segmentType === 'number') {
Expand All @@ -94,3 +140,49 @@ export function toString(path) {
throw new Error(`Unsupported path segment \`${JSON.stringify(segment)}\``)
}, '')
}

export function fromString(path: string): Path {
if (typeof path !== 'string') {
throw new Error('Path is not a string')
}

const segments = path.match(rePropName)
if (!segments) {
throw new Error('Invalid path string')
}

return segments.map(normalizePathSegment)
}

function normalizePathSegment(segment: string): PathSegment {
if (isIndexSegment(segment)) {
return normalizeIndexSegment(segment)
}

if (isKeySegment(segment)) {
return normalizeKeySegment(segment)
}

return segment
}

function normalizeIndexSegment(segment: string): PathSegment {
return Number(segment.replace(/[^\d]/g, ''))
}

function normalizeKeySegment(segment: string): PathSegment {
const segments = segment.match(reKeySegment)
return {_key: segments[1]}
}

function isIndexSegment(segment: string | number): boolean {
return typeof segment === 'number' || /^\[\d+\]$/.test(segment)
}

function isKeySegment(segment: PathSegment): boolean {
if (typeof segment === 'string') {
return reKeySegment.test(segment.trim())
}

return segment && segment._key
}
7 changes: 7 additions & 0 deletions packages/@sanity/util/src/typedefs/path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type KeyedSegment = {
_key: string
}

export type PathSegment = string | number | KeyedSegment

export type Path = Array<PathSegment>
Loading

0 comments on commit 9ea15cd

Please sign in to comment.