Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"type": "module",
"main": "./dist/react-native-portable-text.es.js",
"module": "./dist/react-native-portable-text.es.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
Expand Down
36 changes: 35 additions & 1 deletion src/components/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {View, Text} from 'react-native'
import type {PortableTextComponent} from '@portabletext/react'
import type {PortableTextBlock, PortableTextBlockStyle} from '@portabletext/types'

import {blockStyles, textStyles} from './styles'
import {PortableTextFontTheme, blockStyles, getTextStylesWithTheme, textStyles} from './styles'

type BlockStyleName = keyof typeof blockStyles

Expand All @@ -18,6 +18,24 @@ export const DefaultBlock: PortableTextComponent<PortableTextBlock> = ({children
)
}

export const getDefaultBlockWithTheme = (
theme: PortableTextFontTheme,
): PortableTextComponent<PortableTextBlock> => {
const textStylesWithTheme = getTextStylesWithTheme(theme)

const DefaultBlockWithTheme: PortableTextComponent<PortableTextBlock> = ({children, value}) => {
const style = (value.style || 'normal') as BlockStyleName

return (
<View style={blockStyles[style]}>
<Text style={textStylesWithTheme[style]}>{children}</Text>
</View>
)
}

return DefaultBlockWithTheme
}

export const defaultBlockStyles: Record<
PortableTextBlockStyle,
PortableTextComponent<PortableTextBlock> | undefined
Expand All @@ -31,3 +49,19 @@ export const defaultBlockStyles: Record<
h5: DefaultBlock,
h6: DefaultBlock,
}

export const getDefaultBlockStylesWithTheme = (
theme: PortableTextFontTheme,
): Record<PortableTextBlockStyle, PortableTextComponent<PortableTextBlock> | undefined> => {
const defaultBlockWithTheme = getDefaultBlockWithTheme(theme)
return {
normal: defaultBlockWithTheme,
blockquote: defaultBlockWithTheme,
h1: defaultBlockWithTheme,
h2: defaultBlockWithTheme,
h3: defaultBlockWithTheme,
h4: defaultBlockWithTheme,
h5: defaultBlockWithTheme,
h6: defaultBlockWithTheme,
}
}
27 changes: 24 additions & 3 deletions src/components/defaults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import React from 'react'
import {Text} from 'react-native'
import type {PortableTextReactComponents} from '@portabletext/react'

import {defaultMarks} from './marks'
import {defaultBlockStyles} from './block'
import {DefaultList, defaultListItems} from './list'
import {defaultMarks, getDefaultMarksWithTheme} from './marks'
import {defaultBlockStyles, getDefaultBlockStylesWithTheme} from './block'
import {DefaultList, defaultListItems, getDefaultListItemsWithTheme} from './list'
import {
DefaultUnknownType,
DefaultUnknownMark,
DefaultUnknownList,
DefaultUnknownListItem,
DefaultUnknownBlockStyle,
} from './unknown'
import {PortableTextFontTheme} from './styles'

export const DefaultHardBreak = () => <Text>{'\n'}</Text>

Expand All @@ -30,3 +31,23 @@ export const defaultComponents: PortableTextReactComponents = {
unknownListItem: DefaultUnknownListItem,
unknownBlockStyle: DefaultUnknownBlockStyle,
}

export const getDefaultComponentsWithTheme = (
theme: PortableTextFontTheme,
): PortableTextReactComponents => {
return {
types: {},

block: getDefaultBlockStylesWithTheme(theme),
marks: getDefaultMarksWithTheme(theme),
list: DefaultList,
listItem: getDefaultListItemsWithTheme(theme),
hardBreak: DefaultHardBreak,

unknownType: DefaultUnknownType,
unknownMark: DefaultUnknownMark,
unknownList: DefaultUnknownList,
unknownListItem: DefaultUnknownListItem,
unknownBlockStyle: DefaultUnknownBlockStyle,
}
}
28 changes: 27 additions & 1 deletion src/components/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {View, Text} from 'react-native'
import type {PortableTextListComponent, PortableTextListItemComponent} from '@portabletext/react'
import type {PortableTextListItemType} from '@portabletext/types'

import {listStyles} from './styles'
import {PortableTextFontTheme, getListStylesWithTheme, listStyles} from './styles'

export const DefaultList: PortableTextListComponent = ({value, children}) => {
const base = value.level > 1 ? listStyles.listDeep : listStyles.list
Expand All @@ -28,3 +28,29 @@ export const defaultListItems: Record<
</View>
),
}

export const getDefaultListItemsWithTheme = (
theme: PortableTextFontTheme,
): Record<PortableTextListItemType, PortableTextListItemComponent | undefined> => {
const listStylesWithTheme = getListStylesWithTheme(theme)

const defaultListItemsWithTheme: Record<
PortableTextListItemType,
PortableTextListItemComponent | undefined
> = {
bullet: ({children}) => (
<View style={listStylesWithTheme.listItemWrapper}>
<Text style={listStylesWithTheme.bulletListIcon}>{'\u00B7'}</Text>
<Text style={listStylesWithTheme.listItem}>{children}</Text>
</View>
),
number: ({children, index}) => (
<View style={listStylesWithTheme.listItemWrapper}>
<Text style={listStylesWithTheme.bulletListIcon}>{index + 1}. </Text>
<Text style={listStylesWithTheme.listItem}>{children}</Text>
</View>
),
}

return defaultListItemsWithTheme
}
19 changes: 18 additions & 1 deletion src/components/marks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, {useCallback} from 'react'
import {Text, Linking} from 'react-native'
import type {PortableTextMarkComponent} from '@portabletext/react'
import type {TypedObject} from '@portabletext/types'
import {markStyles} from './styles'
import {PortableTextFontTheme, markStyles, getMarkStylesWithTheme} from './styles'

interface DefaultLink extends TypedObject {
_type: 'link'
Expand All @@ -28,3 +28,20 @@ export const defaultMarks: Record<string, PortableTextMarkComponent | undefined>
'strike-through': ({children}) => <Text style={markStyles.strikeThrough}>{children}</Text>,
link,
}

export const getDefaultMarksWithTheme = (
theme: PortableTextFontTheme,
): Record<string, PortableTextMarkComponent | undefined> => {
const markStylesWithTheme = getMarkStylesWithTheme(theme)

return {
em: ({children}) => <Text style={markStylesWithTheme.em}>{children}</Text>,
strong: ({children}) => <Text style={markStylesWithTheme.strong}>{children}</Text>,
code: ({children}) => <Text style={markStylesWithTheme.code}>{children}</Text>,
underline: ({children}) => <Text style={markStylesWithTheme.underline}>{children}</Text>,
'strike-through': ({children}) => (
<Text style={markStylesWithTheme.strikeThrough}>{children}</Text>
),
link,
}
}
96 changes: 95 additions & 1 deletion src/components/styles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
import {StyleSheet} from 'react-native'
import {StyleSheet, TextStyle} from 'react-native'

type LinkTheme = Record<'link', {color: string}>

type StrongTheme = Record<'strong', {fontWeight: TextStyle['fontWeight']; fontFamily: string}>

type CodeTheme = Record<'code', {backgroundColor: string; color: string}>

export type PortableTextFontTheme = Record<
keyof typeof blockStyles | 'bulletListIcon' | 'listItem',
{
fontWeight?: TextStyle['fontWeight']
fontSize?: number
color?: string
fontFamily?: string
lineHeight?: number
}
> &
LinkTheme &
StrongTheme &
CodeTheme

export const blockStyles = StyleSheet.create({
normal: {marginBottom: 16},
Expand Down Expand Up @@ -45,6 +65,43 @@ export const listStyles = StyleSheet.create({
},
})

export const getListStylesWithTheme = (theme: PortableTextFontTheme) =>
StyleSheet.create({
list: {
marginVertical: 16,
},

listDeep: {
marginVertical: 0,
},

listItem: {
flex: 1,
flexWrap: 'wrap',
flexDirection: 'row',
fontSize: theme.listItem.fontSize,
fontWeight: theme.listItem.fontWeight,
color: theme.listItem.color,
fontFamily: theme.listItem.fontFamily,
lineHeight: theme.listItem.lineHeight,
},

bulletListIcon: {
marginLeft: 10,
marginRight: 10,
fontSize: theme.bulletListIcon.fontSize,
fontWeight: theme.bulletListIcon.fontWeight,
color: theme.bulletListIcon.color,
fontFamily: theme.bulletListIcon.fontFamily,
lineHeight: theme.bulletListIcon.lineHeight,
},

listItemWrapper: {
flexDirection: 'row',
justifyContent: 'flex-start',
},
})

export const textStyles = StyleSheet.create({
h1: {
fontWeight: 'bold',
Expand Down Expand Up @@ -80,6 +137,10 @@ export const textStyles = StyleSheet.create({
blockquote: {},
})

export const getTextStylesWithTheme = (theme: PortableTextFontTheme) => {
return StyleSheet.create(theme)
}

export const markStyles = StyleSheet.create({
strong: {
fontWeight: 'bold',
Expand Down Expand Up @@ -109,6 +170,39 @@ export const markStyles = StyleSheet.create({
},
})

export const getMarkStylesWithTheme = (theme: PortableTextFontTheme) => {
return StyleSheet.create({
strong: {
fontWeight: theme.strong.fontWeight,
fontFamily: theme.strong.fontFamily,
},

em: {
fontStyle: 'italic',
},

link: {
textDecorationLine: 'underline',
color: theme.link.color,
},

underline: {
textDecorationLine: 'underline',
},

strikeThrough: {
textDecorationLine: 'line-through',
},

code: {
paddingVertical: 3,
paddingHorizontal: 5,
backgroundColor: theme.code.backgroundColor,
color: theme.code.color,
},
})
}

export const utilityStyles = StyleSheet.create({
hidden: {
display: 'none',
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from '@portabletext/react'
export {PortableText} from './react-native-portable-text'
export {defaultComponents} from './components/defaults'
export {defaultComponents, getDefaultComponentsWithTheme} from './components/defaults'
export type {PortableTextFontTheme} from './components/styles'
11 changes: 5 additions & 6 deletions src/react-native-portable-text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import React from 'react'
import type {PortableTextProps} from '@portabletext/react'
import type {TypedObject, PortableTextBlock} from '@portabletext/types'
import {PortableText as BasePortableText, mergeComponents} from '@portabletext/react'
import {defaultComponents} from './components/defaults'
import {defaultComponents, getDefaultComponentsWithTheme} from './components/defaults'
import {PortableTextFontTheme} from './components/styles'

export function PortableText<B extends TypedObject = PortableTextBlock>(
props: Omit<PortableTextProps<B>, 'listNestingMode'>,
props: Omit<PortableTextProps<B>, 'listNestingMode'> & {theme?: PortableTextFontTheme},
) {
const components = props.theme ? getDefaultComponentsWithTheme(props.theme) : defaultComponents
return (
<BasePortableText
{...props}
components={mergeComponents(defaultComponents, props.components ?? {})}
/>
<BasePortableText {...props} components={mergeComponents(components, props.components ?? {})} />
)
}
Loading