import React, { Component, useContext } from 'react'; import { StyleSheet, Animated, Easing } from 'react-native'; import { lightTheme } from '../../constants/themes'; import type { CustomStyles, ToggleData, ToolbarCustom, ToolbarTheme, } from '../../types'; export interface ContextProps { apply: (name: string, value: any) => void; selectedFormats: object; isSelected: (name: string, value: any) => boolean; theme: ToolbarTheme; show: (name: string, options: Array<ToggleData>) => void; hide: Function; open: boolean; options: Array<ToggleData>; selectionName: string; getSelected: (name: string) => any; styles?: CustomStyles; } const ToolbarContext = React.createContext<ContextProps>({ apply: () => {}, show: () => {}, hide: () => {}, selectedFormats: {}, open: false, isSelected: () => false, theme: lightTheme, options: [], selectionName: '', getSelected: () => false, }); export const ToolbarConsumer = ToolbarContext.Consumer; interface ProviderProps { format: Function; selectedFormats: Record<string, any>; theme: ToolbarTheme; custom?: ToolbarCustom; styles?: CustomStyles; } interface ProviderState { open: boolean; isAnimating: boolean; options: Array<ToggleData>; name: string; } export class ToolbarProvider extends Component<ProviderProps, ProviderState> { animatedValue: Animated.Value; constructor(props: ProviderProps) { super(props); this.state = { open: false, isAnimating: false, options: [], name: '', }; this.animatedValue = new Animated.Value(0); } show = (name: string, options: Array<ToggleData>) => { if (this.state.isAnimating) return; const { theme } = this.props; if (theme) { this.setState({ options, name, isAnimating: true }, () => { Animated.timing(this.animatedValue, { toValue: 2 * theme.size + 14, duration: 200, easing: Easing.sin, useNativeDriver: false, }).start(() => this.setState({ open: true, isAnimating: false })); }); } }; hide = () => { if (this.state.isAnimating) return; const { theme } = this.props; if (theme) { this.setState({ isAnimating: true }, () => { Animated.timing(this.animatedValue, { toValue: theme.size + 10, duration: 200, easing: Easing.linear, useNativeDriver: false, }).start(() => { this.setState({ name: '', open: false, options: [], isAnimating: false, }); }); }); } }; componentDidMount() { const { theme } = this.props; this.animatedValue = new Animated.Value(theme.size + 10); } isSelected = (name: string, value: any = true): boolean => { const { selectedFormats } = this.props; const selected = selectedFormats[name]; return selected ? selected === value : value === false; }; getSelected = (name: string): any => { const { selectedFormats } = this.props; const selected = selectedFormats[name]; return selected ? selected : false; }; apply = (name: string, value: any) => { const { format, custom } = this.props; if (custom?.actions) custom.actions.find((x) => x === name); if (custom?.actions && custom?.actions?.indexOf(name) > -1) { if (custom?.handler) custom.handler(name, value); } else { format(name, value); } }; render() { const { selectedFormats, children, theme, styles } = this.props; const { open, options, name } = this.state; const defaultStyles = makeStyles(theme); const rootStyle = styles?.toolbar?.provider ? styles?.toolbar?.provider(defaultStyles.root) : defaultStyles.root; return ( <ToolbarContext.Provider value={{ selectedFormats, apply: this.apply, isSelected: this.isSelected, theme, open, show: this.show, hide: this.hide, getSelected: this.getSelected, selectionName: name, options, styles, }} > <Animated.View style={[ rootStyle, { height: this.animatedValue, }, ]} > {children} </Animated.View> </ToolbarContext.Provider> ); } } const makeStyles = (theme: ToolbarTheme) => StyleSheet.create({ root: { borderTopWidth: 1, borderLeftWidth: 1, borderRightWidth: 1, borderColor: theme.color, position: 'relative', backgroundColor: theme.background, width: '100%', }, }); export const withToolbar = (MyComponent: any) => { const WrappedComponent = React.forwardRef((props, ref) => ( <ToolbarContext.Consumer> {(context) => ( <MyComponent {...props} ref={ref} apply={context.apply} selectedFormats={context.selectedFormats} /> )} </ToolbarContext.Consumer> )); return WrappedComponent; }; export const useToolbar = (): ContextProps => useContext(ToolbarContext);