Last active
August 29, 2019 21:44
-
-
Save dinocarl/c2a9406773a2b9327de9c2e418bc8f81 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { | |
compose, | |
concat, | |
curry, | |
flatten, | |
flip, | |
identity, | |
isNil, | |
isEmpty, | |
join, | |
length, | |
map, | |
prop, | |
propOr, | |
reduce, | |
reject, | |
splitAt, | |
toPairs, | |
uniq, | |
zip | |
} = R // ramda | |
const el = curry(React.createElement) | |
const identityTag = curry((props, ...children) => children) | |
const div = el(`div`) | |
const h1 = el(`h1`) | |
const h2 = el(`h2`) | |
const p = el(`p`) | |
const span = el(`span`) | |
const a = el(`a`) | |
const ul = el(`ul`) | |
const li = el(`li`) | |
const textTmplt = el(`p`) | |
const linkTmplt = el(`a`) | |
const listedItems = map(li({})) | |
const addLink = (condition, linkPath) => (condition | |
? linkTmplt( | |
{ href: linkPath, className: 'whole-item' }, | |
span({ className: 'hidden-text' }, 'Link') | |
) | |
: '' | |
) | |
const condLink = (condition) => (condition | |
? linkTmplt | |
: identityTag | |
) | |
const templateOptions = { | |
link: linkTmplt, | |
title: el('h1'), | |
heading: el('h2'), | |
subheading: el('h3'), | |
kicker: el('h4'), | |
plainText: textTmplt, | |
image: el('img'), | |
} | |
const itemTemplate = curry((data, linkField, linkContent, limiter) => { | |
let type = prop('renderAs', limiter) | |
let attrName = prop('attr', limiter) | |
let val = prop(attrName, data) | |
let attrs = (type === 'image') | |
? { | |
className: type, | |
src: val, | |
alt: propOr('', 'altText', limiter) | |
} | |
: { className: type } | |
let content = (type !== 'image') | |
? val | |
: null | |
let wrapper = condLink((linkContent === attrName)) | |
return wrapper( | |
{ href: propOr('', linkField, data) }, | |
propOr(textTmplt, type, templateOptions)( | |
attrs, | |
content | |
) | |
) | |
}) | |
const cardColumn = (size) => div({ className: size }) | |
const create2Cols = curry((sizeA, sizeB, [childrenA, childrenB]) => [ | |
cardColumn(sizeA)(childrenA), | |
cardColumn(sizeB)(childrenB) | |
]) | |
const groupPermutations = ({ size, rt, lt }) => [ | |
`${size}-${lt}`, | |
`${size}-${rt}`, | |
] | |
const sizeGroupings = compose( | |
map(groupPermutations), | |
uniq | |
) | |
const colClass = concat('col-') | |
const colClasses = compose( | |
join(' '), | |
map(colClass), | |
reject(isEmpty), | |
flatten | |
) | |
// Creates the sizing tuple needed for a 2 column layout. | |
// Expects an array of sizes that look like | |
// [{size: 'xs', grouping: '3-9'}, {size: 'sm', grouping: '5-7'}] | |
// and returns an array of col size strings like | |
// ['col-xs-3 col-sm-5', 'col-xs-9 col-sm-7'] | |
const colClassTuple = compose( | |
map(colClasses), | |
reduce(zip, ['', '']), | |
sizeGroupings | |
) | |
const miniTmplt = (data, arr, layout, layoutOpts, splitPos, linkField, linkContent) => { | |
// Convert the array of attributes into an array of HTML strings | |
let dataBasedHtml = map(itemTemplate(data, linkField, linkContent), arr) | |
// Use a default layout or the one specified in the data | |
let layoutData = isNil(layoutOpts) || length(layoutOpts) === 0 | |
? [{ size: 'xs', lt: '3', rt: '3' }] | |
: layoutOpts; | |
// Sometimes the data provides a basePath data point. Use it as a | |
// possible prefix to the whatever is being passed in to linkField | |
let linkPath = compose( | |
join('/'), | |
reject(isEmpty) | |
)([ | |
propOr('', 'basePath', data), | |
propOr('', linkField, data) | |
]); | |
// A value can be passed from the data that specifies when after | |
// which array member a division should be created. Defaults to 1 | |
let splitPosition = isNil(splitPos) | |
? 1 | |
: splitPos; | |
// Function ready to concatenate either the returned formed link | |
// based on the data supplied or empty string to whatever is passed to it. | |
// used to add an extra anchor tag after the rest of the created HTML | |
// in case the whole Item is intended to serve as the link | |
let wholeItemLink = flip(concat)([addLink((linkContent === 'wholeItem'), linkPath)]); | |
// We might need a tuple to be be able to create a 2 column layout | |
let cardGroups = (layout === 'card') | |
? colClassTuple(layoutData) | |
: []; | |
// Function that either provides a pass-thru or a layout Function | |
let assembleFn = length(cardGroups) === 2 | |
? create2Cols(...cardGroups) | |
: identity; | |
let condSplit = (layout === 'card') | |
? splitAt(splitPosition) | |
: identity; | |
return compose( | |
wholeItemLink, | |
assembleFn, | |
condSplit | |
)(dataBasedHtml); | |
}; | |
// data | |
const listData = [ | |
`a`, `b`, `c` | |
] | |
const data = { | |
title: "The Adventures of Tom Sawyer", | |
path: "https://www.gutenberg.org/cache/epub/74/pg74.cover.medium.jpg", | |
kicker: "This is the Kicker", | |
author: "Mark Twain", | |
date: "1559674546767", | |
urlPath: "https://www.gutenberg.org/files/74/74-0.txt", | |
} | |
const limiter = [ | |
{ | |
attr: "path", | |
renderAs: "image" | |
}, | |
{ | |
attr: "title", | |
renderAs: "heading" | |
}, | |
{ | |
attr: "kicker", | |
renderAs: "subheading" | |
}, | |
{ | |
attr: "author", | |
renderAs: "subheading" | |
}, | |
] | |
const layoutOpts = [ | |
{ | |
size: 'xs', | |
lt: '4', | |
rt: '8' | |
}, { | |
size: 'md', | |
lt: '6', | |
rt: '6' | |
}, { | |
size: 'lg', | |
lt: '8', | |
rt: '4' | |
} | |
] | |
const App = () => div( | |
{ className: `container` }, | |
h1( | |
{}, | |
span( | |
{ style: { color: `red` } }, | |
`React without JSX` | |
), | |
` - `, | |
a( | |
{ | |
id: `some-id`, | |
href: `/path/to/resource` | |
}, | |
`link text` | |
), | |
' ', | |
span( | |
{}, | |
'last span' | |
) | |
), | |
div( | |
{ className: 'row' }, | |
...miniTmplt( | |
data, | |
limiter, | |
'card', | |
layoutOpts, | |
1, | |
'urlPath', | |
'wholeItem' | |
) | |
), | |
ul( | |
{}, | |
listedItems(listData) | |
) | |
) | |
ReactDOM.render( | |
App(), | |
document.getElementById(`app`) | |
); | |
// https://jsfiddle.net/48bkn7Lo/ | |
// https://jsfiddle.net/9g7yq345/ | |
// https://jsfiddle.net/ytx5uj9c/1/ | |
// https://jsfiddle.net/p5Lj6e3d/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment