UNPKG

swingset

Version:

drop-in component library and documentation pages for next.js

113 lines (108 loc) 3.47 kB
import s from './style.module.css' import marked from 'marked' import { Fragment } from 'react' export default function PropsTable({ props }) { return ( <table className={s.root}> <thead> <tr> <th>Name</th> <th>Description</th> </tr> </thead> <tbody>{renderRows(props)}</tbody> </table> ) } function renderRows(props, prefixes = []) { const res = [] if (Array.isArray(props)) { props.map((prop) => { // render the row for the current property res.push(renderRow('[x]', prop, prefixes, true)) // render rows for sub-properties if they exist if (prop.properties) res.push(renderRows(prop.properties, [...prefixes, '[x]'])) }) } else { for (let key in props) { const value = props[key] // figure out whether the property of the current item is an array, and whether it has multiple valid types const arrayPropertyOptions = value.properties && value.properties.length ? value.properties.length > 1 ? value.properties.length : 'single' : null // render the row given the information res.push(renderRow(key, value, prefixes, null, arrayPropertyOptions)) // render rows for sub-properties if relevant if (value.properties) res.push(renderRows(value.properties, [...prefixes, key])) } } return res } function renderRow(key, value, prefixes, isArray, arrayOptions) { // this bunch of business is to ensure that object syntax chain are separated by periods, // but array syntax are not. like `foo.bar.baz` vs `foo[x].bar` // if the current item is an array syntax, we slice off the trailing period below const prefixSet = prefixes.reduce( (m, p, i) => `${m}${prefixes[i + 1]?.charAt(0) === '[' ? p : p + '.'}`, '' ) return ( <tr key={key}> <td> <code> {prefixes.length ? ( <span className={s.prefix}> {isArray ? prefixSet.slice(0, -1) : prefixSet} </span> ) : ( '' )} {key} {value.required ? <span className={s.required}>*</span> : ''} </code> <div className={s.type}>{value.type}</div> </td> <td className={s.descriptionCol}> <span dangerouslySetInnerHTML={{ __html: marked.parseInline(value.description || ''), }} /> {value.options && ( <div className={s.options}> <strong>Options: </strong> {value.options.map((opt, idx) => ( <Fragment key={opt}> <code>{JSON.stringify(opt)}</code> {idx < value.options.length - 1 && `, `} </Fragment> ))} </div> )} {value.itemType && ( <div className={s.options}> <strong>Item Type: </strong> <code>{value.itemType}</code> </div> )} {value.properties && ( <div className={s.containsNested}> {renderHelperText(arrayOptions)} </div> )} </td> </tr> ) } function renderHelperText(arrayOptions) { if (typeof arrayOptions === 'number') return `Array can contain any of the ${arrayOptions} types below:` if (arrayOptions === 'single') return 'Array members must be of the type below:' return 'Object contains nested props, see below:' }