@cianciarusocataldo/demo-ui
Version:
A tiny UI library designed for build interactive demos for every react app
256 lines (244 loc) • 16.4 kB
JavaScript
/* eslint-disable */ import './styles/index.css';
import React from 'react';
import html2canvas from 'html2canvas';
import classnames from 'classnames';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
var FORMATTERS = {
checkbox: function (value) { return !(String(value) === "true"); },
text: function (value) { return value; },
number: function (value) { return value; },
range: function (value) { return value; },
select: function (value) { return value; },
color: function (value) { return String(value); },
};
/**
* Format a valued based on the given type
*
* @param value a value to format
* @param type the prop's type
*
* @returns a formatted value for the prop
*/
var formatValue = function (value, type) {
return FORMATTERS[type](value);
};
/**
*
*
* @param base
* @param toMerge
* @returns
*/
var merge = function (base, toMerge) {
var tmp = __assign({}, base);
Object.keys(toMerge).forEach(function (key) { return (tmp[key] = toMerge[key]); });
return tmp;
};
var getProps = function (props) {
var tmp = {};
Object.keys(props).forEach(function (prop) { return (tmp[prop] = props[prop].value); });
return tmp;
};
var takeScreenshot = function (element, background) {
if (background === void 0) { background = "#ffffff"; }
html2canvas(element, {
scrollY: 0,
scrollX: 0,
allowTaint: true,
backgroundColor: background,
useCORS: true,
logging: false,
height: window.outerHeight + window.innerHeight,
windowHeight: window.outerHeight + window.innerHeight,
}).then(function (canvas) {
downloadFile(canvas
.toDataURL("image/png")
.replace(/^data:image\/png/, "data:application/octet-stream"));
});
};
var downloadFile = function (url) {
var downloadLink = document.createElement("a");
downloadLink.setAttribute("download", "component_preview.png");
downloadLink.setAttribute("href", url);
downloadLink.click();
};
var Box = function (_a) {
var className = _a.className, _b = _a.label, label = _b === void 0 ? "" : _b, children = _a.children; _a.callBack; var _c = _a.values, values = _c === void 0 ? {} : _c, _d = _a.hide, hide = _d === void 0 ? false : _d, id = _a.id, _e = _a.onChange, onChange = _e === void 0 ? function () { } : _e, inputProps = __rest(_a, ["className", "label", "children", "callBack", "values", "hide", "id", "onChange"]);
var _f = React.useState(""), selectedValue = _f[0], setValue = _f[1];
var boxClassname = classnames("box", inputProps.type);
return (React.createElement("div", { className: classnames(className, { hidden: hide }) },
React.createElement("div", { className: "box-container", key: id || label },
React.createElement("div", { key: "demo_label", className: "demo-label" },
React.createElement("label", null, label)),
inputProps.type === "select" ? (React.createElement("select", { className: boxClassname, value: selectedValue, onChange: function (e) {
onChange(values[e.target.value]);
setValue(e.target.value);
} }, Object.keys(values).map(function (element) { return (React.createElement("option", { key: element, value: element }, element)); }))) : (React.createElement("input", __assign({}, inputProps, { checked: inputProps.value, className: boxClassname, onChange: function (e) { return onChange(e.target.value); } })))),
children));
};
var Previewer = function (_a) {
var className = _a.className, borderContent = _a.borderContent, label = _a.label, children = _a.children, screenshot = _a.screenshot, header = _a.header, _b = _a.startColor, startColor = _b === void 0 ? "#FFFFFF" : _b, _c = _a.id, id = _c === void 0 ? String(Math.random()) : _c;
var _d = React.useState(startColor), color = _d[0], setColor = _d[1];
return (React.createElement("div", { className: className },
header && React.createElement("p", { className: "previewer-header" }, header),
React.createElement("div", { className: "previewer-container" },
React.createElement("div", null,
React.createElement("p", { className: "previewer-label" }, label),
React.createElement("div", { className: "preview", id: "preview", style: {
backgroundColor: color,
} },
React.createElement("div", { className: "preview-tools" },
React.createElement("input", { type: "color", onChange: function (e) { return setColor(e.target.value); }, value: color, id: "color-picker-" + id, style: { display: "none" } }),
React.createElement("button", { className: "icon-button", onClick: function () { var _a; return (_a = document.getElementById("color-picker-" + id)) === null || _a === void 0 ? void 0 : _a.click(); } },
React.createElement("svg", { width: "60", height: "60", viewBox: "0 0 64 64" },
React.createElement("circle", { cx: "31.8", cy: "32", r: "32", fill: "#e0e0d1", className: "fill" }),
React.createElement("path", { d: "M32 12c-8.5 0-18 5.4-21.1 15.7-1.6 5.2 5.2 3.1 5.6 9.2.4 4.9-1.1 6.6-1.4 9.6-.4 4 8.9 10 17.2 9.6C44.6 55.4 54 46.3 54 34s-9.7-22-22-22zm-8 36c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z", fill: "#231f20", opacity: ".2" }),
React.createElement("path", { d: "M32 10c-8.5 0-18 5.4-21.1 15.7-1.6 5.2 5.2 3.1 5.6 9.2.4 4.9-1.1 6.6-1.4 9.6-.4 4 8.9 10 17.2 9.6C44.6 53.4 54 44.3 54 32s-9.7-22-22-22zm-8 36c-2.2 0-4-1.8-4-4s1.8-4 4-4 4 1.8 4 4-1.8 4-4 4z", fill: "#e0995e" }),
React.createElement("g", { fill: "#231f20" },
React.createElement("circle", { cx: "20.7", cy: "25", r: "4", opacity: ".2" }),
React.createElement("circle", { cx: "32", cy: "19.6", r: "4", opacity: ".2" }),
React.createElement("circle", { cx: "43.2", cy: "24.7", r: "4", opacity: ".2" }),
React.createElement("circle", { cx: "46.2", cy: "37.3", r: "4", opacity: ".2" }),
React.createElement("circle", { cx: "37.1", cy: "47.1", r: "4", opacity: ".2" })),
React.createElement("circle", { cx: "20.7", cy: "23", r: "4", fill: "#c75c5c" }),
React.createElement("circle", { cx: "32", cy: "17.6", r: "4", fill: "#f5cf87" }),
React.createElement("circle", { cx: "43.2", cy: "22.7", r: "4", fill: "#76c2af" }),
React.createElement("circle", { cx: "46.2", cy: "35.3", r: "4", fill: "#fff" }),
React.createElement("circle", { cx: "37.1", cy: "45.1", r: "4", fill: "#4f5d73" }))),
screenshot && (React.createElement("button", { className: "icon-button", onClick: function () {
takeScreenshot(document.getElementById(id), color);
} },
React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "60", height: "48", viewBox: "0 0 45 36" },
React.createElement("path", { d: "M42.117 5.215c.043-.121.07-.152.07-.285 0-.594-.473-.984-1.055-.984h-2.461c-.578 0-1.055.48-1.055 1.074 0 .125.023 0 .063.359h-5.023l-1.543-3.242C30.555.957 29.051 0 27.766 0H17.234c-1.285 0-2.785.957-3.348 2.137l-1.543 3.242h-1.445V3.762c0-.195-.156-.176-.352-.176H3.516c-.195 0-.352-.02-.352.176v1.438C1.406 5.199 0 6.645 0 8.426v24.379c0 1.781 1.418 3.406 3.164 3.406h38.672c1.746 0 3.164-1.625 3.164-3.406V8.426c0-1.684-1.27-3.066-2.883-3.211zM28.125 4.66c0 .199-.156.359-.352.359H17.227c-.195 0-.352-.16-.352-.359V3.227c0-.195.156-.359.352-.359h10.547c.195 0 .352.16.352.359zM4.922 12.367c-1.164 0-2.109-.961-2.109-2.148s.949-2.152 2.109-2.152 2.109.965 2.109 2.152-.945 2.148-2.109 2.148zm2.813 1.793c-.578 0-1.055-.48-1.055-1.074s.473-1.074 1.055-1.074a1.07 1.07 0 0 1 1.055 1.074c0 .594-.473 1.074-1.055 1.074zM22.5 33.164c-6.785 0-12.305-5.629-12.305-12.547S15.715 8.066 22.5 8.066s12.305 5.633 12.305 12.547S29.289 33.164 22.5 33.164zm0-22.59c-5.426 0-9.844 4.504-9.844 10.043s4.418 10.035 9.844 10.035 9.844-4.5 9.844-10.039S27.93 10.574 22.5 10.574zm0 15.418c-2.906 0-5.273-2.414-5.273-5.375s2.367-5.379 5.273-5.379 5.273 2.41 5.273 5.379-2.367 5.375-5.273 5.375zm0 0", fill: "#373f4e", className: "fill" }))))),
React.createElement("div", { className: "preview-screen", id: id }, children)),
React.createElement("div", { className: "border" }, borderContent)))));
};
/**
*
* @param props input demo props for the component. For every demo prop (excluding the hidden props) will be rendered a UI element (Box),
* to change every props's values in real time
*
* @param rows the UI schema matrix for the rendered Boxes (one for every props specified with the props parameter). If not set, all UI props's elements
* will be rendered in one line, on the Previewer's bottom border. If set, every array element (an array, as it must be an array of arrays) will be
* rendered as a row. Every string inside this matrix should be the name of one of the props specified, as it will render the associated prop, into the related row.
* The strings that are not valid props's names, won't be included
*
* @param debug if set, it shows useful debug infos, for example the props values as they are, printed from the state
*
* @param screenshot (extended from Previewer component) if set, enable the screenshot feature, that generate a png image of the wrapped component
* (useful for presentations)
*
* @param header (extended from Previewer component) the internal Previewer title, showed upon the Demo component
*
* @param label (extended from Previewer component) the internal Previewer label, showed inside the Previewer component, on the top border
*
* @param id (extended from Previewer component) a custom id applied as a key to the main component. If no specified, a random one will be generated It is mostly used to prevent
* React warnings and errors (also, this prevent unexpected re-renderings)
*
* @param className a custom className applied on the main component's container
*
* @param children The component to wrap. The behaviour is different, based on its type:
* - JSX.ELement: Basically, the Demo component use the Previewer component internally (it can be used as a Previewer,
* as they share most of the props), so the component can be a simple JSX.Element. in this case, it will be rendered as is, without the normal
* Demo component's flow (that change the previewed component as the props's values change).
*
*
* - Render Function (BaseCallback type): if the children is a function (with 2 arguments, the props and a function to change them), it will be
* connected to the Demo component's flow. In this case, be sure to apply those given props (that are derived essentially from the `props` parameter)
* to the final component
*
* @returns a Demo component that wrap the given children component
*/
var Demo = function (_a) {
var _b = _a.props, inputProps = _b === void 0 ? {} : _b, rows = _a.rows, children = _a.children, debug = _a.debug, previewerProps = __rest(_a, ["props", "rows", "children", "debug"]);
var _c = React.useState(getProps(inputProps)), props = _c[0], setProps = _c[1];
var boxRows = rows ? rows : [Object.keys(inputProps)];
return (React.createElement(Previewer, __assign({}, previewerProps, { borderContent: React.createElement("div", { key: "demo_rows" }, boxRows.map(function (row, index) { return (React.createElement("div", { className: "demo-row", key: "demo_row_" + index }, row.map(function (prop) {
return (React.createElement("div", { key: prop },
React.createElement(Box, { id: prop, label: prop, hide: inputProps[prop].hide, type: inputProps[prop].type, value: props[prop], onChange: function (value) {
var _a;
return setProps(merge(props, (_a = {},
_a[prop] = formatValue(value, inputProps[prop].type),
_a)));
}, values: inputProps[prop].values })));
}))); })) }),
React.createElement("div", { className: "demo-container", key: "demo_container" },
React.createElement("div", { key: "demo_children" }, typeof children === "function"
? children(props, function (newProps) { return setProps(merge(props, newProps)); })
: children),
debug && (React.createElement("div", { key: "props-debug", className: "props-container" },
"{",
Object.keys(props).map(function (prop) { return (React.createElement("div", { className: "prop", key: prop },
React.createElement("label", { className: "name" }, prop + " : "),
React.createElement("label", { className: "value" }, "" + props[prop]),
",")); }),
"}")))));
};
var Prop = function (type, value, values, hide, min, max, callback) {
if (hide === void 0) { hide = false; }
if (min === void 0) { min = 0; }
if (max === void 0) { max = 100; }
return ({
type: type,
value: value,
values: values,
hide: hide,
min: min,
max: max,
callBack: callback,
});
};
var HiddenProp = function (value) {
return Prop("text", value, null, true);
};
var StringProp = function (value) { return Prop("text", value); };
var SelectProp = function (value) {
return Prop("select", value[Object.keys(value)[0]], value);
};
var NumberProp = function (value) { return Prop("number", value); };
var BooleanProp = function (value) {
return Prop("checkbox", value);
};
var RangeProp = function (value, min, max) {
if (value === void 0) { value = 0; }
if (min === void 0) { min = 0; }
if (max === void 0) { max = 100; }
return Prop("range", value, null, false, min, max);
};
var ColorProp = function (value) {
if (value === void 0) { value = "#ffffff"; }
return Prop("color", value);
};
export { BooleanProp, Box, ColorProp, Demo, HiddenProp, NumberProp, Previewer, RangeProp, SelectProp, StringProp, downloadFile, formatValue, getProps, merge, takeScreenshot };