UNPKG

@onesy/ui-react

Version:
555 lines (554 loc) 29.4 kB
"use strict"; var __rest = (this && this.__rest) || function (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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const utils_1 = require("@onesy/utils"); const style_react_1 = require("@onesy/style-react"); const Surface_1 = __importDefault(require("../Surface")); const utils_2 = require("../utils"); const useStyle = (0, style_react_1.style)(theme => ({ root: { width: '100vw' }, size_small: { maxWidth: '180px' }, size_regular: { maxWidth: '240px' }, size_large: { maxWidth: '300px' }, boundary_1: { aspectRatio: '1' }, boundary_075: { aspectRatio: '1' }, boundary_05: { aspectRatio: '1' }, boundary_025: { aspectRatio: '1' }, label: Object.assign(Object.assign({}, theme.typography.values.b2), { textAnchor: 'middle', alignmentBaseline: 'central', dominantBaseline: 'central' }), svg: { position: 'relative', width: '100%', height: 'auto' } }), { name: 'onesy-RoundMeter' }); const RoundMeter = react_1.default.forwardRef((props_, ref) => { const theme = (0, style_react_1.useOnesyTheme)(); const props = react_1.default.useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; return (Object.assign(Object.assign(Object.assign({}, (_d = (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _a === void 0 ? void 0 : _a.elements) === null || _b === void 0 ? void 0 : _b.all) === null || _c === void 0 ? void 0 : _c.props) === null || _d === void 0 ? void 0 : _d.default), (_h = (_g = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _e === void 0 ? void 0 : _e.elements) === null || _f === void 0 ? void 0 : _f.onesyRoundMeter) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props_)); }, [props_]); const Surface = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Surface) || Surface_1.default; }, [theme]); const { tonal = true, color = 'primary', size = 'regular', parts: parts_ = 1, lineCap, padding: outsidePadding = 0, gap: gap_ = 0, border = false, background = false, boundary: boundary_ = 1, boundaryWidth = 1, arcProgress = false, arcsVisible = true, marksVisible = true, labelsVisible = true, marks: marks_ = [], markSize = 4, markWidth = 1, labels: labels_ = [], renderLabel, childrenPosition = 'post', additional, textProps, pathProps, SvgProps, MarkProps, LabelProps, BackgroundProps, BorderProps, ArcProps, ArcMainProps, ArcsProgressProps, ArcProgressProps, Component = 'div', className, style, children } = props, other = __rest(props, ["tonal", "color", "size", "parts", "lineCap", "padding", "gap", "border", "background", "boundary", "boundaryWidth", "arcProgress", "arcsVisible", "marksVisible", "labelsVisible", "marks", "markSize", "markWidth", "labels", "renderLabel", "childrenPosition", "additional", "textProps", "pathProps", "SvgProps", "MarkProps", "LabelProps", "BackgroundProps", "BorderProps", "ArcProps", "ArcMainProps", "ArcsProgressProps", "ArcProgressProps", "Component", "className", "style", "children"]); const { classes } = useStyle(); const refs = { root: react_1.default.useRef(undefined) }; const styles = { root: {} }; let radius; const boundary = (0, utils_1.parse)(boundary_); const width = 240; const height = width; let gap = ['round', 'square'].includes(lineCap) ? gap_ + (boundaryWidth / 2) : gap_; const parts = (0, utils_1.clamp)((0, utils_1.parse)(parts_), 1, 180); let min = 0; let max = 360; let yViewBox = 0; // 1 if (boundary === 1) { // 0 is middle top // ie. 270 degreese min = 270; max = 270 + 360; } // 0.75 if (boundary === 0.75) { // 0 is angle bottom left // ie. 270 degreese min = 135; max = 135 + 270; yViewBox = -15; } // 0.5 if (boundary === 0.5) { // 0 is left // ie. 180 degreese min = 180; max = 180 + 180; yViewBox = -50; } // 0.25 if (boundary === 0.25) { // 0 is angle top left // ie. 225 degreese min = 225; max = 225 + 90; yViewBox = -60; } if (!['small', 'regular', 'large'].includes(size)) styles.root.maxWidth = size; const marks = react_1.default.useMemo(() => { const values = []; if (marks_.length) { const center = (0, utils_2.toNumber)(width / 2); radius = (0, utils_2.toNumber)((width / 2) - boundaryWidth - outsidePadding); let marksValues = marks_; if (!(0, utils_1.is)('array', marksValues[0])) marksValues = [marksValues]; marksValues.forEach((marksValue, index) => { values[index] = []; marksValue.forEach((mark) => { const { size: size_, padding: markPadding = 0, position } = mark, other_ = __rest(mark, ["size", "padding", "position"]); const itemPadding = (0, utils_2.toNumber)(markPadding); const angle = (0, utils_1.valueFromPercentageWithinRange)(position, min, max); const start = (0, utils_2.angleToCoordinates)(angle, center, center, radius - itemPadding); const end = (0, utils_2.angleToCoordinates)(angle, center, center, radius - (size_ !== undefined ? size_ : markSize) - itemPadding); values[index].push(Object.assign({ d: [ 'M', start.x, start.y, 'L', end.x, end.y ].join(' ') }, other_)); }); }); } return values; }, [width, height, parts, marks_, markSize, boundary, boundaryWidth, lineCap, outsidePadding, gap]); const labels = react_1.default.useMemo(() => { var _a; const values = []; if (labels_.length) { const center = (0, utils_2.toNumber)(width / 2); const marksPadding = (0, utils_2.toNumber)((marks_ === null || marks_ === void 0 ? void 0 : marks_.length) ? ((_a = (marks_ || []).sort((a, b) => b.size - a.size)[0]) === null || _a === void 0 ? void 0 : _a.size) || markSize : 0); radius = (0, utils_2.toNumber)((width / 2) - boundaryWidth - marksPadding - outsidePadding); let labelsValues = labels_; if (!(0, utils_1.is)('array', labelsValues[0])) labelsValues = [labelsValues]; labelsValues.forEach((labelsValue, index) => { values[index] = []; labelsValue.forEach((label) => { var _a; const { value, padding: labelPadding = 0, position } = label, other_ = __rest(label, ["value", "padding", "position"]); const itemPadding = (0, utils_2.toNumber)(labelPadding); const fontSize = (0, utils_2.toNumber)(((_a = label.style) === null || _a === void 0 ? void 0 : _a.fontSize) !== undefined ? label.style.fontSize : 14); const angle = (0, utils_1.valueFromPercentageWithinRange)(position, min, max); const start = (0, utils_2.angleToCoordinates)(angle, center, center, radius - (fontSize / 2) - itemPadding); values[index].push(Object.assign({ x: start.x, y: start.y, value }, other_)); }); }); } return values; }, [width, height, parts, marks_, markSize, boundary, boundaryWidth, lineCap, outsidePadding, gap]); const arcs = react_1.default.useMemo(() => { const values = []; let value = []; const offset = outsidePadding; // 1 if (boundary === 1) { if (parts === 1) { radius = ((width / 2) - (boundaryWidth / 2)) - offset; values.push({ d: [ // Move 'M', offset + (boundaryWidth / 2), (width / 2) + 0.001, // Arc 'A', radius, radius, 0, 1, 0, offset + (boundaryWidth / 2), (width / 2) ].join(' ') }); } else { const center = width / 2; radius = (width / 2) - (boundaryWidth / 2) - offset; const total = 360; const part = (total - (parts * gap)) / parts; const angles = { start: (0, utils_2.angleToCoordinates)(0, center, center, radius) }; let anglePrevious = 0; for (let i = 0; i < parts; i++) { // Move to 0 deg if (i === 0) value.push( // Move to 0 deg 'M', angles.start.x, angles.start.y); const angleEnd = anglePrevious + part; angles.end = (0, utils_2.angleToCoordinates)(angleEnd, center, center, radius); angles.move = (0, utils_2.angleToCoordinates)(angleEnd + gap, center, center, radius); // Arc value.push('A', radius, radius, 0, 0, 1, angles.end.x, angles.end.y); // Move the gap if there's a gap if (gap > 0 && i < parts - 1) { value.push('M', angles.move.x, angles.move.y); anglePrevious = angleEnd + gap; } else anglePrevious = angleEnd; values.push({ d: value.join(' ') }); // Move for the next value if (i < parts - 1) { value = [ 'M', angles.move.x, angles.move.y ]; } } } } // 0.75 if (boundary === 0.75) { value = []; const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const angles = { end: (0, utils_2.angleToCoordinates)(45, center, center, radius), start: (0, utils_2.angleToCoordinates)(135, center, center, radius) }; if (parts === 1) { values.push({ d: [ // Line middle bottom 'M', angles.start.x, angles.start.y, // Arc 'A', radius, radius, 0, 1, 1, angles.end.x, angles.end.y ].join(' ') }); } else { const total = 270; const part = (total - ((parts - 1) * gap)) / parts; const angles_ = { 0: (0, utils_2.angleToCoordinates)(135, center, center, radius) }; let anglePrevious = 135; for (let i = 0; i < parts; i++) { // Move to 135 deg if (i === 0) value.push( // Move to 0 deg 'M', angles_[0].x, angles_[0].y); const angleEnd = anglePrevious + part; angles_.end = (0, utils_2.angleToCoordinates)(angleEnd, center, center, radius); angles_.move = (0, utils_2.angleToCoordinates)(angleEnd + gap, center, center, radius); // Arc value.push('A', radius, radius, 0, 0, 1, angles_.end.x, angles_.end.y); // Move the gap if there's a gap if (gap > 0 && i < parts - 1) { value.push('M', angles_.move.x, angles_.move.y); anglePrevious = angleEnd + gap; } else anglePrevious = angleEnd; values.push({ d: value.join(' ') }); // Move for the next value if (i < parts - 1) { value = [ 'M', angles_.move.x, angles_.move.y ]; } } } } // 0.5 if (boundary === 0.5) { value = []; const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const total = 180; const part = (total - ((parts - 1) * gap)) / parts; const angles = { start: (0, utils_2.angleToCoordinates)(180, center, center, radius) }; let anglePrevious = 180; for (let i = 0; i < parts; i++) { // Move to 180 deg if (i === 0) value.push( // Move to 0 deg 'M', angles.start.x, angles.start.y); const angleEnd = anglePrevious + part; angles.end = (0, utils_2.angleToCoordinates)(angleEnd, center, center, radius); angles.move = (0, utils_2.angleToCoordinates)(angleEnd + gap, center, center, radius); // Arc value.push('A', radius, radius, 0, 0, 1, angles.end.x, angles.end.y); // Move the gap if there's a gap if (gap > 0 && i < parts - 1) { value.push('M', angles.move.x, angles.move.y); anglePrevious = angleEnd + gap; } else anglePrevious = angleEnd; values.push({ d: value.join(' ') }); // Move for the next value if (i < parts - 1) { value = [ 'M', angles.move.x, angles.move.y ]; } } } // 0.25 if (boundary === 0.25) { value = []; const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const total = 90; const part = (0, utils_1.clamp)((total - ((parts - 1) * gap)) / parts, 0.01); gap = (0, utils_1.clamp)(gap, 0, ((total - (part * parts)) / (parts - 1))); const angles = { start: (0, utils_2.angleToCoordinates)(225, center, center, radius) }; let anglePrevious = 225; for (let i = 0; i < parts; i++) { // Move to 225 deg if (i === 0) value.push( // Move to 0 deg 'M', angles.start.x, angles.start.y); const angleEnd = anglePrevious + part; angles.end = (0, utils_2.angleToCoordinates)(angleEnd, center, center, radius); angles.move = (0, utils_2.angleToCoordinates)(angleEnd + gap, center, center, radius); // Arc value.push('A', radius, radius, 0, 0, 1, angles.end.x, angles.end.y); // Move the gap if there's a gap if (gap > 0 && i < parts - 1) { value.push('M', angles.move.x, angles.move.y); anglePrevious = angleEnd + gap; } else anglePrevious = angleEnd; values.push({ d: value.join(' ') }); // Move for the next value if (i < parts - 1) { value = [ 'M', angles.move.x, angles.move.y ]; } } } return values; }, [width, height, parts, boundary, boundaryWidth, lineCap, outsidePadding, gap, gap_]); const pathBackground = react_1.default.useMemo(() => { const values = []; const offset = outsidePadding; // 1 if (boundary === 1) { radius = ((width / 2) - (boundaryWidth / 2)) - offset; values.push( // Move 'M', offset + (boundaryWidth / 2), (width / 2) + 0.001, // Arc 'A', radius, radius, 0, 1, 0, offset + (boundaryWidth / 2), (width / 2)); } // 0.75 if (boundary === 0.75) { const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const angles = { end: (0, utils_2.angleToCoordinates)(45, center, center, radius), start: (0, utils_2.angleToCoordinates)(135, center, center, radius) }; values.push( // Move 'M', center, center, // Line middle bottom 'L', angles.start.x, angles.start.y, // Arc 'A', radius, radius, 0, 1, 1, angles.end.x, angles.end.y, // Line bottom middle 'L', center, center, 'Z'); } // 0.5 if (boundary === 0.5) { const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const total = 180; const part = (total - ((parts - 1) * gap)) / parts; const angles = { start: (0, utils_2.angleToCoordinates)(180, center, center, radius) }; const anglePrevious = 180; const angleEnd = anglePrevious + part; angles.end = (0, utils_2.angleToCoordinates)(angleEnd, center, center, radius); angles.move = (0, utils_2.angleToCoordinates)(angleEnd + gap, center, center, radius); values.push( // Move 'M', angles.start.x, angles.start.y, // Arc 'A', radius, radius, 0, 0, 1, angles.end.x, angles.end.y, 'Z'); } // 0.25 if (boundary === 0.25) { const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const total = 90; const part = (0, utils_1.clamp)((total - ((parts - 1) * gap)) / parts, 0.01); gap = (0, utils_1.clamp)(gap, 0, ((total - (part * parts)) / (parts - 1))); const angles = { start: (0, utils_2.angleToCoordinates)(225, center, center, radius) }; const anglePrevious = 225; const angleEnd = anglePrevious + part; angles.end = (0, utils_2.angleToCoordinates)(angleEnd, center, center, radius); angles.move = (0, utils_2.angleToCoordinates)(angleEnd + gap, center, center, radius); values.push( // Move 'M', center, ((width / 2) - boundaryWidth), // Line middle bottom, top quarter left 'L', angles.start.x, angles.start.y, // Arc 'A', radius, radius, 0, 0, 1, angles.end.x, angles.end.y, // Line top quarter right, middle bottom 'L', center, ((width / 2) - boundaryWidth), 'Z'); } return values.join(' '); }, [width, height, boundary, boundaryWidth, outsidePadding]); const pathBorder = react_1.default.useMemo(() => { const values = []; const offset = outsidePadding; // 0.75 if (boundary === 0.75) { const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const angles = { end: (0, utils_2.angleToCoordinates)(45, center, center, radius), start: (0, utils_2.angleToCoordinates)(135, center, center, radius) }; values.push( // Move bottom angle left 'M', angles.start.x, angles.start.y, // Line middle 'L', center, center, // Line bottom angle right 'L', angles.end.x, angles.end.y); } // 0.5 if (boundary === 0.5) { const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const angles = { start: (0, utils_2.angleToCoordinates)(180, center, center, radius) }; values.push( // Move 'M', angles.start.x, angles.start.y, // Line 'L', (width - (boundaryWidth / 2)) - offset, angles.start.y); } // 0.25 if (boundary === 0.25) { const center = width / 2; radius = ((width / 2) - (boundaryWidth / 2)) - offset; const total = 90; const part = (0, utils_1.clamp)((total - ((parts - 1) * gap)) / parts, 0.01); gap = (0, utils_1.clamp)(gap, 0, ((total - (part * parts)) / (parts - 1))); const angles = { start: (0, utils_2.angleToCoordinates)(225, center, center, radius), end: (0, utils_2.angleToCoordinates)(315, center, center, radius) }; values.push( // Move middle bottom, top quarter left 'M', angles.start.x, angles.start.y, // Line middle bottom 'L', center, ((width / 2) - boundaryWidth), // Arc top quarter right, middle bottom 'L', angles.end.x, angles.end.y); } return values.join(' '); }, [width, height, boundary, boundaryWidth, outsidePadding]); const children_ = children && ((0, jsx_runtime_1.jsx)("g", Object.assign({ className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-children' ], classes.children ]) }, { children: react_1.default.Children.toArray(children).map((item, index) => { return (react_1.default.cloneElement(item, { key: index, fill: item.props.fill !== undefined ? item.props.fill : color, stroke: item.props.stroke !== undefined ? item.props.stroke : color, // clean up value: undefined, style: Object.assign(Object.assign({}, (item.props.value !== undefined ? { transform: `rotate(${(0, utils_1.valueFromPercentageWithinRange)(item.props.value, min, max)}deg)` } : undefined)), item.props.style) })); }) }))); return ((0, jsx_runtime_1.jsxs)(Component, Object.assign({ ref: item => { if (ref) { if ((0, utils_1.is)('function', ref)) ref(item); else ref.current = item; } refs.root.current = item; }, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-root', `onesy-RoundMeter-size-${size}` ], className, classes.root, classes[`size_${size}`], classes[`boundary_${String(boundary).replace('.', '')}`] ]), style: Object.assign(Object.assign({}, styles.root), style) }, other, { children: [additional, (0, jsx_runtime_1.jsx)(Surface, Object.assign({ tonal: tonal, color: color }, { children: ({ color: color_, backgroundColor }) => ((0, jsx_runtime_1.jsxs)("svg", Object.assign({ xmlns: 'http://www.w3.org/2000/svg', viewBox: `0 ${yViewBox} ${width || 0} ${height || 0}` }, SvgProps, { className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-svg' ], SvgProps === null || SvgProps === void 0 ? void 0 : SvgProps.className, classes.svg ]) }, { children: [childrenPosition === 'pre' && children_, background && ((0, jsx_runtime_1.jsx)("path", Object.assign({ d: pathBackground, fill: backgroundColor, stroke: 'none' }, pathProps, BackgroundProps))), border && ((0, jsx_runtime_1.jsx)("path", Object.assign({ d: pathBorder, fill: 'none', stroke: color_, strokeWidth: boundaryWidth }, pathProps, BorderProps))), arcsVisible && ((0, jsx_runtime_1.jsx)("g", Object.assign({ className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-arcs' ], classes.arcs ]) }, { children: (arcs.map((item, index) => ((0, jsx_runtime_1.jsx)("path", Object.assign({ d: item.d, fill: 'none', stroke: color_, strokeWidth: boundaryWidth, strokeLinecap: lineCap }, pathProps, ArcProps, ArcMainProps), index)))) }))), arcsVisible && arcProgress && ((0, jsx_runtime_1.jsx)("g", Object.assign({}, ArcsProgressProps, { className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-arcs-progress' ], ArcsProgressProps === null || ArcsProgressProps === void 0 ? void 0 : ArcsProgressProps.className, classes.arcs_progress ]) }, { children: (arcs.map((item, index) => ((0, jsx_runtime_1.jsx)("path", Object.assign({ d: item.d, fill: 'none', stroke: color_, strokeWidth: boundaryWidth, strokeLinecap: lineCap }, pathProps, ArcProps, ArcProgressProps), index)))) }))), childrenPosition === 'pre-marks' && children_, marksVisible && !!marks_.length && (marks.map((marksValue, index) => ((0, jsx_runtime_1.jsx)("g", Object.assign({ className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-marks' ], classes.marks ]) }, { children: (marksValue.map((item, index_) => ((0, jsx_runtime_1.jsx)("path", Object.assign({ d: item.d, fill: 'none', stroke: color_, strokeWidth: item.width !== undefined ? item.width : markWidth, strokeLinecap: lineCap }, pathProps, MarkProps), index_)))) }), index)))), childrenPosition === 'pre-labels' && children_, labelsVisible && !!labels_.length && (labels.map((labelsValue, index) => { return ((0, jsx_runtime_1.jsx)("g", Object.assign({ className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-labels' ], classes.labels ]) }, { children: (labelsValue.map((item, index_) => { const { x, y, value } = item, other_ = __rest(item, ["x", "y", "value"]); const propsLabel = Object.assign(Object.assign(Object.assign(Object.assign({}, other_), textProps), LabelProps), { style: Object.assign(Object.assign(Object.assign({ fill: color_ }, other_.style), textProps === null || textProps === void 0 ? void 0 : textProps.style), LabelProps === null || LabelProps === void 0 ? void 0 : LabelProps.style) }); if ((0, utils_1.is)('function', renderLabel)) return renderLabel(x, y, value, propsLabel); return ((0, jsx_runtime_1.jsx)("text", Object.assign({ x: x, y: y }, propsLabel, { className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('RoundMeter', theme) && [ 'onesy-RoundMeter-label' ], other_ === null || other_ === void 0 ? void 0 : other_.className, textProps === null || textProps === void 0 ? void 0 : textProps.className, LabelProps === null || LabelProps === void 0 ? void 0 : LabelProps.className, classes.label ]) }, { children: value }), index_)); })) }), index)); })), childrenPosition === 'post' && children_] }))) }))] }))); }); RoundMeter.displayName = 'onesy-RoundMeter'; exports.default = RoundMeter;