From 6fd60178744c14669d6f6edf65148bb0f27b9d86 Mon Sep 17 00:00:00 2001 From: M-i-k-e-l Date: Thu, 21 Aug 2025 13:07:56 +0300 Subject: [PATCH 1/4] Incubator.Gradient - new component --- demo/src/index.js | 3 + demo/src/screens/ExampleScreenPresenter.tsx | 68 ++++++++--- demo/src/screens/MenuStructure.js | 3 +- .../IncubatorGradientScreen.tsx | 114 ++++++++++++++++++ demo/src/screens/incubatorScreens/index.js | 1 + src/incubator/gradient/BorderGradient.tsx | 34 ++++++ src/incubator/gradient/CircleGradient.tsx | 25 ++++ src/incubator/gradient/RectangleGradient.tsx | 24 ++++ .../__tests__/useAngleTransform.spec.ts | 44 +++++++ src/incubator/gradient/index.tsx | 38 ++++++ src/incubator/gradient/types.ts | 35 ++++++ src/incubator/gradient/useAngleTransform.ts | 68 +++++++++++ src/incubator/index.ts | 1 + .../LinearGradientPackage.ts | 6 +- 14 files changed, 447 insertions(+), 17 deletions(-) create mode 100644 demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx create mode 100644 src/incubator/gradient/BorderGradient.tsx create mode 100644 src/incubator/gradient/CircleGradient.tsx create mode 100644 src/incubator/gradient/RectangleGradient.tsx create mode 100644 src/incubator/gradient/__tests__/useAngleTransform.spec.ts create mode 100644 src/incubator/gradient/index.tsx create mode 100644 src/incubator/gradient/types.ts create mode 100644 src/incubator/gradient/useAngleTransform.ts diff --git a/demo/src/index.js b/demo/src/index.js index bc0c42b433..95788454be 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -263,6 +263,9 @@ module.exports = { get IncubatorCalendarScreen() { return require('./screens/incubatorScreens/IncubatorCalendarScreen').default; }, + get IncubatorGradient() { + return require('./screens/incubatorScreens/IncubatorGradientScreen').default; + }, // realExamples get AppleMusic() { return require('./screens/realExamples/AppleMusic').default; diff --git a/demo/src/screens/ExampleScreenPresenter.tsx b/demo/src/screens/ExampleScreenPresenter.tsx index 1f6ac2abb1..ed889f391f 100644 --- a/demo/src/screens/ExampleScreenPresenter.tsx +++ b/demo/src/screens/ExampleScreenPresenter.tsx @@ -16,12 +16,22 @@ import { View } from 'react-native-ui-lib'; -interface RadioGroupOptions { +interface StateOptions { + state?: string; + setState?: React.Dispatch>; +} + +interface RadioGroupBaseOptions { isRow?: boolean; - afterValueChanged?: () => void; useValueAsLabel?: boolean; } +type RadioGroupOptions = + | (RadioGroupBaseOptions & { + afterValueChanged?: () => void; + }) + | (RadioGroupBaseOptions & StateOptions); + interface BooleanGroupOptions { spread?: boolean; afterValueChanged?: () => void; @@ -29,11 +39,6 @@ interface BooleanGroupOptions { setState?: React.Dispatch>; } -interface SegmentsExtraOptions { - state?: string; - setState?: React.Dispatch>; -} - export function renderHeader(title: string, others?: TextProps) { return ( @@ -103,9 +108,10 @@ export function renderBooleanGroup(title: string, options: string[]) { export function renderRadioGroup(title: string, key: string, options: object, - {isRow, afterValueChanged, useValueAsLabel}: RadioGroupOptions = {}) { // @ts-ignore - const value = this.state[key]; + {isRow, afterValueChanged, useValueAsLabel, state, setState}: RadioGroupOptions = {}) { + // @ts-ignore + const value = state ?? this.state[key]; return ( {!_.isUndefined(title) && ( @@ -118,7 +124,18 @@ export function renderRadioGroup(title: string, style={isRow && styles.rowWrap} initialValue={value} // @ts-ignore - onValueChange={value => this.setState({[key]: value}, afterValueChanged)} + onValueChange={value => { + if (setState) { + setState(value); + if (afterValueChanged) { + // eslint-disable-next-line no-restricted-syntax + console.error('afterValueChanged is not supported together with the state option'); + } + } else { + // @ts-ignore + this.setState({[key]: value}, afterValueChanged); + } + }} > {_.map(options, (value, key) => { return ( @@ -159,9 +176,25 @@ export function renderColorOption(title: string, export function renderSliderOption(title: string, key: string, - {min = 0, max = 10, step = 1, initial = 0, sliderText = ''}) { + { + min = 0, + max = 10, + step = 1, + initial = 0, + sliderText = '', + state, + setState + }: { + min?: number; + max?: number; + step?: number; + initial?: number; + sliderText?: string; + state?: number; + setState?: React.Dispatch>; + }) { // @ts-ignore - const value = this.state[key] || initial; + const value = state ?? this.state[key] ?? initial; return ( @@ -177,7 +210,14 @@ export function renderSliderOption(title: string, maximumValue={max} step={step} // @ts-ignore - onValueChange={value => this.setState({[key]: value})} + onValueChange={value => { + if (setState) { + setState(value); + } else { + // @ts-ignore + this.setState({[key]: value}); + } + }} /> {sliderText} @@ -191,7 +231,7 @@ export function renderSliderOption(title: string, export function renderMultipleSegmentOptions(title: string, key: string, options: (SegmentedControlItemProps & {value: any})[], - {state, setState}: SegmentsExtraOptions = {}) { + {state, setState}: StateOptions = {}) { // @ts-ignore const value = state ?? this.state[key]; const index = _.findIndex(options, {value}); diff --git a/demo/src/screens/MenuStructure.js b/demo/src/screens/MenuStructure.js index c63e9edf65..0a6753b391 100644 --- a/demo/src/screens/MenuStructure.js +++ b/demo/src/screens/MenuStructure.js @@ -208,7 +208,8 @@ export const navigationData = { tags: 'text field expandable input picker', screen: 'unicorn.components.IncubatorExpandableOverlayScreen' }, - {title: 'PanView', tags: 'pan swipe drag', screen: 'unicorn.incubator.PanViewScreen'} + {title: 'PanView', tags: 'pan swipe drag', screen: 'unicorn.incubator.PanViewScreen'}, + {title: 'Gradient', tags: 'gradient', screen: 'unicorn.components.IncubatorGradientScreen'} ] }, Inspirations: { diff --git a/demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx b/demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx new file mode 100644 index 0000000000..8dbcc17798 --- /dev/null +++ b/demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx @@ -0,0 +1,114 @@ +import React, {useEffect, useMemo, useState} from 'react'; +import {Assets, View, Text, Incubator, Icon, Colors} from 'react-native-ui-lib'; +import {renderRadioGroup, renderSliderOption} from '../ExampleScreenPresenter'; + +const {Gradient} = Incubator; + +const COLORS = [Colors.$backgroundPrimaryHeavy, Colors.$backgroundPrimaryHeavy, Colors.$backgroundPrimaryMedium]; + +const GradientScreen = () => { + const [type, setType] = useState('rectangle'); + const [children, setChildren] = useState('none'); + const [alignment, setAlignment] = useState('none'); + const [size, setSize] = useState('fixed'); + const [error, setError] = useState(''); + const [angle, setAngle] = useState(0); + + const gradientProps = useMemo(() => { + switch (type) { + case 'rectangle': + return size === 'fixed' ? {type: 'rectangle', width: 100, height: 100} : {type: 'rectangle'}; + case 'circle': + return size === 'fixed' ? {type: 'circle', radius: 50} : {type: 'circle'}; + case 'border': + return size === 'fixed' ? {type: 'border', width: 100, height: 100} : {type: 'border'}; + } + }, [type, size]); + + const childrenProps = useMemo(() => { + switch (children) { + case 'shortText': + return Lorem ipsum dolor sit amet.; + case 'text': + return ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat + nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit + anim id est laborum. + + ); + case 'icon': + return ; + } + }, [children]); + + const alignmentProp = useMemo(() => { + switch (alignment) { + case 'none': + return undefined; + case 'center': + return {center: true}; + case 'centerH': + return {centerH: true}; + case 'centerV': + return {centerV: true}; + } + }, [alignment]); + + useEffect(() => { + if (children === 'none' && size === 'flex' && type !== 'border') { + setError('No children + flex gives no gradient'); + } else if (size === 'flex' && type === 'circle') { + setError('flex size will result with an ellipse instead of a circle'); + } else { + setError(''); + } + }, [children, size, type]); + + return ( + + {renderRadioGroup('Select type', + 'type', + {Rectangle: 'rectangle', Circle: 'circle', Border: 'border'}, + {isRow: true, state: type, setState: setType})} + {renderRadioGroup('Select children', + 'children', + {No: 'none', 'Short text': 'shortText', Text: 'text', Icon: 'icon'}, + {isRow: true, state: children, setState: setChildren})} + {renderRadioGroup('Select alignment', + 'alignment', + {None: 'none', Center: 'center', CenterH: 'centerH', CenterV: 'centerV'}, + {isRow: true, state: alignment, setState: setAlignment})} + {renderRadioGroup('Select size', + 'size', + {Fixed: 'fixed', Flex: 'flex'}, + {isRow: true, state: size, setState: setSize})} + + {renderSliderOption('Angle', 'angle', { + min: 0, + max: 360, + step: 1, + state: angle, + setState: setAngle + })} + + + {childrenProps} + + + {error} + + + ); +}; + +export default GradientScreen; diff --git a/demo/src/screens/incubatorScreens/index.js b/demo/src/screens/incubatorScreens/index.js index 247080359c..e97cb0255d 100644 --- a/demo/src/screens/incubatorScreens/index.js +++ b/demo/src/screens/incubatorScreens/index.js @@ -6,4 +6,5 @@ export function registerScreens(registrar) { registrar('unicorn.components.IncubatorToastScreen', () => require('./IncubatorToastScreen').default); registrar('unicorn.incubator.PanViewScreen', () => require('./PanViewScreen').default); registrar('unicorn.components.IncubatorSliderScreen', () => require('./IncubatorSliderScreen').default); + registrar('unicorn.components.IncubatorGradientScreen', () => require('./IncubatorGradientScreen').default); } diff --git a/src/incubator/gradient/BorderGradient.tsx b/src/incubator/gradient/BorderGradient.tsx new file mode 100644 index 0000000000..3e9e290ed9 --- /dev/null +++ b/src/incubator/gradient/BorderGradient.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import {LinearGradientPackage} from 'optionalDeps'; +const LinearGradient = LinearGradientPackage?.default; +import {BorderGradientProps} from './types'; +import View from '../../components/view'; +import Spacings from '../../style/spacings'; +import Colors from '../../style/colors'; +import useAngleTransform from './useAngleTransform'; + +const BorderGradient = (props: BorderGradientProps) => { + const {colors, borderWidth = Spacings.s1, borderRadius, children, width, height, angle, ...others} = props; + + const innerWidth = width ? width - borderWidth * 2 : undefined; + const innerHeight = height ? height - borderWidth * 2 : undefined; + const {start, end} = useAngleTransform({angle}); + + return ( + + + + {children} + + + + ); +}; + +export default BorderGradient; diff --git a/src/incubator/gradient/CircleGradient.tsx b/src/incubator/gradient/CircleGradient.tsx new file mode 100644 index 0000000000..6221edc84d --- /dev/null +++ b/src/incubator/gradient/CircleGradient.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {LinearGradientPackage} from 'optionalDeps'; +const LinearGradient = LinearGradientPackage?.default; +import {CircleGradientProps} from './types'; +import View from '../../components/view'; +import useAngleTransform from './useAngleTransform'; + +const CircleGradient = (props: CircleGradientProps) => { + const {colors, radius, angle, children, ...others} = props; + + const internalDiameter = radius ? radius * 2 : undefined; + const {start, end} = useAngleTransform({angle}); + + return ( + + + + {children} + + + + ); +}; + +export default CircleGradient; diff --git a/src/incubator/gradient/RectangleGradient.tsx b/src/incubator/gradient/RectangleGradient.tsx new file mode 100644 index 0000000000..304a72e2e0 --- /dev/null +++ b/src/incubator/gradient/RectangleGradient.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import {LinearGradientPackage} from 'optionalDeps'; +const LinearGradient = LinearGradientPackage?.default; +import {RectangleGradientProps} from './types'; +import View from '../../components/view'; +import useAngleTransform from './useAngleTransform'; + +const RectangleGradient = (props: RectangleGradientProps) => { + const {colors, width, height, angle, children, ...others} = props; + + const {start, end} = useAngleTransform({angle}); + + return ( + + + + {children} + + + + ); +}; + +export default RectangleGradient; diff --git a/src/incubator/gradient/__tests__/useAngleTransform.spec.ts b/src/incubator/gradient/__tests__/useAngleTransform.spec.ts new file mode 100644 index 0000000000..870eb3b9bd --- /dev/null +++ b/src/incubator/gradient/__tests__/useAngleTransform.spec.ts @@ -0,0 +1,44 @@ +import {_forTesting} from '../useAngleTransform'; +const {getStartEndFromAngle} = _forTesting; + +describe('useAngleTransform', () => { + it('getStartEndFromAngle - default', () => { + expect(getStartEndFromAngle()).toEqual({start: {x: 0.5, y: 1.0}, end: {x: 0.5, y: 0.0}}); + }); + + it('getStartEndFromAngle - 0', () => { + expect(getStartEndFromAngle(0)).toEqual({start: {x: 0.5, y: 1.0}, end: {x: 0.5, y: 0.0}}); + }); + + it('getStartEndFromAngle - 45', () => { + expect(getStartEndFromAngle(45)).toEqual({start: {x: 0.0, y: 1.0}, end: {x: 1.0, y: 0.0}}); + }); + + it('getStartEndFromAngle - 90', () => { + expect(getStartEndFromAngle(90)).toEqual({start: {x: 0.0, y: 0.5}, end: {x: 1.0, y: 0.5}}); + }); + + it('getStartEndFromAngle - 135', () => { + expect(getStartEndFromAngle(135)).toEqual({start: {x: 0.0, y: 0.0}, end: {x: 1.0, y: 1.0}}); + }); + + it('getStartEndFromAngle - 180', () => { + expect(getStartEndFromAngle(180)).toEqual({start: {x: 0.5, y: 0.0}, end: {x: 0.5, y: 1.0}}); + }); + + it('getStartEndFromAngle - 225', () => { + expect(getStartEndFromAngle(225)).toEqual({start: {x: 1.0, y: 0.0}, end: {x: 0.0, y: 1.0}}); + }); + + it('getStartEndFromAngle - 270', () => { + expect(getStartEndFromAngle(270)).toEqual({start: {x: 1.0, y: 0.5}, end: {x: 0.0, y: 0.5}}); + }); + + it('getStartEndFromAngle - 315', () => { + expect(getStartEndFromAngle(315)).toEqual({start: {x: 1.0, y: 1.0}, end: {x: 0.0, y: 0.0}}); + }); + + it('getStartEndFromAngle - 360', () => { + expect(getStartEndFromAngle(360)).toEqual({start: {x: 0.5, y: 1.0}, end: {x: 0.5, y: 0.0}}); + }); +}); diff --git a/src/incubator/gradient/index.tsx b/src/incubator/gradient/index.tsx new file mode 100644 index 0000000000..7f2106f5e3 --- /dev/null +++ b/src/incubator/gradient/index.tsx @@ -0,0 +1,38 @@ +import React, {useEffect} from 'react'; +import {LinearGradientPackage} from 'optionalDeps'; +const LinearGradient = LinearGradientPackage?.default; +import {LogService} from 'services'; +import {GradientProps, RectangleGradientProps, CircleGradientProps, BorderGradientProps} from './types'; +import RectangleGradient from './RectangleGradient'; +import CircleGradient from './CircleGradient'; +import BorderGradient from './BorderGradient'; + +export {GradientProps}; + +const Gradient = (props: GradientProps) => { + const {type = 'rectangle', ...others} = props; + + useEffect(() => { + if (LinearGradient === undefined) { + LogService.error(`RNUILib SkeletonView's requires installing "react-native-linear-gradient" dependency`); + } + }, []); + + if (!LinearGradient) { + return null; + } + + if (type === 'rectangle') { + return ; + } + + if (type === 'circle') { + return ; + } + + if (type === 'border') { + return ; + } +}; + +export default Gradient; diff --git a/src/incubator/gradient/types.ts b/src/incubator/gradient/types.ts new file mode 100644 index 0000000000..987d29ddc0 --- /dev/null +++ b/src/incubator/gradient/types.ts @@ -0,0 +1,35 @@ +import type {LinearGradientProps} from 'react-native-linear-gradient'; + +type CommonGradientProps = Pick & { + angle?: number; + center?: boolean; + centerH?: boolean; + centerV?: boolean; +}; + +// type GradientType = 'rectangle' | 'circle' | 'border'; + +export type GradientProps = + | ({ + type: 'rectangle'; + } & RectangleGradientProps) + | ({ + type: 'circle'; + } & CircleGradientProps) + | ({ + type: 'border'; + } & BorderGradientProps); + +export type RectangleGradientProps = CommonGradientProps & { + width?: number; + height?: number; +}; + +export type CircleGradientProps = CommonGradientProps & { + radius: number; +}; + +export type BorderGradientProps = RectangleGradientProps & { + borderWidth?: number; + borderRadius?: number; +}; diff --git a/src/incubator/gradient/useAngleTransform.ts b/src/incubator/gradient/useAngleTransform.ts new file mode 100644 index 0000000000..a3d3ab9bfd --- /dev/null +++ b/src/incubator/gradient/useAngleTransform.ts @@ -0,0 +1,68 @@ +import {useMemo} from 'react'; + +const EPSILON = 1e-12; + +function getStartEndFromAngle(angle = 0) { + // Normalize angle to [0, 360) + let a = angle % 360; + if (a < 0) { + a += 360; + } + + const rad = (a * Math.PI) / 180; + + // Direction vector where 0deg points up, 90deg right, etc. + const vx = Math.sin(rad); + const vy = -Math.cos(rad); + + // Distance from center (0.5,0.5) to box edge along v + const denomX = Math.abs(vx) > EPSILON ? 0.5 / Math.abs(vx) : Number.POSITIVE_INFINITY; + const denomY = Math.abs(vy) > EPSILON ? 0.5 / Math.abs(vy) : Number.POSITIVE_INFINITY; + const t = Math.min(denomX, denomY); + + const cx = 0.5; + const cy = 0.5; + + const end = {x: cx + vx * t, y: cy + vy * t}; + const start = {x: cx - vx * t, y: cy - vy * t}; + + // Quantize to avoid tiny floating errors for canonical angles (0, 45, 90, ...). + const quantize = (v: number) => { + if (Math.abs(v - 0) < EPSILON) { + return 0; + } + if (Math.abs(v - 0.5) < EPSILON) { + return 0.5; + } + if (Math.abs(v - 1) < EPSILON) { + return 1; + } + // Clamp just in case of tiny over/underflows + if (v < 0) { + return 0; + } + if (v > 1) { + return 1; + } + return v; + }; + + return { + start: {x: quantize(start.x), y: quantize(start.y)}, + end: {x: quantize(end.x), y: quantize(end.y)} + }; +} + +export const _forTesting = {getStartEndFromAngle}; // exporting private functions for testing only + +export type AngleTransformProps = { + angle?: number; +}; + +const useAngleTransform = (props: AngleTransformProps) => { + const {angle} = props; + const startEnd = useMemo(() => getStartEndFromAngle(angle), [angle]); + return startEnd; +}; + +export default useAngleTransform; diff --git a/src/incubator/index.ts b/src/incubator/index.ts index b230c0c8be..82f65ba95d 100644 --- a/src/incubator/index.ts +++ b/src/incubator/index.ts @@ -18,3 +18,4 @@ export {default as Dialog, DialogProps, DialogHeaderProps, DialogStatics, Dialog // TODO: delete exports after fully removing from private export {default as ChipsInput, ChipsInputProps, ChipsInputChangeReason, ChipsInputChipProps} from '../components/chipsInput'; export {default as WheelPicker, WheelPickerProps, WheelPickerItemProps, WheelPickerAlign, WheelPickerItemValue} from '../components/WheelPicker'; +export {default as Gradient, GradientProps} from './gradient'; diff --git a/src/optionalDependencies/LinearGradientPackage.ts b/src/optionalDependencies/LinearGradientPackage.ts index c25c919859..27527b0677 100644 --- a/src/optionalDependencies/LinearGradientPackage.ts +++ b/src/optionalDependencies/LinearGradientPackage.ts @@ -1,6 +1,8 @@ -let LinearGradientPackage: any; +let LinearGradientPackage: typeof import('react-native-linear-gradient') | {default: () => null}; try { LinearGradientPackage = require('react-native-linear-gradient'); -} catch (error) {} +} catch (error) { + LinearGradientPackage = {default: () => null}; +} export default LinearGradientPackage; From 55c819f831f26148472280c857ffc1287b4d9a2e Mon Sep 17 00:00:00 2001 From: M-i-k-e-l Date: Thu, 21 Aug 2025 13:11:33 +0300 Subject: [PATCH 2/4] Oops --- src/incubator/gradient/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/incubator/gradient/index.tsx b/src/incubator/gradient/index.tsx index 7f2106f5e3..44364bca42 100644 --- a/src/incubator/gradient/index.tsx +++ b/src/incubator/gradient/index.tsx @@ -14,7 +14,7 @@ const Gradient = (props: GradientProps) => { useEffect(() => { if (LinearGradient === undefined) { - LogService.error(`RNUILib SkeletonView's requires installing "react-native-linear-gradient" dependency`); + LogService.error(`RNUILib Gradient requires installing "react-native-linear-gradient" dependency`); } }, []); From 3d2419842e10732e836754bd5c975e69ed88e3e9 Mon Sep 17 00:00:00 2001 From: M-i-k-e-l Date: Sun, 24 Aug 2025 15:05:42 +0300 Subject: [PATCH 3/4] Revert LinearGradientPackage change --- src/incubator/gradient/BorderGradient.tsx | 4 ++++ src/incubator/gradient/CircleGradient.tsx | 4 ++++ src/incubator/gradient/RectangleGradient.tsx | 4 ++++ src/incubator/gradient/index.tsx | 23 ++++++++----------- .../LinearGradientPackage.ts | 6 ++--- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/incubator/gradient/BorderGradient.tsx b/src/incubator/gradient/BorderGradient.tsx index 3e9e290ed9..df1dd8f3b2 100644 --- a/src/incubator/gradient/BorderGradient.tsx +++ b/src/incubator/gradient/BorderGradient.tsx @@ -14,6 +14,10 @@ const BorderGradient = (props: BorderGradientProps) => { const innerHeight = height ? height - borderWidth * 2 : undefined; const {start, end} = useAngleTransform({angle}); + if (!LinearGradient) { + return null; + } + return ( diff --git a/src/incubator/gradient/CircleGradient.tsx b/src/incubator/gradient/CircleGradient.tsx index 6221edc84d..aff89273eb 100644 --- a/src/incubator/gradient/CircleGradient.tsx +++ b/src/incubator/gradient/CircleGradient.tsx @@ -11,6 +11,10 @@ const CircleGradient = (props: CircleGradientProps) => { const internalDiameter = radius ? radius * 2 : undefined; const {start, end} = useAngleTransform({angle}); + if (!LinearGradient) { + return null; + } + return ( diff --git a/src/incubator/gradient/RectangleGradient.tsx b/src/incubator/gradient/RectangleGradient.tsx index 304a72e2e0..1ec48a81ab 100644 --- a/src/incubator/gradient/RectangleGradient.tsx +++ b/src/incubator/gradient/RectangleGradient.tsx @@ -10,6 +10,10 @@ const RectangleGradient = (props: RectangleGradientProps) => { const {start, end} = useAngleTransform({angle}); + if (!LinearGradient) { + return null; + } + return ( diff --git a/src/incubator/gradient/index.tsx b/src/incubator/gradient/index.tsx index 44364bca42..651b0c4b2a 100644 --- a/src/incubator/gradient/index.tsx +++ b/src/incubator/gradient/index.tsx @@ -18,20 +18,15 @@ const Gradient = (props: GradientProps) => { } }, []); - if (!LinearGradient) { - return null; - } - - if (type === 'rectangle') { - return ; - } - - if (type === 'circle') { - return ; - } - - if (type === 'border') { - return ; + switch (type) { + case 'rectangle': + return ; + case 'circle': + return ; + case 'border': + return ; + default: + return null; } }; diff --git a/src/optionalDependencies/LinearGradientPackage.ts b/src/optionalDependencies/LinearGradientPackage.ts index 27527b0677..2da1707770 100644 --- a/src/optionalDependencies/LinearGradientPackage.ts +++ b/src/optionalDependencies/LinearGradientPackage.ts @@ -1,8 +1,6 @@ -let LinearGradientPackage: typeof import('react-native-linear-gradient') | {default: () => null}; +let LinearGradientPackage: typeof import('react-native-linear-gradient') | undefined; try { LinearGradientPackage = require('react-native-linear-gradient'); -} catch (error) { - LinearGradientPackage = {default: () => null}; -} +} catch (error) {} export default LinearGradientPackage; From 1431d2f01b82f46eb30bdb05bd878f1fcbff5ba9 Mon Sep 17 00:00:00 2001 From: M-i-k-e-l Date: Tue, 2 Sep 2025 12:00:58 +0300 Subject: [PATCH 4/4] Improve screen --- demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx b/demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx index 8dbcc17798..673d56bfaa 100644 --- a/demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx +++ b/demo/src/screens/incubatorScreens/IncubatorGradientScreen.tsx @@ -68,7 +68,7 @@ const GradientScreen = () => { }, [children, size, type]); return ( - + {renderRadioGroup('Select type', 'type', {Rectangle: 'rectangle', Circle: 'circle', Border: 'border'}, @@ -77,7 +77,7 @@ const GradientScreen = () => { 'children', {No: 'none', 'Short text': 'shortText', Text: 'text', Icon: 'icon'}, {isRow: true, state: children, setState: setChildren})} - {renderRadioGroup('Select alignment', + {renderRadioGroup('Select children`s alignment', 'alignment', {None: 'none', Center: 'center', CenterH: 'centerH', CenterV: 'centerV'}, {isRow: true, state: alignment, setState: setAlignment})}