UNPKG

romaine-components

Version:
380 lines (364 loc) 22.3 kB
import React, { useReducer, useEffect, useState, useCallback } from 'react'; import { useRomaine } from 'romaine'; /*! ***************************************************************************** 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. ***************************************************************************** */ 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; } const FolderSelection = (_a) => { var { handleDrop, getFiles, children, image, outline = true, menu = false } = _a, props = __rest(_a, ["handleDrop", "getFiles", "children", "image", "outline", "menu"]); const initialInputStyles = { display: "none", }; const initialLabelStyles = { position: "relative", display: "grid", placeItems: "center", width: "100%", height: "100%", outline: outline ? "thin solid black" : undefined, }; const initialDivStyles = { position: "absolute", inset: 0, zIndex: 2000, backgroundColor: "white", }; const hoverInputStyles = { position: "absolute", top: 0, right: 0, bottom: 0, left: 0, display: "block", visibility: "hidden", }; const hoverLabelStyles = { position: "relative", display: "grid", placeItems: "center", width: "100%", height: "100%", outline: outline ? "thin solid black" : undefined, backgroundColor: "lightblue", }; const hoverDivStyles = { position: "absolute", inset: 0, backgroundColor: "white", zIndex: 2000, }; const finalInputStyles = { position: "absolute", top: 0, right: 0, bottom: 0, left: 0, display: "block", visibility: "hidden", }; const finalLabelStyles = { position: "relative", display: "grid", placeItems: "center", width: "100%", height: "100%", outline: outline ? "thin solid black" : undefined, cursor: "pointer", }; const finalDivStyles = { display: menu ? "hidden" : "flex", position: "absolute", right: 0, bottom: 0, zIndex: 300, width: "240px", }; const initialState = { inputStyles: initialInputStyles, labelStyles: initialLabelStyles, divStyles: initialDivStyles, }; const reducer = (state, action) => { if (action === "initial") return { inputStyles: initialInputStyles, labelStyles: initialLabelStyles, divStyles: initialDivStyles, }; if (action === "hover") return { inputStyles: hoverInputStyles, labelStyles: hoverLabelStyles, divStyles: hoverDivStyles, }; if (action === "final") return { inputStyles: finalInputStyles, labelStyles: finalLabelStyles, divStyles: finalDivStyles, }; return Object.assign({}, state); }; const [state, dispatch] = useReducer(reducer, initialState); useEffect(() => { if (image) dispatch("final"); const preventDefault = (event) => { event.preventDefault(); }; const preventDefaultDrop = (event) => { dispatch("final"); event.preventDefault(); }; const dragEnter = () => { dispatch("hover"); }; const dragExit = () => { dispatch(image ? "final" : "initial"); }; window.addEventListener("dragover", preventDefault); window.addEventListener("drop", preventDefaultDrop); window.addEventListener("dragenter", dragEnter); window.addEventListener("dragexit", dragExit); return () => { window.removeEventListener("dragover", preventDefault); window.removeEventListener("drop", preventDefault); window.removeEventListener("dragenter", dragEnter); window.removeEventListener("dragexit", dragExit); }; // @eslint-disable-next-line react-hooks/exhaustive-deps }, [image]); //https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop function dropHandler(ev) { ev.preventDefault(); const files = ev.dataTransfer ? ev.dataTransfer.files : null; handleDrop && handleDrop(files, ev); getFiles && getFiles(files); dispatch("initial"); } const [inputValue, setInputValue] = useState(); return (React.createElement("div", { onDrop: dropHandler, style: state.divStyles }, React.createElement("label", { id: "drop_zone", htmlFor: "romaine-folder-input", style: state.labelStyles }, React.createElement("input", Object.assign({ type: "file", value: inputValue }, props, { style: Object.assign(Object.assign({}, state.inputStyles), props.style), id: "romaine-folder-input", onChange: (e) => { props.onChange && props.onChange(e); setInputValue(e.target.value); getFiles && getFiles(e.target.files); dispatch("final"); } })), children))); }; const IconWrapper = (_a) => { var { selected, children, tooltip, disabled = false } = _a, props = __rest(_a, ["selected", "children", "tooltip", "disabled"]); const { romaine: { mode }, } = useRomaine(); const [hover, sethover] = useState(false); return (React.createElement("abbr", { style: { cursor: disabled ? "not-allowed" : "pointer", display: "block", width: "100%", height: "100%", }, title: tooltip, "aria-label": tooltip }, React.createElement("button", Object.assign({ onMouseEnter: () => sethover(true), onMouseLeave: () => sethover(false) }, props, { style: Object.assign({ border: selected === true || selected === mode || hover ? "thin solid black" : "thin solid transparent", borderRadius: "4px", backgroundColor: hover ? "#888" : selected === true || selected === mode ? "#555" : "#fff0", display: "grid", placeItems: "center", height: "100%", width: "100%", pointerEvents: disabled ? "none" : "all" }, props.style) }), children))); }; const CropperIcon = (props) => { var _a, _b; const { setMode } = useRomaine(); useEffect(() => { // using keydown because it already requires another key to be pressed const eventListenerCropper = (e) => { if (!e.ctrlKey && e.shiftKey && e.key === "C") { e.preventDefault(); setMode === null || setMode === void 0 ? void 0 : setMode("crop"); } }; window.removeEventListener("keydown", eventListenerCropper); window.addEventListener("keydown", eventListenerCropper); return () => { window.removeEventListener("keydown", eventListenerCropper); }; }, [setMode]); return (React.createElement(IconWrapper, Object.assign({}, props, { onClick: () => setMode && setMode("crop"), selected: "crop", tooltip: "Crop Tool (Shift + C)" }), React.createElement("svg", { stroke: "currentColor", fill: "none", strokeWidth: "2", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("path", { d: "M6.13 1L6 16a2 2 0 0 0 2 2h15" }), React.createElement("path", { d: "M1 6.13L16 6a2 2 0 0 1 2 2v15" })))); }; /** * @copyright The SVG comes from: Remix Icons https://remixicon.com/ * @license `Apache 2.0` */ const FullReset = (props) => { var _a, _b; const { setMode, romaine: { history: { pointer, commands: { length }, }, }, } = useRomaine(); // using keydown because it already requires another key to be pressed const eventListenerCropper = useCallback((e) => { if (e.ctrlKey && e.shiftKey && e.key === "Z") { e.preventDefault(); pointer && window.confirm("Are you sure you want to reset this image? All current cropping progress will be lost.") && (setMode === null || setMode === void 0 ? void 0 : setMode("full-reset")); } }, [pointer, setMode]); useEffect(() => { window.removeEventListener("keydown", eventListenerCropper); window.addEventListener("keydown", eventListenerCropper); return () => { window.removeEventListener("keydown", eventListenerCropper); }; }, [eventListenerCropper]); return (React.createElement(IconWrapper, Object.assign({}, props, { onClick: () => window.confirm("Are you sure you want to reset this image? All current cropping progress will be lost.") && setMode && setMode("full-reset"), selected: "full-reset", tooltip: "Reinitialize Image (Ctrl + Shift + Z)", disabled: !length }), React.createElement("svg", { stroke: "currentColor", fill: "currentColor", strokeWidth: "0", viewBox: "0 0 24 24", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("g", null, React.createElement("path", { fill: "none", d: "M0 0h24v24H0z" }), React.createElement("path", { d: "M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm4.82-4.924A7 7 0 0 0 9.032 5.658l.975 1.755A5 5 0 0 1 17 12h-3l2.82 5.076zm-1.852 1.266l-.975-1.755A5 5 0 0 1 7 12h3L7.18 6.924a7 7 0 0 0 7.788 11.418z" }))))); }; const PerspectiveIcon = (props) => { var _a, _b; const { setMode } = useRomaine(); useEffect(() => { // using keydown because it already requires another key to be pressed const eventListenerPerspective = (e) => { if (e.ctrlKey && e.shiftKey && e.key === "C") { e.preventDefault(); setMode && setMode("perspective-crop"); } }; window.removeEventListener("keydown", eventListenerPerspective); window.addEventListener("keydown", eventListenerPerspective); return () => { window.removeEventListener("keydown", eventListenerPerspective); }; }, [setMode]); return (React.createElement(IconWrapper, Object.assign({}, props, { onClick: () => setMode && setMode("perspective-crop"), selected: "perspective-crop", tooltip: "Perspective Cropper (Ctrl + Shift + C)" }), React.createElement("svg", { stroke: "currentColor", fill: "currentColor", strokeWidth: "0", viewBox: "0 0 512 512", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("path", { d: "M208 151c-28 0-60.378 17.102-90.992 37.512-30.615 20.41-58.84 44.594-75.37 61.125L35.27 256l6.367 6.363c16.53 16.53 44.756 40.716 75.37 61.125C147.623 343.898 180 361 208 361h3.73l2.633-2.637c8.644-8.643 15.787-18.62 21.49-29.47-20.47-1.078-36.916-10.11-47.767-23.13C176.512 291.872 171 273.874 171 256c0-17.875 5.512-35.873 17.086-49.762 10.85-13.02 27.297-22.053 47.768-23.13-5.704-10.85-12.847-20.828-21.49-29.47L211.728 151H208zm32 50c-17.6 0-29.66 6.65-38.086 16.762C193.488 227.872 189 241.875 189 256s4.488 28.127 12.914 38.238C210.34 304.348 222.4 311 240 311h3.7c1.89-5.276 3.485-10.685 4.796-16.182-2.5 1.36-5.324 2.182-8.496 2.182-9.282 0-15.65-6.92-19.363-14.348-3.715-7.428-5.637-16.6-5.637-26.652 0-10.053 1.922-19.224 5.637-26.652C224.35 221.918 230.717 215 240 215c3.172 0 5.995.822 8.496 2.182-1.31-5.497-2.905-10.906-4.797-16.182H240zm176 7v32h-96v32h96v32l48-48-48-48zm-168 16a8 16 0 0 0-8 16 8 16 0 0 0 8 16 8 16 0 0 0 8-16 8 16 0 0 0-8-16z" })))); }; /** * @todo * 1) Need the ability to change angle for rotation * 1) Need to add angle of rotation to the Romaine context and create a reducer helper to change value * * * This needs to be done in romaine not romaine-components (here) * 2) Need to dynamically add an input that can change the angle of rotation * @copyright The SVG comes from: Ant Design Icons https://github.com/ant-design/ant-design-icons * @license MIT */ const RotateLeft = (props) => { var _a, _b; const { setMode } = useRomaine(); return (React.createElement(IconWrapper, Object.assign({}, props, { onClick: () => setMode && setMode("rotate-left"), selected: "rotate-left" }), React.createElement("svg", { stroke: "currentColor", fill: "currentColor", strokeWidth: "0", viewBox: "0 0 1024 1024", version: "1.1", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("defs", null), React.createElement("path", { d: "M672 418H144c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H188V494h440v326z" }), React.createElement("path", { d: "M819.3 328.5c-78.8-100.7-196-153.6-314.6-154.2l-0.2-64c0-6.5-7.6-10.1-12.6-6.1l-128 101c-4 3.1-3.9 9.1 0 12.3L492 318.6c5.1 4 12.7 0.4 12.6-6.1v-63.9c12.9 0.1 25.9 0.9 38.8 2.5 42.1 5.2 82.1 18.2 119 38.7 38.1 21.2 71.2 49.7 98.4 84.3 27.1 34.7 46.7 73.7 58.1 115.8 11 40.7 14 82.7 8.9 124.8-0.7 5.4-1.4 10.8-2.4 16.1h74.9c14.8-103.6-11.3-213-81-302.3z" })))); }; /** * @copyright Ant Design Icons https://github.com/ant-design/ant-design-icons * @license MIT */ const RotateRight = (props) => { var _a, _b; const { setMode } = useRomaine(); return (React.createElement(IconWrapper, Object.assign({ selected: "rotate-right" }, props, { onClick: () => setMode && setMode("rotate-right") }), React.createElement("svg", { stroke: "currentColor", fill: "currentColor", strokeWidth: "0", viewBox: "0 0 1024 1024", version: "1.1", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("defs", null), React.createElement("path", { d: "M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-0.4-12.6 6.1l-0.2 64c-118.6 0.5-235.8 53.4-314.6 154.2-69.6 89.2-95.7 198.6-81.1 302.4h74.9c-0.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8z" }), React.createElement("path", { d: "M880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H396V494h440v326z" })))); }; const UndoIcon = (props) => { var _a, _b; const { setMode, romaine: { history: { pointer }, }, } = useRomaine(); // using keydown because it already requires another key to be pressed const eventListenerCropper = useCallback((e) => { if (e.ctrlKey && /z/i.test(e.key)) { e.preventDefault(); pointer && (setMode === null || setMode === void 0 ? void 0 : setMode("undo")); } }, [pointer, setMode]); useEffect(() => { window.removeEventListener("keydown", eventListenerCropper); window.addEventListener("keydown", eventListenerCropper); return () => { window.removeEventListener("keydown", eventListenerCropper); }; }, [eventListenerCropper]); return (React.createElement(IconWrapper, Object.assign({}, props, { onClick: () => setMode === null || setMode === void 0 ? void 0 : setMode("undo"), selected: "undo", tooltip: "Undo (Ctrl + Z)", disabled: !pointer }), React.createElement("svg", { stroke: "currentColor", fill: "currentColor", strokeWidth: "0", viewBox: "0 0 1024 1024", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("path", { d: "M511.4 124C290.5 124.3 112 303 112 523.9c0 128 60.2 242 153.8 315.2l-37.5 48c-4.1 5.3-.3 13 6.3 12.9l167-.8c5.2 0 9-4.9 7.7-9.9L369.8 727a8 8 0 0 0-14.1-3L315 776.1c-10.2-8-20-16.7-29.3-26a318.64 318.64 0 0 1-68.6-101.7C200.4 609 192 567.1 192 523.9s8.4-85.1 25.1-124.5c16.1-38.1 39.2-72.3 68.6-101.7 29.4-29.4 63.6-52.5 101.7-68.6C426.9 212.4 468.8 204 512 204s85.1 8.4 124.5 25.1c38.1 16.1 72.3 39.2 101.7 68.6 29.4 29.4 52.5 63.6 68.6 101.7 16.7 39.4 25.1 81.3 25.1 124.5s-8.4 85.1-25.1 124.5a318.64 318.64 0 0 1-68.6 101.7c-7.5 7.5-15.3 14.5-23.4 21.2a7.93 7.93 0 0 0-1.2 11.1l39.4 50.5c2.8 3.5 7.9 4.1 11.4 1.3C854.5 760.8 912 649.1 912 523.9c0-221.1-179.4-400.2-400.6-399.9z" })))); }; const FlipHorizontalIcon = (props) => { var _a, _b; const { setMode } = useRomaine(); useEffect(() => { // using keydown because it already requires another key to be pressed const eventListenerFlipHorizontal = (e) => { if (e.ctrlKey && e.shiftKey && e.key === "_") { e.preventDefault(); setMode === null || setMode === void 0 ? void 0 : setMode("flip-horizontal"); } }; window.removeEventListener("keydown", eventListenerFlipHorizontal); window.addEventListener("keydown", eventListenerFlipHorizontal); return () => { window.removeEventListener("keydown", eventListenerFlipHorizontal); }; }, [setMode]); return (React.createElement(IconWrapper, Object.assign({}, props, { onClick: () => setMode === null || setMode === void 0 ? void 0 : setMode("flip-horizontal"), selected: "flip-horizontal", tooltip: "Flip Horizontal (Ctrl + Shift + _)" }), React.createElement("svg", { stroke: "currentColor", fill: "none", strokeWidth: "2", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }), React.createElement("path", { d: "M3 12l18 0" }), React.createElement("path", { d: "M7 16l10 0l-10 5l0 -5" }), React.createElement("path", { d: "M7 8l10 0l-10 -5l0 5" })))); }; const FlipVerticalIcon = (props) => { var _a, _b; const { setMode } = useRomaine(); useEffect(() => { // using keydown because it already requires another key to be pressed const eventListenerFlipVertical = (e) => { if (e.ctrlKey && e.shiftKey && e.key === "|") { e.preventDefault(); setMode === null || setMode === void 0 ? void 0 : setMode("flip-vertical"); } }; window.removeEventListener("keydown", eventListenerFlipVertical); window.addEventListener("keydown", eventListenerFlipVertical); return () => { window.removeEventListener("keydown", eventListenerFlipVertical); }; }, [setMode]); return (React.createElement(IconWrapper, Object.assign({}, props, { onClick: () => setMode === null || setMode === void 0 ? void 0 : setMode("flip-vertical"), selected: "flip-vertical", tooltip: "Flip Vertical (Ctrl + Shift + |)" }), React.createElement("svg", { stroke: "currentColor", fill: "none", strokeWidth: "2", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", height: ((_a = props.style) === null || _a === void 0 ? void 0 : _a.height) || "25px", width: ((_b = props.style) === null || _b === void 0 ? void 0 : _b.width) || "25px", xmlns: "http://www.w3.org/2000/svg" }, React.createElement("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }), React.createElement("path", { d: "M12 3l0 18" }), React.createElement("path", { d: "M16 7l0 10l5 0l-5 -10" }), React.createElement("path", { d: "M8 7l0 10l-5 0l5 -10" })))); }; export { CropperIcon, FlipHorizontalIcon, FlipVerticalIcon, FolderSelection, FullReset, IconWrapper, PerspectiveIcon, RotateLeft, RotateRight, UndoIcon }; //# sourceMappingURL=index.jsx.map