Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/viewstools/morph
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Jones authored and Ryan Jones committed Sep 10, 2019
2 parents cc8c376 + 249d578 commit 6b2e9dc
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 66 deletions.
1 change: 1 addition & 0 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ let wait = time => new Promise(resolve => setTimeout(resolve, time))
--as target platform
react-dom (default)
react-native
react-pdf
--clean clean the autogenerated .view.js files
--local default local language, defaults to English (en)
--tools use with Views Tools, defauls to true when
Expand Down
134 changes: 83 additions & 51 deletions ensure-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,98 +5,130 @@ let USE_DATA = `// This file is automatically generated by Views and will be ove
// when the morpher runs. If you want to contribute to how it's generated, eg,
// improving the algorithms inside, etc, see this:
// https://github.com/viewstools/morph/blob/master/ensure-data.js
import get from 'lodash/get'
import produce from 'immer'
import set from 'lodash/set'
import React, { useContext, useEffect, useMemo, useReducer } from 'react'
import parseDate from 'date-fns/parse'
import parseISO from 'date-fns/parseISO'
import formatDate from 'date-fns/format'
import isValidDate from 'date-fns/isValid'
let identity = { in: i => i, out: i => i }
import * as fromValidate from './Data/validators.js';
import get from 'lodash/get';
import produce from 'immer';
import set from 'lodash/set';
import React, { useContext, useEffect, useMemo, useReducer, useRef } from 'react';
import parseDate from 'date-fns/parse';
import parseISO from 'date-fns/parseISO';
import formatDate from 'date-fns/format';
import isValidDate from 'date-fns/isValid';
let identity = { in: i => i, out: i => i };
// show
let ItemContext = React.createContext({})
export let ItemProvider = ItemContext.Provider
export let useItem = (path = null, format = identity) => {
let item = useContext(ItemContext)
return useMemo(() => (
path? format.in(get(item, path)) : item
), [item, path, format]) // eslint-ignore-line
let ItemContext = React.createContext({});
export let ItemProvider = ItemContext.Provider;
export let useItem = ({ path = null, format = identity } = {}) => {
let item = useContext(ItemContext);
return useMemo(() => (path ? { value: format.in(get(item, path)) } : item), [
item,
path,
format,
]); // eslint-ignore-line
// ignore get
}
};
// capture
let captureItemReducer = produce((draft, action) => {
switch (action.type) {
case CAPTURE_SET_FIELD: {
set(draft, action.key, action.value)
break
set(draft, action.key, action.value);
break;
}
case CAPTURE_RESET: {
return action.state
return action.state;
}
default: {
throw new Error(
\`Unknown action type "\${action.type}" in update item reducer.\`
)
);
}
}
})
let CAPTURE_SET_FIELD = 'capture/SET_FIELD'
});
let CAPTURE_SET_FIELD = 'capture/SET_FIELD';
export let setField = (key, value) => ({
type: CAPTURE_SET_FIELD,
key,
value,
})
let CAPTURE_RESET = 'capture/RESET'
export let reset = state => ({ type: CAPTURE_RESET, state })
let CaptureItemContext = React.createContext({})
export let CaptureItemProvider = CaptureItemContext.Provider
export let useCaptureItem = (path = null, format = identity) => {
let captureItem = useContext(CaptureItemContext)
});
let CAPTURE_RESET = 'capture/RESET';
export let reset = state => ({ type: CAPTURE_RESET, state });
let CaptureItemContext = React.createContext({});
export let CaptureItemProvider = CaptureItemContext.Provider;
export let useCaptureItem = ({
path = null, format = identity, validate = null
} = {}) => {
let captureItem = useContext(CaptureItemContext);
let touched = useRef(false);
if (process.env.NODE_ENV === 'development') {
if (validate && !(validate in fromValidate)) {
throw new Error(
\`"\${validate}" function doesn't exist or is not exported in Data/validators.js\`
);
}
}
return useMemo(() => {
if (!path) return captureItem
if (!path) return captureItem;
let [item, dispatch, onSubmit] = captureItem;
if (!item) return {};
let value = format.in(get(item, path));
let isValid =
touched.current && validate ? fromValidate[validate](value) : true;
let onChange = value => {
touched.current = true;
dispatch(setField(path, format.out(value)));
}
return {
onChange: value => dispatch(setField(path, format.out(value))),
onChange,
onSubmit,
value: format.in(get(item, path)),
}
}, [captureItem, path, format])
}
value,
isValid,
isInvalid: !isValid
};
}, [captureItem, path, format, validate]);
};
export let useCaptureItemProvider = (item, onSubmit) => {
let [state, dispatch] = useReducer(captureItemReducer, item)
let [state, dispatch] = useReducer(captureItemReducer, item);
useEffect(() => {
dispatch(reset(item))
}, [item])
dispatch(reset(item));
}, [item]);
return useMemo(() => [state, dispatch, onSubmit], [state, dispatch, onSubmit])
}
return useMemo(() => [state, dispatch, onSubmit], [
state,
dispatch,
onSubmit,
]);
};
function formatDateInOut(rvalue, formatIn, formatOut, whenInvalid = '') {
let value =
formatIn === 'iso'
? parseISO(rvalue)
: parseDate(rvalue, formatIn, new Date())
return isValidDate(value) ? formatDate(value, formatOut) : whenInvalid
: parseDate(rvalue, formatIn, new Date());
return isValidDate(value) ? formatDate(value, formatOut) : whenInvalid;
}
export let useMakeFormatDate = (formatIn, formatOut, whenInvalid) =>
useMemo(() => ({
in: value => formatDateInOut(value, formatIn, formatOut, whenInvalid),
out: value => formatDateInOut(value, formatOut, formatIn, whenInvalid),
}), []) // eslint-disable-line
// ignore formatIn, formatouOut, whenInvalid
useMemo(
() => ({
in: value => formatDateInOut(value, formatIn, formatOut, whenInvalid),
out: value => formatDateInOut(value, formatOut, formatIn, whenInvalid),
}),
[]
); // eslint-disable-line
// ignore formatIn, formatouOut, whenInvalid
`

export default function ensureIsBefore({ src }) {
Expand Down
2 changes: 1 addition & 1 deletion ensure-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function ensureFirstStoryIsOn(flow, key, stories) {
if (!stories.has(key)) return

let story = flow.get(key)
if (story.stories.size > 0) {
if (story && story.stories.size > 0) {
let index = 0
for (let id of story.stories) {
if (index === 0 || !story.isSeparate) {
Expand Down
1 change: 1 addition & 0 deletions fonts.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import relativise from './relativise.js'
let morphFont = {
'react-dom': morphFontAsReactDom,
'react-native': morphFontAsReactNative,
'react-pdf': morphFontAsReactNative,
}

export async function ensureFontsDirectory(src) {
Expand Down
1 change: 1 addition & 0 deletions morph/react-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default ({
cssStatic: false,
data: view.parsed.view.data,
dataFormat: view.parsed.view.dataFormat,
dataValidate: view.parsed.view.dataValidate,
dependencies: new Set(),
flow: null,
setFlow: false,
Expand Down
6 changes: 4 additions & 2 deletions morph/react-dom/get-value-for-property.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ export default (node, parent, state) => {
state.data &&
(node.value === 'props.value' ||
node.value === 'props.onSubmit' ||
node.value === 'props.onChange')
node.value === 'props.onChange' ||
node.value === 'props.isInvalid' ||
node.value === 'props.isValid')
) {
return {
[node.name]: `{${node.value.replace('props.', '')} || ${node.value}}`,
[node.name]: `{${node.value.replace('props.', 'data.')}}`,
}
} else if (isValidImgSrc(node, parent)) {
return {
Expand Down
3 changes: 3 additions & 0 deletions morph/react-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default ({
getSystemImport,
local,
localSupported,
reactNativeLibraryImport = 'react-native',
track,
view,
viewsById,
Expand All @@ -44,6 +45,7 @@ export default ({
images: [],
data: view.parsed.view.data,
dataFormat: view.parsed.view.dataFormat,
dataValidate: view.parsed.view.dataValidate,
dependencies: new Set(),
flow: null,
setFlow: false,
Expand Down Expand Up @@ -74,6 +76,7 @@ export default ({
testIdKey: 'testID',
testIds: {},
track,
reactNativeLibraryImport,
usedBlockNames: { [finalName]: 1, AutoSizer: 1, Column: 1, Table: 1 },
uses: [],
use(block, isLazy = false) {
Expand Down
13 changes: 12 additions & 1 deletion morph/react-native/get-value-for-property.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,18 @@ let getImageSource = (node, state, parent) => {
}

export default (node, parent, state) => {
if (isValidImgSrc(node, parent)) {
if (
state.data &&
(node.value === 'props.value' ||
node.value === 'props.onSubmit' ||
node.value === 'props.onChange' ||
node.value === 'props.isValid' ||
node.value === 'props.isInvalid')
) {
return {
[node.name]: `{${node.value.replace('props.', 'data.')}}`,
}
} else if (isValidImgSrc(node, parent)) {
return (
!parent.isSvg && {
source: getImageSource(node, state, parent),
Expand Down
7 changes: 7 additions & 0 deletions morph/react-pdf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import reactNativeMorph from './react-native.js'

export default options =>
reactNativeMorph({
...options,
reactNativeLibraryImport: '@react-pdf/renderer',
})
9 changes: 7 additions & 2 deletions morph/react/block-off-when.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ export function enter(node, parent, state) {
node.onWhen = true

if (parent && !isList(parent)) state.render.push('{')

state.render.push(`${onWhen.value} ? `)
let value = onWhen.value
if (state.data && value === 'props.isInvalid') {
value = 'data.isInvalid'
} else if (state.data && value === 'props.isValid') {
value = 'data.isValid'
}
state.render.push(`${value} ? `)
} else if (isStory(node, state)) {
node.onWhen = true
state.render.push(`{flow.has("${state.pathToStory}/${node.name}") ? `)
Expand Down
17 changes: 11 additions & 6 deletions morph/react/get-body.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,19 @@ export default ({ state, name }) => {
if (state.data) {
switch (state.data.type) {
case 'show': {
data.push(`let value = fromData.useItem('${state.data.path}'`)
data.push(`let data = fromData.useItem({ path: '${state.data.path}', `)
maybeDataFormat(state.dataFormat, data)
data.push(')')
data.push('})')
break
}

case 'capture': {
data.push(
`let { value, onChange, onSubmit }= fromData.useCaptureItem('${state.data.path}'`
`let data = fromData.useCaptureItem({ path: '${state.data.path}', `
)
maybeDataFormat(state.dataFormat, data)
data.push(')')
maybeDataValidate(state.dataValidate, data)
data.push('})')
break
}

Expand Down Expand Up @@ -126,10 +127,14 @@ function maybeDataFormat(format, data) {
if (format.type !== 'date') return

data.push(
`, fromData.useMakeFormatDate('${format.formatIn}', '${format.formatOut}'`
`format: fromData.useMakeFormatDate('${format.formatIn}', '${format.formatOut}'`
)
if (format.whenInvalid) {
data.push(`, '${format.whenInvalid}'`)
}
data.push(`)`)
data.push(`),`)
}
function maybeDataValidate(validate, data) {
if (!validate || validate.type !== 'js') return
data.push(`validate: '${validate.value}',`)
}
6 changes: 5 additions & 1 deletion morph/react/get-dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,11 @@ export default (state, getImport) => {
}

if (usesNative.length > 0) {
dependencies.push(`import { ${usesNative.join(', ')} } from 'react-native'`)
dependencies.push(
`import { ${usesNative.join(', ')} } from '${
state.reactNativeLibraryImport
}'`
)
}

if (state.track) {
Expand Down
4 changes: 3 additions & 1 deletion morph/react/property-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ let parseFormatValue = (value, type) => {

export function enter(node, parent, state) {
if (node.name === 'text' && parent.name === 'Text') {
if (hasCustomScopes(node, parent)) {
if (state.data && node.value === 'props.value') {
parent.explicitChildren = '{data.value}'
} else if (hasCustomScopes(node, parent)) {
parent.explicitChildren = wrap(getScopedCondition(node, parent))
} else if (isSlot(node)) {
parent.explicitChildren = wrap(node.value)
Expand Down
2 changes: 2 additions & 0 deletions morphers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import reactDom from './morph/react-dom.js'
import reactNative from './morph/react-native.js'
import reactPdf from './morph/react-pdf.js'

export default {
'react-dom': reactDom,
'react-native': reactNative,
'react-pdf': reactPdf,
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@viewstools/morph",
"version": "19.7.2",
"version": "19.9.0",
"description": "Views language morpher",
"main": "index.js",
"type": "module",
Expand Down
8 changes: 8 additions & 0 deletions parse/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ export let getDataFormat = maybeProp => {
}
}

export let getDataValidate = maybeProp => {
if (!maybeProp || !maybeProp.value) return null
return {
type: 'js',
value: maybeProp.value,
}
}

export let getFormat = line => {
let properties = {}
let values = line.split(' ')
Expand Down
Loading

0 comments on commit 6b2e9dc

Please sign in to comment.