|
| 1 | +/** |
| 2 | + * A custom "URL" renderer -- an object that has "text" and "url" properties, |
| 3 | + * but is displayed as a clickable string |
| 4 | + */ |
| 5 | + |
| 6 | +import React, { useState } from 'react' |
| 7 | +import { toPathString, StringDisplay, StringEdit, type CustomNodeProps } from '@json-edit-react' |
| 8 | + |
| 9 | +export interface EnhancedLinkProps { |
| 10 | + linkStyles?: React.CSSProperties |
| 11 | + propertyStyles?: React.CSSProperties |
| 12 | + labels?: { text: string; url: string } |
| 13 | + fieldNames?: { text: string; url: string } |
| 14 | + stringTruncate?: number |
| 15 | + [key: string]: unknown |
| 16 | +} |
| 17 | + |
| 18 | +type EnhancedLink = { |
| 19 | + [key: string]: string |
| 20 | +} |
| 21 | + |
| 22 | +export const EnhancedLinkCustomComponent: React.FC<CustomNodeProps<EnhancedLinkProps>> = ( |
| 23 | + props |
| 24 | +) => { |
| 25 | + const { setIsEditing, getStyles, nodeData, customNodeProps = {}, isEditing, handleEdit } = props |
| 26 | + const { |
| 27 | + linkStyles = { fontWeight: 'bold', textDecoration: 'underline' }, |
| 28 | + propertyStyles = {}, |
| 29 | + labels: { text: textLabel, url: urlLabel } = { text: 'Text', url: 'Link' }, |
| 30 | + fieldNames: { text: textField, url: urlField } = { text: 'text', url: 'url' }, |
| 31 | + stringTruncate = 120, |
| 32 | + } = customNodeProps |
| 33 | + const [text, setText] = useState((nodeData.value as EnhancedLink)[textField]) |
| 34 | + const [url, setUrl] = useState((nodeData.value as EnhancedLink)[urlField]) |
| 35 | + |
| 36 | + const styles = getStyles('string', nodeData) |
| 37 | + |
| 38 | + return ( |
| 39 | + <div |
| 40 | + onClick={(e) => { |
| 41 | + if (e.getModifierState('Control') || e.getModifierState('Meta')) setIsEditing(true) |
| 42 | + }} |
| 43 | + style={styles} |
| 44 | + > |
| 45 | + {isEditing ? ( |
| 46 | + <div style={{ display: 'flex', flexDirection: 'column', gap: '0.3em', marginTop: '0.4em' }}> |
| 47 | + <div style={{ display: 'flex', alignItems: 'center', gap: '0.5em' }}> |
| 48 | + <span style={propertyStyles}>{textLabel}:</span> |
| 49 | + <StringEdit |
| 50 | + styles={getStyles('input', nodeData)} |
| 51 | + pathString={toPathString(nodeData.path)} |
| 52 | + {...props} |
| 53 | + value={text} |
| 54 | + setValue={(val) => setText(val)} |
| 55 | + /> |
| 56 | + </div> |
| 57 | + <div style={{ display: 'flex', alignItems: 'center', gap: '0.5em' }}> |
| 58 | + <span style={propertyStyles}>{urlLabel}:</span> |
| 59 | + <StringEdit |
| 60 | + styles={getStyles('input', nodeData)} |
| 61 | + pathString={toPathString(nodeData.path)} |
| 62 | + {...props} |
| 63 | + value={url} |
| 64 | + setValue={(val) => setUrl(val)} |
| 65 | + handleEdit={() => { |
| 66 | + handleEdit({ [textField]: text, [urlField]: url }) |
| 67 | + }} |
| 68 | + /> |
| 69 | + </div> |
| 70 | + </div> |
| 71 | + ) : ( |
| 72 | + <StringDisplay |
| 73 | + {...props} |
| 74 | + pathString={toPathString(nodeData.path)} |
| 75 | + styles={{ ...styles }} |
| 76 | + value={text} |
| 77 | + stringTruncate={stringTruncate} |
| 78 | + TextWrapper={({ children }) => { |
| 79 | + return ( |
| 80 | + <a |
| 81 | + href={url} |
| 82 | + target="_blank" |
| 83 | + rel="noreferrer" |
| 84 | + style={{ ...styles, ...linkStyles, cursor: 'pointer' }} |
| 85 | + > |
| 86 | + {children} |
| 87 | + </a> |
| 88 | + ) |
| 89 | + }} |
| 90 | + /> |
| 91 | + )} |
| 92 | + </div> |
| 93 | + ) |
| 94 | +} |
0 commit comments