diff --git a/demo/src/screens/MenuStructure.js b/demo/src/screens/MenuStructure.js index c755c44436..da530ea008 100644 --- a/demo/src/screens/MenuStructure.js +++ b/demo/src/screens/MenuStructure.js @@ -84,6 +84,11 @@ export const navigationData = { title: 'Overlays', screens: [ {title: 'Action Sheet', tags: 'action sheet cross-platform', screen: 'unicorn.components.ActionSheetScreen'}, + { + title: 'Action Sheet (Incubator)', + tags: 'action sheet', + screen: 'unicorn.components.IncubatorActionSheetScreen' + }, {title: 'Dialog', tags: 'dialog modal popup alert', screen: 'unicorn.components.DialogScreen'}, {title: 'Feature Highlight', tags: 'feature overlay', screen: 'unicorn.components.FeatureHighlightScreen'}, {title: 'Floating Button', tags: 'floating button', screen: 'unicorn.components.FloatingButtonScreen'}, @@ -99,7 +104,11 @@ export const navigationData = { {title: 'Conversation List', tags: 'list conversation', screen: 'unicorn.lists.ConversationListScreen'}, {title: 'Drawer', tags: 'drawer', screen: 'unicorn.components.DrawerScreen'}, {title: 'SortableList', tags: 'sortable list drag', screen: 'unicorn.components.SortableListScreen'}, - {title: 'HorizontalSortableList', tags: 'sortable horizontal list drag', screen: 'unicorn.components.HorizontalSortableListScreen'}, + { + title: 'HorizontalSortableList', + tags: 'sortable horizontal list drag', + screen: 'unicorn.components.HorizontalSortableListScreen' + }, {title: 'GridList', tags: 'grid list', screen: 'unicorn.components.GridListScreen'}, {title: 'SortableGridList', tags: 'sort grid list drag', screen: 'unicorn.components.SortableGridListScreen'} ] @@ -120,7 +129,11 @@ export const navigationData = { {title: 'Modal', tags: 'modal topbar screen', screen: 'unicorn.screens.ModalScreen'}, {title: 'StateScreen', tags: 'empty state screen', screen: 'unicorn.screens.EmptyStateScreen'}, {title: 'TabController', tags: 'tabbar controller native', screen: 'unicorn.components.TabControllerScreen'}, - {title: 'TabControllerWithStickyHeader', tags: 'tabbar controller native sticky header', screen: 'unicorn.components.TabControllerWithStickyHeaderScreen'}, + { + title: 'TabControllerWithStickyHeader', + tags: 'tabbar controller native sticky header', + screen: 'unicorn.components.TabControllerWithStickyHeaderScreen' + }, {title: 'Timeline', tags: 'timeline', screen: 'unicorn.components.TimelineScreen'}, { title: 'withScrollEnabler', diff --git a/demo/src/screens/incubatorScreens/ActionSheetItems.tsx b/demo/src/screens/incubatorScreens/ActionSheetItems.tsx new file mode 100644 index 0000000000..c0260a0aa1 --- /dev/null +++ b/demo/src/screens/incubatorScreens/ActionSheetItems.tsx @@ -0,0 +1,210 @@ +import React from 'react'; +import {Alert, StyleSheet} from 'react-native'; +import {Assets, BorderRadiuses, Colors, Image, ImageProps, Spacings, View} from 'react-native-ui-lib'; + +export enum TEXT_LENGTH { + NO_TEXT = 'No text', + SHORT = 'Short', + LONG = 'Long' +} + +export enum CUSTOM_TITLE_COMPONENT { + NONE = 'None', + AVATAR = 'Avatar', + ICON = 'Icon', + THUMBNAIL = 'Thumbnail' +} + +export enum OPTIONS_TYPE { + NONE = 'None', + REGULAR = 'Regular', + WITH_ICONS = 'With icons', + SECTION_HEADERS = 'Section headers', + GRID_VIEW = 'Grid view', + GRID_VIEW_LONG = 'Grid view long' +} + +export type State = { + titleLength: TEXT_LENGTH; + titleIsProminent: boolean; + titleIsClickable: boolean; + subtitleLength: TEXT_LENGTH; + showFooter: boolean; + optionsType: OPTIONS_TYPE; + visible: boolean; +}; + +export const ICONS = [ + Assets.icons.demo.settings, + Assets.icons.demo.refresh, + Assets.icons.check, + Assets.icons.x, + Assets.icons.plusSmall, + Assets.icons.demo.camera +]; + +const GRID_ITEM_CIRCLE_SIZE = 52; + +const pickOption = (option: string) => { + Alert.alert(`picked: ${option}`); +}; + +const renderCustomItem = (imageProps: ImageProps) => { + return ( + + + + + + ); +}; + +export const listItems = [ + {title: 'Open Settings', onPress: () => pickOption('Open Settings')}, + {title: 'View Notifications', onPress: () => pickOption('View Notifications')}, + {title: 'Update Profile', onPress: () => pickOption('Update Profile')}, + {title: 'Log Out', onPress: () => pickOption('Log Out')}, + {title: 'Share Post', onPress: () => pickOption('Share Post')}, + {title: 'Send Message', onPress: () => pickOption('Send Message')}, + {title: 'Take Photo', onPress: () => pickOption('Take Photo')}, + {title: 'Record Video', onPress: () => pickOption('Record Video')}, + {title: 'Add to Favorites', onPress: () => pickOption('Add to Favorites')}, + {title: 'Search', onPress: () => pickOption('Search')}, + {title: 'Refresh Feed', onPress: () => pickOption('Refresh Feed')}, + {title: 'Edit Post', onPress: () => pickOption('Edit Post')}, + {title: 'Report Issue', onPress: () => pickOption('Report Issue')}, + {title: 'Contact Support', onPress: () => pickOption('Contact Support')}, + {title: 'View Profile', onPress: () => pickOption('View Profile')}, + {title: 'Cancel', onPress: () => pickOption('Cancel')} +]; + +export const gridItems = [ + { + title: 'Open Settings', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.settings}), + onPress: () => pickOption('Open Settings') + }, + { + title: 'View Notifications', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.refresh}), + onPress: () => pickOption('View Notifications') + }, + { + title: 'Update Profile', + renderCustomItem: () => renderCustomItem({source: Assets.icons.check}), + onPress: () => pickOption('Update Profile'), + avoidDismiss: true + }, + { + title: 'Log Out', + renderCustomItem: () => renderCustomItem({source: Assets.icons.x}), + onPress: () => pickOption('Log Out') + }, + { + title: 'Share Post', + renderCustomItem: () => renderCustomItem({source: Assets.icons.plusSmall}), + onPress: () => pickOption('Share Post') + }, + { + title: 'Take Photo', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.camera}), + onPress: () => pickOption('Take Photo') + }, + { + title: 'Record Video', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.camera}), + onPress: () => pickOption('Record Video') + }, + { + title: 'Send Message', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.settings}), + onPress: () => pickOption('Send Message') + }, + { + title: 'Create Event', + renderCustomItem: () => renderCustomItem({source: Assets.icons.x}), + onPress: () => pickOption('Create Event') + }, + { + title: 'Browse Contacts', + renderCustomItem: () => renderCustomItem({source: Assets.icons.check}), + onPress: () => pickOption('Browse Contacts') + }, + { + title: 'Check Updates', + renderCustomItem: () => renderCustomItem({source: Assets.icons.plusSmall}), + onPress: () => pickOption('Check Updates') + }, + { + title: 'Provide Feedback', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.refresh}), + onPress: () => pickOption('Provide Feedback') + }, + { + title: 'View Gallery', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.camera}), + onPress: () => pickOption('View Gallery') + }, + { + title: 'Access Help', + renderCustomItem: () => renderCustomItem({source: Assets.icons.check}), + onPress: () => pickOption('Access Help') + }, + { + title: 'Explore Settings', + renderCustomItem: () => renderCustomItem({source: Assets.icons.x}), + onPress: () => pickOption('Explore Settings') + }, + { + title: 'Manage Subscriptions', + renderCustomItem: () => renderCustomItem({source: Assets.icons.plusSmall}), + onPress: () => pickOption('Manage Subscriptions') + }, + { + title: 'Change Password', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.settings}), + onPress: () => pickOption('Change Password') + }, + { + title: 'View Terms of Service', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.refresh}), + onPress: () => pickOption('View Terms of Service') + }, + { + title: 'Contact Support', + renderCustomItem: () => renderCustomItem({source: Assets.icons.x}), + onPress: () => pickOption('Contact Support') + }, + { + title: 'Manage Privacy Settings', + renderCustomItem: () => renderCustomItem({source: Assets.icons.check}), + onPress: () => pickOption('Manage Privacy Settings') + }, + { + title: 'Send Feedback', + renderCustomItem: () => renderCustomItem({source: Assets.icons.plusSmall}), + onPress: () => pickOption('Send Feedback') + }, + { + title: 'View FAQ', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.camera}), + onPress: () => pickOption('View FAQ') + }, + { + title: 'Reset App Preferences', + renderCustomItem: () => renderCustomItem({source: Assets.icons.demo.refresh}), + onPress: () => pickOption('Reset App Preferences') + } +]; + +const styles = StyleSheet.create({ + gridItemCircle: { + width: GRID_ITEM_CIRCLE_SIZE, + height: GRID_ITEM_CIRCLE_SIZE, + borderWidth: 1, + borderRadius: BorderRadiuses.br100, + borderColor: Colors.$outlineDisabled + }, + + containerStyle: {marginBottom: Spacings.s2, marginHorizontal: Spacings.s2, alignContent: 'center'} +}); diff --git a/demo/src/screens/incubatorScreens/IncubatorActionSheetScreen.tsx b/demo/src/screens/incubatorScreens/IncubatorActionSheetScreen.tsx new file mode 100644 index 0000000000..0ffd0aeb1e --- /dev/null +++ b/demo/src/screens/incubatorScreens/IncubatorActionSheetScreen.tsx @@ -0,0 +1,260 @@ +import _ from 'lodash'; +import React, {useState} from 'react'; +import {Alert, ScrollView, StyleSheet} from 'react-native'; +import {View, Button, Incubator, Text, Switch, RadioButton, RadioGroup, Typography, Colors} from 'react-native-ui-lib'; +import {listItems, gridItems, TEXT_LENGTH, OPTIONS_TYPE, State, ICONS} from './ActionSheetItems'; + +function IncubatorActionSheetScreen() { + const [actionSheetOptions, setActionSheetOptions] = useState({ + titleLength: TEXT_LENGTH.NO_TEXT, + titleIsProminent: false, + titleIsClickable: false, + subtitleLength: TEXT_LENGTH.NO_TEXT, + showFooter: false, + optionsType: OPTIONS_TYPE.NONE, + visible: false + }); + + const updateState = (newValues: Partial) => { + setActionSheetOptions(prevOptions => ({ + ...prevOptions, + ...newValues + })); + }; + + const setTitleLength = (titleLength: TEXT_LENGTH) => { + if (titleLength !== actionSheetOptions.titleLength) { + updateState({titleLength}); + } + }; + + const toggleProminent = () => { + updateState({titleIsProminent: !actionSheetOptions.titleIsProminent}); + }; + + const toggleClickable = () => { + updateState({titleIsClickable: !actionSheetOptions.titleIsClickable}); + }; + + const setSubtitleLength = (subtitleLength: TEXT_LENGTH) => { + if (subtitleLength !== actionSheetOptions.subtitleLength) { + updateState({subtitleLength}); + } + }; + + const toggleFooter = () => { + updateState({showFooter: !actionSheetOptions.showFooter}); + }; + + const setOptionsType = (optionsType: OPTIONS_TYPE) => { + if (optionsType !== actionSheetOptions.optionsType) { + updateState({optionsType}); + } + }; + + const showActionSheet = () => { + updateState({visible: true}); + }; + + const setVisible = (visible: boolean) => { + updateState({visible}); + }; + + const getTitle = () => { + const {titleLength} = actionSheetOptions; + switch (titleLength) { + case TEXT_LENGTH.NO_TEXT: + return undefined; + case TEXT_LENGTH.SHORT: + default: + return 'Title'; + case TEXT_LENGTH.LONG: + return 'This is a very long title, perhaps too long'; + } + }; + + const clicked = (text: string) => { + Alert.alert(text); + }; + + const getSubtitle = () => { + const {subtitleLength} = actionSheetOptions; + switch (subtitleLength) { + case TEXT_LENGTH.NO_TEXT: + default: + return undefined; + case TEXT_LENGTH.SHORT: + return 'Subtitle'; + case TEXT_LENGTH.LONG: + return 'This is a very long subtitle that hopefully tests two lines'; + } + }; + + const getHeaderProps = () => { + const {titleIsProminent, titleIsClickable} = actionSheetOptions; + const onPress = titleIsClickable ? clicked('Header clicked') : undefined; + const titleStyle = titleIsProminent ? {...Typography.text70BO} : undefined; + + return { + title: getTitle(), + titleProps: {accessibilityLabel: 'Custom accessibility label for ActionSheet header'}, + subtitle: getSubtitle(), + titleStyle, + onPress + }; + }; + + const getGridOptions = () => { + const {optionsType} = actionSheetOptions; + if (optionsType === OPTIONS_TYPE.GRID_VIEW || optionsType === OPTIONS_TYPE.GRID_VIEW_LONG) { + return { + numColumns: 3 + }; + } + }; + + const getList = () => { + const {optionsType} = actionSheetOptions; + switch (optionsType) { + case 'None': + return []; + case 'Regular': + return listItems; + case 'With icons': + return listItems.map((item, index) => ({ + ...item, + icon: { + source: ICONS[index % ICONS.length], + tintColor: index % ICONS.length === 2 && 'red', + style: {marginRight: 10} + } + })); + case 'Grid view': + return gridItems.slice(0, 6).map(item => ({ + ...item, + containerStyle: styles.gridItemsContainer + })); + case 'Grid view long': + return gridItems.map(item => ({ + ...item, + containerStyle: styles.gridItemsContainer + })); + case 'Section headers': + return listItems.map((item, index) => ({ + ...item, + isSectionHeader: index % 3 === 0, + titleStyle: index % 3 === 0 && {...Typography.text65}, + sectionHeaderStyle: styles.sectionHeaders + })); + default: + return []; + } + }; + + const renderRadioButton = (key: string, value: string, hasLeftMargin: boolean) => { + return ; + }; + + const renderRadioGroup = (title: string, + data: {[s: number]: string}, + initialValue?: string, + onValueChange?: (data: any) => void) => { + const radioButtons: React.ReactElement[] = []; + Object.entries(data).forEach(([key, value], index) => { + radioButtons.push(renderRadioButton(`${title}_${key}`, value, index !== _.size(data) - 1)); + }); + + return ( + + {title}: + + {radioButtons} + + + ); + }; + + const renderTitleSwitches = () => { + const {titleIsProminent, titleIsClickable} = actionSheetOptions; + + return ( + + Prominent style: + + Clickable: + + + ); + }; + + const renderActionSheet = () => { + const {visible, showFooter} = actionSheetOptions; + const list = getList(); + const headerProps = getHeaderProps(); + const gridOptions = getGridOptions(); + const footerCustomElement = showFooter ? ( + + + Footer + + + ) : undefined; + + return ( + { + console.log(`props onDismiss called!`); + setVisible(false); + }} + dialogProps={{ + bottom: true, + centerH: true, + width: '95%', + height: _.isEmpty(list) && !gridOptions ? 150 : undefined, + //@ts-ignore + headerProps + }} + gridOptions={gridOptions} + footerCustomElement={footerCustomElement} + /> + ); + }; + + return ( + + + Action Sheet + + + + {renderRadioGroup('Title', TEXT_LENGTH, actionSheetOptions.titleLength, setTitleLength)} + {renderTitleSwitches()} + {renderRadioGroup('Subtitle', TEXT_LENGTH, actionSheetOptions.subtitleLength, setSubtitleLength)} + {renderRadioGroup('Options', OPTIONS_TYPE, actionSheetOptions.optionsType, setOptionsType)} + + Add footer: + + + +