Skip to content

Instantly share code, notes, and snippets.

@dinocarl
Last active July 3, 2019 16:53
Show Gist options
  • Save dinocarl/78cfc4eb14f18880d3a3385156c08ad7 to your computer and use it in GitHub Desktop.
Save dinocarl/78cfc4eb14f18880d3a3385156c08ad7 to your computer and use it in GitHub Desktop.
const {
compose,
concat,
cond,
curry,
flatten,
flip,
identity,
is,
isNil,
isEmpty,
join,
length,
map,
prop,
propOr,
reduce,
reject,
splitAt,
toPairs,
trim,
T,
uniq,
zip
} = require('ramda');
const condJoin = cond([
[is(Array), join('')],
[T, identity]
]);
const attrTmplt = ([key, val]) => `${key}="${val}"`;
const attrsTmplt = compose(
join(' '),
map(attrTmplt),
toPairs
);
const pairedTag = curry((tagname, [attrs, content]) => `${trim(
join(
' ',
[
`<${tagname}`,
attrsTmplt(attrs)
]
)
)}>${content}</${tagname}>`);
const selfClosingTag = curry((tagname, [attrs]) => `<${tagname} ${attrsTmplt(attrs)} />`);
const identityTag = ([, content]) => content;
const textTmplt = pairedTag('p');
const linkTmplt = pairedTag('a');
const div = pairedTag('div');
const span = pairedTag('span');
const condLink = (condition) => (condition
? linkTmplt
: identityTag
);
const addLink = (condition, linkPath) => (condition
? linkTmplt(
[
{ href: linkPath, class: 'whole-item' },
span([{ class: 'hidden-text' }, 'Link'])
]
)
: ''
);
const templateOptions = {
link: linkTmplt,
title: pairedTag('h1'),
heading: pairedTag('h2'),
subheading: pairedTag('h3'),
kicker: pairedTag('h4'),
plainText: textTmplt,
image: selfClosingTag('img'),
none: identityTag,
};
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')
? {
class: type,
src: val,
alt: propOr('', 'altText', limiter)
}
: { class: type };
let content = (type !== 'image')
? val
: '';
let wrapper = condLink((linkContent === attrName));
return wrapper([
{ href: propOr('', linkField, data) },
propOr(textTmplt, type, templateOptions)([
attrs,
content
])
]);
});
// The following 2 functions are for when an attribute
// layout selection has been passed that requires clever grouping.
// `size` will be a string, and arrContent is the content as an array
const cardColumn = curry((size, arrContent) => div([
{ class: size },
join('', arrContent)
]));
const create2Cols = curry((sizeA, sizeB, [htmlStringsA, htmlStringsB]) => concat(
cardColumn(sizeA, htmlStringsA),
cardColumn(sizeB, htmlStringsB)
));
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 htmlStrs = 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 = join('/', [
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,
condJoin,
assembleFn,
condSplit
)(htmlStrs);
};
// End Prep
// Data
const data = {
title: "The Adventures of Tom Sawyer",
path: "https://www.gutenberg.org/cache/epub/74/pg74.cover.medium.jpg",
kicker: "Kicker",
author: "Mark Twain",
date: "1559674546767",
urlPath: "https://www.gutenberg.org/files/74/74-0.txt",
};
const layoutOpts = [
{
size: 'xs',
lt: '4',
rt: '8'
}, {
size: 'md',
lt: '6',
rt: '6'
}, {
size: 'lg',
lt: '8',
rt: '4'
}
];
const limiter = [
{
attr: "path",
renderAs: "image"
},
{
attr: "title",
renderAs: "heading"
},
{
attr: "kicker",
renderAs: "subheading"
},
{
attr: "author",
renderAs: "subheading"
},
];
miniTmplt(
data,
limiter,
'card',
layoutOpts,
1,
'urlPath',
'wholeItem'
);
// https://codepen.io/anon/pen/vqgmgw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment