![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mets.corals.io/wp-content/plugins/ultimate-blocks/src/blocks/how-to/ |
import { useEffect, useState } from "react"; import { convertFromSeconds } from "../../common"; const { __ } = wp.i18n; // Import __() from wp.i18n const { RichText, MediaUpload, InspectorControls } = wp.blockEditor || wp.editor; const { Button, ToggleControl, PanelBody, RadioControl, RangeControl, SelectControl, } = wp.components; function InspectorPanel(props) { const { attributes: { advancedMode, section, sectionListStyle, suppliesListStyle, toolsListStyle, showUnitFirst, useSections, includeToolsList, addToolImages, includeSuppliesList, addSupplyImages, finalImageID, finalImageWidth, finalImageFloat, firstLevelTag, secondLevelTag, thirdLevelTag, }, setAttributes, currentStep, updateState, } = props; let activeImage = { width: 0, float: "none" }; let sectionNum = -1; let stepNum = -1; const tagList = ["h1", "h2", "h3", "h4", "h5", "h6", "p", "strong"]; if (currentStep !== "") { if (currentStep === "final") { if (finalImageID > -1) { activeImage = { width: finalImageWidth, float: finalImageFloat }; } } else { const parsed = currentStep.split("-"); if (useSections) { sectionNum = parseInt(parsed[1]); stepNum = parseInt(parsed[3]); if (!isNaN(stepNum)) { //exclude review image const { width, float, id } = section[sectionNum].steps[stepNum].stepPic; if (id > -1) { activeImage = { width, float }; } } } else { stepNum = parseInt(parsed[1]); if (!isNaN(stepNum)) { //exclude review image const { width, float, id } = section[0].steps[stepNum].stepPic; if (id > -1) { activeImage = { width, float }; } } } } } return ( <InspectorControls> <PanelBody title={__("How To Settings")}> <ToggleControl label={__("Use sections")} checked={useSections} onChange={(useSections) => { setAttributes({ useSections }); if (useSections) { let newSection = JSON.parse(JSON.stringify(section)); newSection.forEach((ns, i) => ns.steps.forEach((s, j) => { s.anchor = `section${i}step${j}`; }) ); if (currentStep !== "") { updateState({ currentStep: `section-0-${currentStep}` }); } } else { updateState({ currentStep: currentStep.slice(currentStep.indexOf("step")), }); if (section.length < 1) { setAttributes({ section: [{ sectionName: "", steps: [] }], }); } else { let newSection = JSON.parse(JSON.stringify(section)); newSection[0].steps.forEach((s, i) => { s.anchor = `step${i}`; }); setAttributes({ section: newSection }); } } }} /> <ToggleControl label={__("Use additional recommended attributes")} checked={advancedMode} onChange={(advancedMode) => setAttributes({ advancedMode })} /> {advancedMode && ( <> <ToggleControl label={__("Include list of supplies")} checked={includeSuppliesList} onChange={(includeSuppliesList) => setAttributes({ includeSuppliesList }) } /> <ToggleControl label={__("Include list of tools")} checked={includeToolsList} onChange={(includeToolsList) => setAttributes({ includeToolsList }) } /> <ToggleControl label={__("Display the unit first in cost")} checked={showUnitFirst} onChange={(showUnitFirst) => setAttributes({ showUnitFirst })} /> </> )} {useSections && ( <RadioControl label={__("Section list style")} selected={sectionListStyle} options={["none", "ordered", "unordered"].map((a) => ({ label: __(a), value: a, }))} onChange={(sectionListStyle) => setAttributes({ sectionListStyle })} /> )} </PanelBody> {activeImage.width > 0 && ( <PanelBody title={__("Desktop image display settings")}> <RangeControl label={__("Image width")} value={activeImage.width} onChange={(imageWidth) => { if (currentStep === "final") { setAttributes({ finalImageWidth: imageWidth }); } else { const parsed = currentStep.split("-"); let sectionClone = JSON.parse(JSON.stringify(section)); if (useSections) { sectionNum = parseInt(parsed[1]); stepNum = parseInt(parsed[3]); } else { stepNum = parseInt(parsed[1]); } sectionClone[Math.max(sectionNum, 0)].steps[ stepNum ].stepPic.width = imageWidth; setAttributes({ section: sectionClone }); } }} min={200} max={800} /> <SelectControl label={__("Image float")} value={activeImage.float} onChange={(newFloatValue) => { if (currentStep === "final") { setAttributes({ finalImageFloat: newFloatValue }); } else { const parsed = currentStep.split("-"); let sectionClone = JSON.parse(JSON.stringify(section)); if (useSections) { sectionNum = parseInt(parsed[1]); stepNum = parseInt(parsed[3]); } else { stepNum = parseInt(parsed[1]); } sectionClone[Math.max(sectionNum, 0)].steps[ stepNum ].stepPic.float = newFloatValue; setAttributes({ section: sectionClone }); } }} options={["none", "left", "right"].map((a) => ({ label: a, value: a, }))} /> </PanelBody> )} {advancedMode && includeSuppliesList && ( <PanelBody title={__("Supplies list settings")}> <ToggleControl label={__("Enable adding image for each supply")} checked={addSupplyImages} onChange={(addSupplyImages) => setAttributes({ addSupplyImages })} /> <RadioControl label={__("Supplies list style")} selected={suppliesListStyle} options={["none", "ordered", "unordered"].map((a) => ({ label: __(a), value: a, }))} onChange={(suppliesListStyle) => setAttributes({ suppliesListStyle }) } /> </PanelBody> )} {advancedMode && includeToolsList && ( <PanelBody title={__("Tools list settings")}> <ToggleControl label={__("Enable adding image for each tool")} checked={addToolImages} onChange={(addToolImages) => setAttributes({ addToolImages })} /> <RadioControl label={__("Tools list style")} selected={toolsListStyle} options={["none", "ordered", "unordered"].map((a) => ({ label: __(a), value: a, }))} onChange={(toolsListStyle) => setAttributes({ toolsListStyle })} /> </PanelBody> )} <PanelBody title={__("Tag Settings")}> <SelectControl label={__("Howto title tag")} value={firstLevelTag} options={tagList.map((tag) => ({ label: __(tag), value: tag }))} onChange={(firstLevelTag) => setAttributes({ firstLevelTag })} /> <SelectControl label={__("Section title tag")} value={secondLevelTag} options={tagList.map((tag) => ({ label: __(tag), value: tag }))} onChange={(secondLevelTag) => setAttributes({ secondLevelTag })} /> <SelectControl label={__("Step title tag")} value={thirdLevelTag} options={tagList.map((tag) => ({ label: __(tag), value: tag }))} onChange={(thirdLevelTag) => setAttributes({ thirdLevelTag })} /> </PanelBody> </InspectorControls> ); } const defaultTimeDisplay = { w: 0, d: 0, h: 0, m: 0, s: 0, }; const ListWrapper = (props) => { const { className, children, listStyle } = props; return listStyle === "ordered" ? ( <ol className={className ? className : null}>{children}</ol> ) : ( <ul className={className ? className : null} style={{ listStyleType: listStyle === "none" ? "none" : null }} > {children} </ul> ); }; function HowToStep(props) { const [startTime, setStartTime] = useState( Object.assign({}, defaultTimeDisplay) ); const [endTime, setEndTime] = useState(Object.assign({}, defaultTimeDisplay)); const [validTimeInput, setTimeValidationStatus] = useState(true); const { sectionNum, stepNum, videoURL, direction, tip, title, editStep, deleteStep, moveUp, moveDown, stepPic, stepTag, clips, hasVideoClip, videoDuration, videoClipEnd, videoClipStart, advancedMode, blockIsSelected, selectStep, } = props; useEffect(() => { if (hasVideoClip) { const start = convertFromSeconds(videoClipStart); const end = convertFromSeconds(videoClipEnd); const clipId = sectionNum > -1 ? `section${sectionNum}step${stepNum}` : `step${stepNum}`; setStartTime({ w: 0, d: start.d, h: start.h, m: start.m, s: start.s }); setEndTime({ w: 0, d: end.d, h: end.h, m: end.m, s: end.s }); setTimeValidationStatus( clips.filter( (c) => c.anchor !== clipId && ((videoClipStart > c.clipStart && videoClipStart < c.clipEnd) || (videoClipEnd > c.clipStart && videoClipEnd < c.clipEnd)) ).length === 0 ); } }, []); useEffect(() => { const clipId = sectionNum > -1 ? `section${sectionNum}step${stepNum}` : `step${stepNum}`; setTimeValidationStatus( videoClipStart <= videoClipEnd && clips.filter( (c) => c.anchor !== clipId && ((videoClipStart > c.clipStart && videoClipStart < c.clipEnd) || (videoClipEnd > c.clipStart && videoClipEnd < c.clipEnd)) ).length === 0 ); }, [videoClipStart, videoClipEnd]); useEffect(() => { setStartTime(Object.assign({}, defaultTimeDisplay)); setEndTime(Object.assign({}, defaultTimeDisplay)); }, [videoURL]); return ( <li className="ub_howto-step"> <div> <RichText tagName={stepTag} keepPlaceholderOnFocus placeholder={__("Title goes here")} value={title} onChange={(newVal) => editStep({ title: newVal })} onFocus={selectStep} /> <Button className="ub_howto-delete" icon="trash" label={__("Delete step")} onClick={() => deleteStep()} /> <Button className="ub_howto-arrow" icon="arrow-up-alt" onClick={() => moveUp()} label={__("Move step up")} /> <Button className="ub_howto-arrow" icon="arrow-down-alt" onClick={() => moveDown()} label={__("Move step down")} /> </div> {stepPic.url !== "" ? ( <figure> <img className="ub_howto-step-image" src={stepPic.url} onClick={selectStep} /> {blockIsSelected && ( <span title={__("Delete image")} className="dashicons dashicons-dismiss" onClick={() => editStep({ stepPic: { id: -1, alt: "", url: "", caption: "", width: 0, float: "none", }, }) } /> )} <RichText tagName="figcaption" keepPlaceholderOnFocus placeholder={__("Step image caption")} value={stepPic.caption} onFocus={selectStep} onChange={(newCaption) => editStep({ stepPic: Object.assign(stepPic, { caption: newCaption }), }) } /> </figure> ) : ( <MediaUpload onSelect={(img) => { editStep({ stepPic: { id: img.id, alt: img.alt, url: img.url, caption: img.caption, width: Math.min(Math.max(img.width, 200), 800), float: "none", }, }); selectStep(); }} allowedTypes={["image"]} value={stepPic.id} render={({ open }) => ( <Button className="button is-default is-large ub_howto-button-default" onClick={open} > {__("Upload Image")} </Button> )} /> )} <div> <RichText keepPlaceholderOnFocus placeholder={__("Direction goes here")} value={direction} onFocus={selectStep} onChange={(newVal) => editStep({ direction: newVal })} /> <RichText keepPlaceholderOnFocus placeholder={__("Add a tip (optional)")} value={tip} onFocus={selectStep} onChange={(newVal) => editStep({ tip: newVal })} /> {advancedMode && ( <> {videoDuration > 0 && ( <ToggleControl checked={hasVideoClip} label={__("Use part of the video in this step")} onChange={(hasVideoClip) => { editStep({ hasVideoClip }); if (!hasVideoClip) { editStep({ videoClipEnd: 0, videoClipStart: 0 }); setStartTime(Object.assign({}, defaultTimeDisplay)); setEndTime(Object.assign({}, defaultTimeDisplay)); } }} /> )} {videoDuration > 0 && hasVideoClip && ( <> <span style={{ color: validTimeInput ? "black" : "red" }}> {__("Start time")} </span> {videoDuration >= 86400 && ( <input type="number" value={startTime.d} min={0} step={1} title={__("Days")} onChange={(e) => { const { h, m, s } = startTime; const d = Number(e.target.value); const startPoint = d * 86400 + h * 3600 + m * 60 + s; if (startPoint < videoDuration && d % 1 === 0 && d > -1) { setStartTime(Object.assign(startTime, { d })); editStep({ videoClipStart: startPoint }); } }} /> )} {videoDuration >= 3600 && ( <input type="number" value={startTime.h} min={0} max={23} step={1} title={__("Hours")} onChange={(e) => { const { d, m, s } = startTime; const h = Number(e.target.value); const startPoint = d * 86400 + h * 3600 + m * 60 + s; if ( startPoint < videoDuration && h % 1 === 0 && h > -1 && h < 24 ) { setStartTime(Object.assign(startTime, { h })); editStep({ videoClipStart: startPoint }); } }} /> )} {videoDuration >= 60 && ( <input type="number" value={startTime.m} min={0} max={59} step={1} title={__("Minutes")} onChange={(e) => { const { d, h, s } = startTime; const m = Number(e.target.value); const startPoint = d * 86400 + h * 3600 + m * 60 + s; if ( startPoint < videoDuration && m % 1 === 0 && m > -1 && m < 60 ) { setStartTime(Object.assign(startTime, { m })); editStep({ videoClipStart: startPoint }); } }} /> )} <input type="number" value={startTime.s} min={0} max={59} step={1} title={__("Seconds")} onChange={(e) => { const { d, h, m } = startTime; const s = Number(e.target.value); const startPoint = d * 86400 + h * 3600 + m * 60 + s; if ( startPoint < videoDuration && s % 1 === 0 && s > -1 && s < 60 ) { setStartTime(Object.assign(startTime, { s })); editStep({ videoClipStart: startPoint }); } }} /> <br /> <span style={{ color: validTimeInput ? "black" : "red" }}> {__("End time")} </span> {videoDuration >= 86400 && ( <input type="number" value={endTime.d} min={0} step={1} title={__("Days")} onChange={(e) => { const { h, m, s } = endTime; const d = Number(e.target.value); const endPoint = d * 86400 + h * 3600 + m * 60 + s; if (endPoint <= videoDuration && d % 1 === 0 && d > -1) { setEndTime(Object.assign(endTime, { d })); editStep({ videoClipEnd: endPoint }); } }} /> )} {videoDuration >= 3600 && ( <input type="number" value={endTime.h} min={0} max={23} step={1} title={__("Hours")} onChange={(e) => { const { d, m, s } = endTime; const h = Number(e.target.value); const endPoint = d * 86400 + h * 3600 + m * 60 + s; if ( endPoint <= videoDuration && h % 1 === 0 && h > -1 && h < 24 ) { setEndTime(Object.assign(endTime, { h })); editStep({ videoClipEnd: endPoint }); } }} /> )} {videoDuration >= 60 && ( <input type="number" value={endTime.m} min={0} max={59} step={1} title={__("Minutes")} onChange={(e) => { const { d, h, s } = endTime; const m = Number(e.target.value); const endPoint = d * 86400 + h * 3600 + m * 60 + s; if ( endPoint <= videoDuration && m % 1 === 0 && m > -1 && m < 60 ) { setEndTime(Object.assign(endTime, { m })); editStep({ videoClipEnd: endPoint }); } }} /> )} <input type="number" value={endTime.s} min={0} max={59} step={1} title={__("Seconds")} onChange={(e) => { const { d, h, m } = endTime; const s = Number(e.target.value); const endPoint = d * 86400 + h * 3600 + m * 60 + s; if ( endPoint <= videoDuration && s % 1 === 0 && s > -1 && s < 60 ) { setEndTimeObject.assign(endTime, { s }); editStep({ videoClipEnd: endPoint }); } }} /> </> )} </> )} </div> </li> ); } function HowToSection(props) { const { sectionListStyle, sectionNum, sectionName, sectionTag, steps, stepTag, editSection, deleteSection, videoDuration, clips, videoURL, advancedMode, blockIsSelected, updateState, currentStep, } = props; return ( <li className="ub_howto-section"> <div> <RichText keepPlaceholderOnFocus tagName={sectionTag} placeholder={__("Section name goes here")} value={sectionName} onChange={(sectionName) => editSection({ sectionName, steps })} /> <Button className="ub_howto-delete" icon="trash" label={__("Delete section")} onClick={() => deleteSection()} /> </div> <ListWrapper className="ub_howto-steps-list" listStyle={sectionListStyle}> {steps.map((step, i) => ( <HowToStep {...step} advancedMode={advancedMode} clips={clips} sectionNum={sectionNum} stepNum={i} stepTag={stepTag} videoURL={videoURL} videoDuration={videoDuration} selectStep={() => props.selectStepInSection(i)} editStep={(newStep) => { editSection({ sectionName, steps: [ ...steps.slice(0, i), Object.assign(steps[i], newStep), ...steps.slice(i + 1), ], }); }} deleteStep={() => { let newSteps = [...steps.slice(0, i), ...steps.slice(i + 1)]; newSteps.forEach( (step, j) => (step.anchor = `section${sectionNum}step${j}`) ); editSection({ sectionName, steps: [...steps.slice(0, i), ...steps.slice(i + 1)], }); if (currentStep === `section-${sectionNum}-step-${i}`) { updateState({ currentStep: "" }); } }} moveUp={() => { if (i > 0) { let newSteps = [ ...steps.slice(0, i - 1), steps[i], steps[i - 1], ...steps.slice(i + 1), ]; newSteps.forEach( (step, j) => (step.anchor = `section${sectionNum}step${j}`) ); editSection({ sectionName, steps: newSteps }); //set value of currentStep to recently-moved step updateState({ currentStep: `section-${sectionNum}-step-${i - 1}`, }); } }} moveDown={() => { if (i < steps.length - 1) { let newSteps = [ ...steps.slice(0, i), steps[i + 1], steps[i], ...steps.slice(i + 2), ]; newSteps.forEach( (step, j) => (step.anchor = `section${sectionNum}step${j}`) ); editSection({ sectionName, steps: newSteps }); updateState({ currentStep: `section-${sectionNum}-step-${i + 1}`, }); } }} blockIsSelected={blockIsSelected} currentStep={currentStep} updateState={updateState} /> ))} </ListWrapper> <Button className="ub_howto-button-default" onClick={() => { editSection({ sectionName, steps: [ ...steps, { anchor: `section${sectionNum}step${steps.length}`, stepPic: { img: -1, alt: "", url: "", width: 0, float: "none", }, direction: "", tip: "", title: "", hasVideoClip: false, videoClipStart: 0, videoClipEnd: 0, }, ], }); }} > {__("Add step")} </Button> </li> ); } export function EditorComponent(props) { const [videoURLInput, setVideoURLInput] = useState(""); const [currentStep, setCurrentStep] = useState(""); useEffect(() => { const { attributes: { videoURL, section }, setAttributes, } = props; setVideoURLInput(videoURL); //add width and float to images for each step let sectionClone = JSON.parse(JSON.stringify(section)); let hasMissingProperties = false; sectionClone.forEach((s) => { s.steps.forEach((st) => { if (!st.stepPic.hasOwnProperty("width")) { hasMissingProperties = true; st.stepPic.width = 200; st.stepPic.float = "none"; } }); }); if (hasMissingProperties) { setAttributes({ section: sectionClone }); } }, []); const { attributes: { blockID, title, introduction, advancedMode, section, sectionListStyle, suppliesIntro, supplies, suppliesListStyle, toolsIntro, tools, toolsListStyle, howToYield, cost, costCurrency, costDisplayText, showUnitFirst, timeIntro, totalTime, totalTimeText, useSections, includeToolsList, addToolImages, includeSuppliesList, addSupplyImages, resultIntro, finalImageID, finalImageURL, finalImageCaption, finalImageWidth, finalImageFloat, videoURL, videoEmbedCode, videoDuration, firstLevelTag, secondLevelTag, thirdLevelTag, }, setAttributes, block, getBlock, getClientIdsWithDescendants, isSelected, } = props; const units = [ "years", "months", "weeks", "days", "hours", "minutes", "seconds", ]; const resetVideoAttributes = () => { let newSection = JSON.parse(JSON.stringify(section)); newSection.forEach((s) => s.steps.map((st) => Object.assign(st, { hasVideoClip: false, videoClipStart: 0, videoClipEnd: 0, }) ) ); setAttributes({ section: newSection, videoURL: "", videoDescription: "", videoUploadDate: 0, videoThumbnailURL: "", videoEmbedCode: `<p>${__( "When insertion is successful, video should appear here" )}</p>`, videoDuration: 0, }); }; const clips = section .reduce((stepList, section) => [...stepList, ...section.steps], []) .filter((s) => s.hasVideoClip) .map((s) => ({ anchor: s.anchor, clipStart: s.videoClipStart, clipEnd: s.videoClipEnd, })); if ( blockID === "" || getClientIdsWithDescendants().some( (ID) => "blockID" in getBlock(ID).attributes && getBlock(ID).attributes.blockID === blockID ) ) { setAttributes({ blockID: block.clientId }); } const checkVideoURLInput = () => { if (/^http(s)?:\/\//g.test(videoURLInput)) { const youtubeMatch = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/g.exec( videoURLInput ); const vimeoMatch = /^(?:https?\:\/\/)?(?:www\.|player\.)?(?:vimeo\.com\/)([0-9]+)/g.exec( videoURLInput ); const dailyMotionMatch = /^(?:https?\:\/\/)?(?:www\.)?(?:dailymotion\.com\/video|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+#video=([a-z0-9]+))?/g.exec( videoURLInput ); const videoPressMatch = /^https?:\/\/(?:www\.)?videopress\.com\/(?:embed|v)\/([a-zA-Z0-9]{8,})/g.exec( videoURLInput ); if (youtubeMatch) { fetch( `https://www.googleapis.com/youtube/v3/videos?id=${youtubeMatch[1]}&part=snippet,contentDetails,player&key=AIzaSyDgItjYofyXkIZ4OxF6gN92PIQkuvU319c` ) .then((response) => { response.json().then((data) => { if (data.items.length) { let timePeriods = data.items[0].contentDetails.duration.match( /(\d{1,2}(?:W|D|H|M|S))/g ); setAttributes({ videoURL: `https://www.youtube.com/watch?v=${youtubeMatch[1]}`, videoName: data.items[0].snippet.title, videoDescription: data.items[0].snippet.description, videoUploadDate: Date.parse(data.items[0].snippet.publishedAt) / 1000, videoThumbnailURL: `https://i.ytimg.com/vi/${youtubeMatch[1]}/default.jpg`, videoEmbedCode: decodeURIComponent( data.items[0].player.embedHtml ), videoDuration: timePeriods.reduce((sum, part) => { let multiplier = { W: 604800, D: 86400, H: 3600, M: 60, S: 1, }; return ( sum + Number(part.slice(0, -1)) * multiplier[part.slice(-1)] ); }, 0), }); } else { resetVideoAttributes(); setAttributes({ videoEmbedCode: `<p>${__("No video found at URL")}</p>`, }); } }); }) .catch((err) => { console.log("youtube fetch error"); console.log(err); }); } else if (vimeoMatch) { fetch(`https://vimeo.com/api/v2/video/${vimeoMatch[1]}.json`) .then((response) => { if (response.ok) { response .json() .then((data) => { setAttributes({ videoURL: data[0].url, videoName: data[0].title, videoDescription: data[0].description, videoUploadDate: Date.parse(data[0].upload_date) / 1000, videoThumbnailURL: data[0].thumbnail_large, videoDuration: data[0].duration, }); fetch( `https://vimeo.com/api/oembed.json?url=${encodeURIComponent( data[0].url )}` ) .then((response) => { response.json().then((data) => { setAttributes({ videoEmbedCode: data.html, }); }); }) .catch((err) => { console.log("vimeo oembed error"); console.log(err); }); }) .catch((err) => { console.log(err); }); } else { resetVideoAttributes(); setAttributes({ videoEmbedCode: `<p>${__("No video found at URL")}</p>`, }); } }) .catch((err) => { console.log("vimeo fetch error"); console.log(err); }); } else if (dailyMotionMatch) { fetch( `https://api.dailymotion.com/video/${dailyMotionMatch[1]}?fields=created_time%2Cthumbnail_1080_url%2Ctitle%2Cdescription%2Curl%2Cembed_html%2Cduration` ) .then((response) => { if (response.ok) { response.json().then((data) => { setAttributes({ videoURL: data.url, videoName: data.title, videoDescription: data.description, videoUploadDate: data.created_time, videoThumbnailURL: data.thumbnail_1080_url, videoEmbedCode: decodeURIComponent(data.embed_html), videoDuration: data.duration, }); }); } else { resetVideoAttributes(); setAttributes({ videoEmbedCode: `<p>${__("No video found at URL")}</p>`, }); } }) .catch((err) => { console.log("dailymotion input error"); console.log(err); }); } else if (videoPressMatch) { fetch( `https://public-api.wordpress.com/rest/v1.1/videos/${videoPressMatch[1]}` ) .then((response) => { if (response.ok) { response.json().then((data) => { setAttributes({ videoURL: `https://videopress.com/v/${data.guid}`, videoName: data.title, videoDescription: data.description, videoUploadDate: Date.parse(data.upload_date) / 1000, videoThumbnailURL: data.poster, videoEmbedCode: `<iframe width="560" height="315" src="https://videopress.com/embed/${data.guid}" frameborder="0" allowfullscreen></iframe> <script src="https://videopress.com/videopress-iframe.js"></script>`, videoDuration: Math.floor(data.duration / 1000), }); }); } else { resetVideoAttributes(); setAttributes({ videoEmbedCode: `<p>${__("No video found at URL")}</p>`, }); } }) .catch((err) => { console.log("videopress input error"); console.log(err); }); } else { resetVideoAttributes(); setAttributes({ videoEmbedCode: "<p>Video site not supported</p>" }); } } else { resetVideoAttributes(); console.log("input is not a url"); } }; return ( <> <InspectorPanel {...props} videoURLInput={videoURLInput} currentStep={currentStep} updateState={(newState) => { if (newState.hasOwnProperty("currentStep")) { setCurrentStep(newState.currentStep); } //videoURLInput isn't being updated via updateState for now }} /> <div className="ub_howto" id={`ub_howto-${blockID}`}> <RichText tagName={firstLevelTag} placeholder={__("How to title")} keepPlaceholderOnFocus={true} value={title} onChange={(title) => setAttributes({ title })} /> <RichText placeholder={__("How to introduction")} keepPlaceholderOnFocus={true} value={introduction} onChange={(introduction) => setAttributes({ introduction })} /> {advancedMode && ( <> <div className="ub_howto-video-input"> <input type="url" placeholder={__("Insert video URL")} className="button-url" value={videoURLInput} onChange={(e) => setVideoURLInput(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { checkVideoURLInput(); } }} /> <Button icon={"editor-break"} label={__("Apply")} type={"submit"} onClick={checkVideoURLInput} /> <Button icon="trash" label={__("Delete")} onClick={() => { resetVideoAttributes(); setVideoURLInput(""); }} /> </div> <div dangerouslySetInnerHTML={{ __html: videoEmbedCode || "<p>Input error</p>", }} /> {includeSuppliesList && ( <> <RichText tagName={secondLevelTag} placeholder={__("Required supplies")} keepPlaceholderOnFocus={true} value={suppliesIntro} onChange={(suppliesIntro) => setAttributes({ suppliesIntro })} /> <ListWrapper className={"ub_howto-supplies-list"} listStyle={suppliesListStyle} > {supplies.map((supply, i) => ( <li> <div> <RichText keepPlaceholderOnFocus value={supply.name} placeholder={__("Enter supply name")} onChange={(newName) => setAttributes({ supplies: [ ...supplies.slice(0, i), Object.assign(supplies[i], { name: newName }), ...supplies.slice(i + 1), ], }) } /> <Button icon="trash" label={__("Delete supply")} onClick={() => setAttributes({ supplies: [ ...supplies.slice(0, i), ...supplies.slice(i + 1), ], }) } /> </div> {addSupplyImages && (supply.imageURL !== "" ? ( <figure> <img className="ub_howto-supply-image" src={supply.imageURL} /> {isSelected && ( <span title={__("Delete image")} className="dashicons dashicons-dismiss" onClick={() => setAttributes({ supplies: [ ...supplies.slice(0, i), Object.assign(supply, { imageID: 0, imageURL: "", imageAlt: "", }), ...supplies.slice(i + 1), ], }) } /> )} </figure> ) : ( <MediaUpload onSelect={(img) => setAttributes({ supplies: [ ...supplies.slice(0, i), Object.assign(supply, { imageID: img.id, imageURL: img.url, imageAlt: img.alt, }), ...supplies.slice(i + 1), ], }) } allowedTypes={["image"]} value={supply.imageID} render={({ open }) => ( <Button className="button is-default is-large ub_howto-button-default" onClick={open} > {__("Upload Image")} </Button> )} /> ))} </li> ))} </ListWrapper> <Button className="ub_howto-button-default" onClick={() => setAttributes({ supplies: [ ...supplies, { name: "", imageID: 0, imageAlt: "", imageURL: "" }, ], }) } > {__("Add new supplies")} </Button> </> )} {includeToolsList && ( <> <RichText tagName={secondLevelTag} placeholder={__("Required tools")} keepPlaceholderOnFocus={true} value={toolsIntro} onChange={(toolsIntro) => setAttributes({ toolsIntro })} /> <ListWrapper className={"ub_howto-tools-list"} listStyle={toolsListStyle} > {tools.map((tool, i) => ( <li> <div> <RichText keepPlaceholderOnFocus value={tool.name} placeholder={__("Enter tool name")} onChange={(newTool) => setAttributes({ tools: [ ...tools.slice(0, i), Object.assign(tools[i], { name: newTool }), ...tools.slice(i + 1), ], }) } /> <Button icon="trash" label={__("Delete tool")} onClick={() => setAttributes({ tools: [ ...tools.slice(0, i), ...tools.slice(i + 1), ], }) } /> </div> {addToolImages && (tool.imageURL !== "" ? ( <figure> <img src={tool.imageURL} /> {isSelected && ( <span title={__("Delete image")} className="dashicons dashicons-dismiss" onClick={() => setAttributes({ tools: [ ...tools.slice(0, i), Object.assign(tool, { imageID: 0, imageURL: "", imageAlt: "", }), ...tools.slice(i + 1), ], }) } /> )} </figure> ) : ( <MediaUpload onSelect={(img) => setAttributes({ tools: [ ...tools.slice(0, i), Object.assign(tool, { imageID: img.id, imageURL: img.url, imageAlt: img.alt, }), ...tools.slice(i + 1), ], }) } allowedTypes={["image"]} value={tool.imageID} render={({ open }) => ( <Button className="button is-default is-large ub_howto-button-default" onClick={open} > {__("Upload Image")} </Button> )} /> ))} </li> ))} </ListWrapper> <Button className="ub_howto-button-default" onClick={() => setAttributes({ tools: [ ...tools, { name: "", imageID: 0, imageAlt: "", imageURL: "" }, ], }) } > {__("Add new tools")} </Button> </> )} <div className="ub_howto_cost_container"> <RichText value={costDisplayText} onChange={(costDisplayText) => setAttributes({ costDisplayText }) } /> <div className="ub_howto_cost_display" style={{ flexDirection: showUnitFirst ? "row" : "row-reverse", }} > <RichText style={ showUnitFirst ? { paddingRight: "10px" } : { paddingLeft: "10px" } } keepPlaceholderOnFocus placeholder={__("Units")} value={costCurrency} onChange={(costCurrency) => { costCurrency = costCurrency.replace(/<br>/g, ""); setAttributes({ costCurrency }); }} /> <RichText keepPlaceholderOnFocus placeholder={__("0")} value={String(cost)} onChange={(cost) => { if (!isNaN(Number(cost))) { setAttributes({ cost: Number(cost) }); } }} /> </div> </div> <RichText tagName={secondLevelTag} placeholder={__("Duration")} keepPlaceholderOnFocus={true} value={timeIntro} onChange={(timeIntro) => setAttributes({ timeIntro })} /> <div className="ub_howto-duration-input"> <span /> {units.map((u) => ( <p>{__(u)}</p> ))} <RichText keepPlaceholderOnFocus value={totalTimeText} onChange={(totalTimeText) => setAttributes({ totalTimeText })} /> {totalTime.map((t, i) => ( <RichText className="ub_howto-time-value" keepPlaceholderOnFocus placeholder={__("0")} value={String(t)} onChange={(newInput) => { if (!isNaN(Number(newInput))) { setAttributes({ totalTime: [ ...totalTime.slice(0, i), Number(newInput), ...totalTime.slice(i + 1), ], }); } }} /> ))} </div> </> )} {useSections ? ( <ListWrapper listStyle={sectionListStyle}> {section.map((s, i) => ( <HowToSection {...s} advancedMode={advancedMode} clips={clips} videoURL={videoURL} videoDuration={videoDuration} sectionListStyle={sectionListStyle} sectionNum={i} sectionTag={secondLevelTag} stepTag={thirdLevelTag} selectStepInSection={(step) => setCurrentStep(`section-${i}-step-${step}`) } editSection={(newSection) => setAttributes({ section: [ ...section.slice(0, i), newSection, ...section.slice(i + 1), ], }) } deleteSection={() => setAttributes({ section: [...section.slice(0, i), ...section.slice(i + 1)], }) } blockIsSelected={isSelected} currentStep={currentStep} updateState={(newState) => { if (newState.hasOwnProperty("currentStep")) { setCurrentStep(newState.currentStep); } }} /> ))} </ListWrapper> ) : ( <> <ListWrapper className={"ub_howto-steps-list"} listStyle={sectionListStyle} > {section[0].steps.map((step, i) => ( <HowToStep advancedMode={advancedMode} sectionNum={-1} stepNum={i} stepTag={thirdLevelTag} {...step} clips={clips} videoURL={videoURL} videoDuration={videoDuration} selectStep={() => setCurrentStep(`step-${i}`)} editStep={(newStep) => { setAttributes({ section: [ Object.assign(section[0], { steps: [ ...section[0].steps.slice(0, i), Object.assign(section[0].steps[i], newStep), ...section[0].steps.slice(i + 1), ], }), ], }); }} deleteStep={() => { let newSection = [ Object.assign(section[0], { steps: [ ...section[0].steps.slice(0, i), ...section[0].steps.slice(i + 1), ], }), ]; section[0].steps.forEach((step, j) => { step.anchor = `step${j}`; }); setAttributes({ section: newSection, }); if (currentStep === `step-${i}`) { setCurrentStep(""); } }} moveUp={() => { if (i > 0) { let newSection = [ Object.assign(section[0], { steps: [ ...section[0].steps.slice(0, i - 1), section[0].steps[i], section[0].steps[i - 1], ...section[0].steps.slice(i + 1), ], }), ]; section[0].steps.forEach((step, j) => { step.anchor = `step${j}`; }); setAttributes({ section: newSection }); setCurrentStep(`step-${i - 1}`); } }} moveDown={() => { if (i < section[0].steps.length - 1) { let newSection = [ Object.assign(section[0], { steps: [ ...section[0].steps.slice(0, i), section[0].steps[i + 1], section[0].steps[i], ...section[0].steps.slice(i + 2), ], }), ]; section[0].steps.forEach((step, j) => { step.anchor = `step${j}`; }); setAttributes({ section: newSection }); setCurrentStep(`step-${i + 1}`); } }} blockIsSelected={isSelected} updateState={(newState) => { if (newState.hasOwnProperty("currentStep")) { setCurrentStep(newState.currentStep); } }} /> ))} </ListWrapper> <Button className="ub_howto-button-default" onClick={() => { setAttributes({ section: [ Object.assign(section[0], { steps: [ ...section[0].steps, { anchor: `step${section[0].steps.length}`, stepPic: { img: -1, alt: "", url: "", width: 0, float: "none", }, direction: "", tip: "", title: "", hasVideoClip: false, videoClipStart: 0, videoClipEnd: 0, }, ], }), ], }); }} > {__("Add step")} </Button> </> )} {useSections && ( <Button className="ub_howto-button-default" onClick={() => setAttributes({ section: [ ...section, { sectionName: "", steps: [ { anchor: `section${section.length}step0`, stepPic: { img: -1, alt: "", url: "", width: 0, float: "none", }, direction: "", tip: "", title: "", hasVideoClip: false, videoClipStart: 0, videoClipEnd: 0, }, ], }, ], }) } > {__("Add Section")} </Button> )} <div className="ub_howto-yield"> <RichText tagName={secondLevelTag} placeholder={__("Result")} keepPlaceholderOnFocus={true} value={resultIntro} onChange={(resultIntro) => setAttributes({ resultIntro })} onFocus={() => setCurrentStep("final")} /> {finalImageURL !== "" ? ( <figure className="ub_howto-yield-image-container"> <img src={finalImageURL} onClick={() => setCurrentStep("final")} /> {isSelected && ( <span title={__("Delete image")} className="dashicons dashicons-dismiss" onClick={() => setAttributes({ finalImageID: -1, finalImageAlt: "", finalImageURL: "", finalImageCaption: "", finalImageWidth: 0, finalImageFloat: "none", }) } /> )} <RichText tagName="figcaption" keepPlaceholderOnFocus placeholder={__("Final image caption")} value={finalImageCaption} onChange={(finalImageCaption) => setAttributes({ finalImageCaption }) } onFocus={() => setCurrentStep("final")} /> </figure> ) : ( <MediaUpload onSelect={(img) => { setCurrentStep("final"); setAttributes({ finalImageID: img.id, finalImageAlt: img.alt, finalImageURL: img.url, finalImageCaption: img.caption, finalImageWidth: Math.min(Math.max(img.width, 200), 800), finalImageFloat: "none", }); }} allowedTypes={["image"]} value={finalImageID} render={({ open }) => ( <Button className="button is-default is-large ub_howto-button-default" onClick={open} > {__("Upload Image")} </Button> )} /> )} <RichText keepPlaceholderOnFocus placeholder={__("Result text")} value={howToYield} onChange={(howToYield) => setAttributes({ howToYield })} onFocus={() => setCurrentStep("final")} /> </div> </div> <style dangerouslySetInnerHTML={{ __html: `@media (min-width:768px) {${ useSections ? section .map((s, i) => s.steps .map((st) => (({ width, float }) => ({ width, float }))(st.stepPic) ) .map((img, j) => img.width > 0 ? `#ub_howto-${blockID} .ub_howto-section:nth-child(${ i + 1 }) .ub_howto-step:nth-child(${ j + 1 }) figure { width: ${img.width}px; float: ${ img.float };}` : "" ) .join("") ) .join("") : section[0].steps .map((s) => (({ width, float }) => ({ width, float }))(s.stepPic) ) .map((img, i) => img.width > 0 ? `#ub_howto-${blockID} .ub_howto-step:nth-child(${ i + 1 }) figure { width: ${img.width}px; float: ${ img.float };}` : "" ) .join("") } ${ finalImageWidth > 0 ? `#ub_howto-${blockID} .ub_howto-yield-image-container{ width: ${finalImageWidth}px; float: ${finalImageFloat}; }` : "" } }`, }} /> </> ); }