import React from "react"; import { Form, Col, Row, InputGroup, Button, FormControl, Badge, } from "react-bootstrap"; import { CollapseButton, Icon, Modal } from "src/components/Shared"; import _ from "lodash"; import { FormattedMessage, useIntl } from "react-intl"; export class ScrapeResult { public newValue?: T; public originalValue?: T; public scraped: boolean = false; public useNewValue: boolean = false; public constructor(originalValue?: T | null, newValue?: T | null) { this.originalValue = originalValue ?? undefined; this.newValue = newValue ?? undefined; const valuesEqual = _.isEqual(originalValue, newValue); this.useNewValue = !!this.newValue && !valuesEqual; this.scraped = this.useNewValue; } public setOriginalValue(value?: T) { this.originalValue = value; this.newValue = value; } public cloneWithValue(value?: T) { const ret = _.clone(this); ret.newValue = value; ret.useNewValue = !_.isEqual(ret.newValue, ret.originalValue); ret.scraped = ret.useNewValue; return ret; } public getNewValue() { if (this.useNewValue) { return this.newValue; } } } interface IHasName { name: string; } interface IScrapedFieldProps { result: ScrapeResult; } interface IScrapedRowProps extends IScrapedFieldProps { title: string; renderOriginalField: (result: ScrapeResult) => JSX.Element | undefined; renderNewField: (result: ScrapeResult) => JSX.Element | undefined; onChange: (value: ScrapeResult) => void; newValues?: V[]; onCreateNew?: (newValue: V) => void; } function renderButtonIcon(selected: boolean) { const className = selected ? "text-success" : "text-muted"; return ( ); } export const ScrapeDialogRow = ( props: IScrapedRowProps ) => { function handleSelectClick(isNew: boolean) { const ret = _.clone(props.result); ret.useNewValue = isNew; props.onChange(ret); } function hasNewValues() { return props.newValues && props.newValues.length > 0 && props.onCreateNew; } if (!props.result.scraped && !hasNewValues()) { return <>; } function renderNewValues() { if (!hasNewValues()) { return; } const ret = ( <> {props.newValues!.map((t) => ( props.onCreateNew!(t)} > {t.name} ))} ); const minCollapseLength = 10; if (props.newValues!.length >= minCollapseLength) { return ( {ret} ); } return ret; } return ( {props.title} {props.renderOriginalField(props.result)} {props.renderNewField(props.result)} {renderNewValues()} ); }; interface IScrapedInputGroupProps { isNew?: boolean; placeholder?: string; result: ScrapeResult; onChange?: (value: string) => void; } const ScrapedInputGroup: React.FC = (props) => { return ( { if (props.isNew && props.onChange) { props.onChange(e.target.value); } }} className="bg-secondary text-white border-secondary" /> ); }; interface IScrapedInputGroupRowProps { title: string; placeholder?: string; result: ScrapeResult; onChange: (value: ScrapeResult) => void; } export const ScrapedInputGroupRow: React.FC = ( props ) => { return ( ( )} renderNewField={() => ( props.onChange(props.result.cloneWithValue(value)) } /> )} onChange={props.onChange} /> ); }; const ScrapedTextArea: React.FC = (props) => { return ( { if (props.isNew && props.onChange) { props.onChange(e.target.value); } }} className="bg-secondary text-white border-secondary scene-description" /> ); }; export const ScrapedTextAreaRow: React.FC = ( props ) => { return ( ( )} renderNewField={() => ( props.onChange(props.result.cloneWithValue(value)) } /> )} onChange={props.onChange} /> ); }; interface IScrapedImageProps { isNew?: boolean; className?: string; placeholder?: string; result: ScrapeResult; } const ScrapedImage: React.FC = (props) => { const value = props.isNew ? props.result.newValue : props.result.originalValue; if (!value) { return <>; } return ( {props.placeholder} ); }; interface IScrapedImageRowProps { title: string; className?: string; result: ScrapeResult; onChange: (value: ScrapeResult) => void; } export const ScrapedImageRow: React.FC = (props) => { return ( ( )} renderNewField={() => ( )} onChange={props.onChange} /> ); }; interface IScrapeDialogProps { title: string; renderScrapeRows: () => JSX.Element; onClose: (apply?: boolean) => void; } export const ScrapeDialog: React.FC = ( props: IScrapeDialogProps ) => { const intl = useIntl(); return ( { props.onClose(true); }, text: intl.formatMessage({ id: "actions.apply" }), }} cancel={{ onClick: () => props.onClose(), text: intl.formatMessage({ id: "actions.cancel" }), variant: "secondary", }} modalProps={{ size: "lg", dialogClassName: "scrape-dialog" }} >
{props.renderScrapeRows()}
); };