Skip to content

Commit

Permalink
customising value display
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Nov 11, 2023
1 parent 08ec0c9 commit b3d6e95
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 61 deletions.
14 changes: 8 additions & 6 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ export default function App() {
)
}

const customRender: CustomRender = () => {
// const { type } = props
// if (type == 'Modal') {
// return () => <span style={{ color: 'blue' }}>modal</span>
// }
return null
const customRender: CustomRender = (props) => {
const { type } = props
if (type == 'DisplayPrimitive') {
const { value } = props
if (typeof value == 'boolean') {
return () => <>{value ? '👍' : '👎'}</>
}
}
}

const bootstrapClassName: ClassNameGenerator = (props) => {
Expand Down
22 changes: 22 additions & 0 deletions demo/src/FastUI/components/Json.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {FC} from 'react'

export type JSON = string | number | boolean | null | JSON[] | { [key: string]: JSON }

export interface JsonProps {
value: JSON
type: 'JSON'
}

export const JsonComp: FC<JsonProps> = ({ value }) => {
// if the value is a string, we assume it's already JSON, and parse it
if (typeof value === 'string') {
value = JSON.parse(value)
}
return (
<div className="code-block">
<pre>
<code>{JSON.stringify(value, null, 2)}</code>
</pre>
</div>
)
}
130 changes: 84 additions & 46 deletions demo/src/FastUI/components/display.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'

export type JSON = string | number | boolean | null | JSON[] | { [key: string]: JSON }
import {JsonComp, JSON} from './Json'
import {useCustomRender} from '../hooks/customRender'

// eslint-disable-next-line react-refresh/only-export-components
export enum DisplayChoices {
Expand All @@ -15,21 +15,95 @@ export enum DisplayChoices {
inline_code = 'inline_code',
}

export interface DisplayProps {
interface DisplayProps {
display?: DisplayChoices
value?: JSON
type: 'Display'
}

export const DisplayComp: FC<DisplayProps> = ({ value, display }) => {
display = display ?? DisplayChoices.auto
value = value ?? null
export type AllDisplayProps = DisplayProps | DisplayArrayProps | DisplayObjectProps | DisplayPrimitiveProps

export const DisplayComp: FC<DisplayProps> = (props) => {
const CustomRenderComp = useCustomRender(props)
if (CustomRenderComp) {
return <CustomRenderComp />
}

const display = props.display ?? DisplayChoices.auto
const value = props.value ?? null
if (display == DisplayChoices.json) {
return <DisplayJsonComp value={value} />
return <JsonComp type="JSON" value={value} />
} else if (Array.isArray(value)) {
return <DisplayArray display={display} value={value} />
return <DisplayArray type="DisplayArray" display={display} value={value} />
} else if (typeof value === 'object' && value !== null) {
return <DisplayObject display={display} value={value} />
return <DisplayObject type="DisplayObject" display={display} value={value} />
} else {
return <DisplayPrimitive type="DisplayPrimitive" display={display} value={value} />
}
}

interface DisplayArrayProps {
value: JSON[]
display?: DisplayChoices
type: 'DisplayArray'
}

export const DisplayArray: FC<DisplayArrayProps> = (props) => {
const CustomRenderComp = useCustomRender(props)
if (CustomRenderComp) {
return <CustomRenderComp />
}
const { display, value } = props

return (
<>
{value.map((v, i) => (
<span key={i}>
<DisplayComp type="Display" display={display} value={v} />,{' '}
</span>
))}
</>
)
}

interface DisplayObjectProps {
value: { [key: string]: JSON }
display?: DisplayChoices
type: 'DisplayObject'
}

export const DisplayObject: FC<DisplayObjectProps> = (props) => {
const CustomRenderComp = useCustomRender(props)
if (CustomRenderComp) {
return <CustomRenderComp />
}
const { display, value } = props

return (
<>
{Object.entries(value).map(([key, v], i) => (
<span key={key}>
{key}: <DisplayComp type="Display" display={display} key={i} value={v} />,{' '}
</span>
))}
</>
)
}

type JSONPrimitive = string | number | boolean | null

interface DisplayPrimitiveProps {
value: JSONPrimitive
display: DisplayChoices
type: 'DisplayPrimitive'
}

export const DisplayPrimitive: FC<DisplayPrimitiveProps> = (props) => {
const CustomRenderComp = useCustomRender(props)
if (CustomRenderComp) {
return <CustomRenderComp />
}
const { display, value } = props

switch (display) {
case DisplayChoices.auto:
Expand All @@ -51,42 +125,6 @@ export const DisplayComp: FC<DisplayProps> = ({ value, display }) => {
}
}

const DisplayJsonComp: FC<{ value: JSON }> = ({ value }) => {
// if the value is a string, we assume it's already JSON, and parse it
if (typeof value === 'string') {
value = JSON.parse(value)
}
return (
<div className="code-block">
<pre>
<code>{JSON.stringify(value, null, 2)}</code>
</pre>
</div>
)
}

const DisplayArray: FC<{ display?: DisplayChoices; value: JSON[] }> = ({ display, value }) => (
<>
{value.map((v, i) => (
<span key={i}>
<DisplayComp display={display} value={v} />,{' '}
</span>
))}
</>
)

const DisplayObject: FC<{ display?: DisplayChoices; value: { [key: string]: JSON } }> = ({ display, value }) => (
<>
{Object.entries(value).map(([key, v], i) => (
<span key={key}>
{key}: <DisplayComp display={display} key={i} value={v} />,{' '}
</span>
))}
</>
)

type JSONPrimitive = string | number | boolean | null

const DisplayNull: FC = () => {
return <span className="fu-null">&mdash;</span>
}
Expand Down Expand Up @@ -144,7 +182,7 @@ const DisplayDuration: FC<{ value: JSONPrimitive }> = ({ value }) => {

// usage as_title('what_ever') > 'What Ever'
// eslint-disable-next-line react-refresh/only-export-components
export const as_title = (s: string): string => s.replace(/(_|-)/g, ' ').replace(/(_|\b)\w/g, (l) => l.toUpperCase())
export const as_title = (s: string): string => s.replace(/[_-]/g, ' ').replace(/(_|\b)\w/g, (l) => l.toUpperCase())

const DisplayAsTitle: FC<{ value: JSONPrimitive }> = ({ value }) => {
if (value === null) {
Expand Down
14 changes: 14 additions & 0 deletions demo/src/FastUI/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { ButtonComp, ButtonProps } from './button'
import { LinkComp, LinkProps } from './link'
import { ModalComp, ModalProps } from './modal'
import { TableComp, TableProps } from './table'
import {AllDisplayProps, DisplayArray, DisplayComp, DisplayObject, DisplayPrimitive} from './display'
import {JsonComp, JsonProps} from './Json'

export type FastProps =
| TextProps
Expand All @@ -19,6 +21,8 @@ export type FastProps =
| ModalProps
| TableProps
| LinkProps
| AllDisplayProps
| JsonProps

export const AnyComp: FC<FastProps> = (props) => {
const { DisplayError } = useContext(ErrorContext)
Expand Down Expand Up @@ -50,6 +54,16 @@ export const AnyComp: FC<FastProps> = (props) => {
return <ModalComp {...props} />
case 'Table':
return <TableComp {...props} />
case 'Display':
return <DisplayComp {...props} />
case 'DisplayArray':
return <DisplayArray {...props} />
case 'DisplayObject':
return <DisplayObject {...props} />
case 'DisplayPrimitive':
return <DisplayPrimitive {...props} />
case 'JSON':
return <JsonComp {...props} />
default:
console.log('unknown component type:', type, props)
return <DisplayError title="Invalid Server Response" description={`Unknown component type: "${type}"`} />
Expand Down
8 changes: 4 additions & 4 deletions demo/src/FastUI/components/table.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { FC } from 'react'
import { ClassName, useClassNameGenerator } from '../hooks/className'
import { PageEvent, GoToEvent } from '../hooks/event'
import {as_title, DisplayChoices, DisplayComp} from './display'
import {as_title, DisplayChoices, DisplayComp } from './display'
import { LinkRender } from './link'
import type { JSON } from './display'
import type { JSON } from './Json'

interface ColumnProps {
field: string
Expand Down Expand Up @@ -72,14 +72,14 @@ const Cell: FC<CellProps> = ({ row, column }) => {
return (
<td>
<LinkRender onClick={event}>
<DisplayComp display={display} value={value} />
<DisplayComp type="Display" display={display} value={value} />
</LinkRender>
</td>
)
} else {
return (
<td>
<DisplayComp display={display} value={value} />
<DisplayComp type="Display" display={display} value={value} />
</td>
)
}
Expand Down
6 changes: 2 additions & 4 deletions demo/src/FastUI/hooks/customRender.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { createContext, FC, useContext } from 'react'
import { FastProps } from '../components'

export type CustomRender = (props: FastProps) => FC | null
export type CustomRender = (props: FastProps) => FC | void

export const CustomRenderContext = createContext<CustomRender | null>(null)

export const useCustomRender = (props: FastProps): FC | null => {
export const useCustomRender = (props: FastProps): FC | void => {
const customRender = useContext(CustomRenderContext)

if (customRender) {
return customRender(props)
} else {
return null
}
}
3 changes: 2 additions & 1 deletion demo/src/FastUI/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { ClassNameContext, ClassNameGenerator } from './hooks/className'
import { ErrorContextProvider, ErrorDisplayType } from './hooks/error'
import { CustomRender, CustomRenderContext } from './hooks/customRender'
import { FastProps } from './components'
import { DisplayChoices } from './components/display'

export type { ClassNameGenerator, CustomRender, ErrorDisplayType, FastProps }
export type { ClassNameGenerator, CustomRender, ErrorDisplayType, FastProps, DisplayChoices }

export interface FastUIProps {
rootUrl: string
Expand Down

0 comments on commit b3d6e95

Please sign in to comment.