/** @jsx jsx */ /** @jsxFrag React.Fragment */ /** * Information source items (a.k.a. citations). * * Originally, citations were a common structure, but not expressed * as a separate register item class. */ import update, { type Spec } from 'immutability-helper'; import { jsx } from '@emotion/react'; import React from 'react'; import { InputGroup, } from '@blueprintjs/core'; import { PropertyDetailView } from '@riboseinc/paneron-registry-kit'; import ItemList from '../helpers/ItemList'; import type { ItemClassConfiguration, ItemListView, Citation, } from '@riboseinc/paneron-registry-kit/types'; import { type CommonGRItemData, DEFAULTS as SHARED_DEFAULTS, COMMON_PROPERTIES, EditView as CommonEditView, ListItemView as CommonListItemView, } from './common'; //type InformationSource = Citation; export interface InformationSourceItemData extends CommonGRItemData { citation: Citation } function isCitation(val: any): val is Citation { return ( val && typeof val.title === 'string' // TBD ); } export const CitationEdit: React.FC<{ citation: Citation onChange?: (newCitation: Citation) => void }> = function ({ citation, onChange }) { function getChangeHandler > (field: F, emptyValue?: '' | null | 'unset' | 'undefined'): undefined | ((val: Citation[F]) => void) { const empty = emptyValue === undefined ? '' : emptyValue === 'undefined' ? undefined : emptyValue; return onChange ? (val) => { let newCitation: Citation; if ((val as string)?.trim() === '') { if (empty === 'unset') { newCitation = update(citation, { $unset: [field] }); } else { //console.debug("Setting to empty", empty); newCitation = update(citation, { [field]: { $set: empty } }); } } else { newCitation = update(citation, { [field]: { $set: val } }); } return onChange!(newCitation); } : undefined; } const getOnChange = React.useCallback(getChangeHandler, [onChange, citation]); if (!isCitation(citation)) { throw new Error("Item given to CitationEdit is not a citation"); } return ( <> items={citation.alternateTitles ?? []} hideOrdinals simpleItems itemLabel="alternate title" itemLabelPlural="alternate titles" onChangeItems={onChange ? (spec) => { // Ensure alternateTitles is a list // (schema allows it to be undefined, which may cause // update() to silently fail without even an error) citation.alternateTitles ??= []; return onChange!(update( citation, { alternateTitles: spec as Spec } )); } : undefined} placeholderItem="" itemRenderer={(title, idx, handleChange, deleteButton) => handleChange!({ $set: evt.currentTarget.value })} /> } /> ); }; export const DEFAULTS: Readonly = Object.freeze({ name: "Unnamed information source", // Non-positive identifiers are replaced with next available positive integers // in afterApprovedCR hook. identifier: 0, // Awkward informationSources: [], informationSourceRefs: [], remarks: '', aliases: [], citation: getInformationSourceStub(), }); export function getInformationSourceStub(): Readonly { return { title: "Untitled publication", author: null, publisher: null, publicationDate: null, revisionDate: null, seriesIssueID: null, seriesName: null, seriesPage: null, edition: null, editionDate: null, otherDetails: '', isbn: null, issn: null, alternateTitles: [], uri: '', doi: '', } as Readonly; } export const informationSource: ItemClassConfiguration = { ...COMMON_PROPERTIES, meta: { title: "Information Source (test)", description: "A citation.", id: 'information-source', alternativeNames: [] as const, }, defaults: { ...SHARED_DEFAULTS, ...DEFAULTS, }, views: { listItemView: CommonListItemView as ItemListView, editView: (props) => ( { if (!props.onChange) { return; } props.onChange({ ...props.itemData, ...newData }); } : undefined}> props.onChange!(update(props.itemData, { citation: { $set: item } })) : undefined} /> {props.children} ), }, validatePayload: async () => true, sanitizePayload: async (t) => t, }; const SimpleField: React.FC<{ val: string | undefined | null, label: string, onChange?: (newVal: string) => void }> = function ({ val, label, onChange }) { return ( ) => onChange!(evt.currentTarget.value)} /> ); };