u
This commit is contained in:
1 parent
c0ff3ce883
commit
69d03709ba
41 files changed
+5352
-67
No files matched your search
+1
-1
@@ -10,7 +10,7 @@ interface EmptyStateProps {
|
||||
onButtonPress: () => void
|
||||
}
|
||||
|
||||
export function EmptyState({
|
||||
export default function EmptyState({
|
||||
image,
|
||||
title,
|
||||
description,
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Ionicons } from '@expo/vector-icons'
|
||||
import { usePathname, useRouter } from 'expo-router'
|
||||
import { Image, Pressable, ScrollView, Text, View } from 'react-native'
|
||||
import Animated, { SlideInRight } from 'react-native-reanimated'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
|
||||
export interface StepLayoutItem {
|
||||
path: string // 步骤路径
|
||||
title?: string // 步骤标题
|
||||
onBack?: () => void // 可选的返回按钮回调
|
||||
showHearder?: boolean // 是否显示 header,单独放在每个步骤中
|
||||
headerRight?: React.ReactNode
|
||||
}
|
||||
|
||||
interface StepLayoutProps {
|
||||
steps: StepLayoutItem[] // 步骤列表
|
||||
children: React.ReactNode // 子内容
|
||||
showHearder?: boolean // 控制整体 header 是否显示
|
||||
}
|
||||
|
||||
export default function StepLayout({ steps, children, showHearder = true }: StepLayoutProps) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
// 是否能返回
|
||||
const canGoBack = router.canGoBack()
|
||||
|
||||
// 当前步骤
|
||||
const currentStep = steps.find(step => step.path === pathname) ?? steps[0]
|
||||
|
||||
// 是否显示header
|
||||
const headerVisible = showHearder !== false && currentStep.showHearder !== false
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<Animated.View
|
||||
key={pathname}
|
||||
entering={SlideInRight.duration(300)}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
{headerVisible && (
|
||||
<View className="flex-row items-center h-12 px-2 ">
|
||||
<Pressable
|
||||
onPress={canGoBack ? (currentStep.onBack ?? (() => router.back())) : undefined}
|
||||
style={{ opacity: canGoBack ? 1 : 0 }}
|
||||
>
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={22}
|
||||
/>
|
||||
</Pressable>
|
||||
|
||||
<Text className="flex-1 text-center text-lg font-semibold">
|
||||
{currentStep.title}
|
||||
</Text>
|
||||
|
||||
<View className="h-full flex items-center justify-center">
|
||||
{currentStep.headerRight ?? (
|
||||
<Image
|
||||
source={require('@/static/images/logo.png')}
|
||||
style={{ width: 22, height: 22 }}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<ScrollView
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<View style={{ flex: 1 }}>{children}</View>
|
||||
</ScrollView>
|
||||
|
||||
</Animated.View>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { useRouter } from 'expo-router'
|
||||
// components/Layout.tsx
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
|
||||
interface LayoutProps {
|
||||
title: string
|
||||
children: React.ReactNode
|
||||
showBack?: boolean
|
||||
right?: React.ReactNode
|
||||
}
|
||||
|
||||
export default function CustomLayout({ title, children, showBack = true, right }: LayoutProps) {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<View className="flex-1">
|
||||
{children}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { icons } from 'lucide-react-native'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler'
|
||||
import Animated, { SlideInLeft, SlideInRight, SlideOutLeft, SlideOutRight, useAnimatedStyle, useSharedValue, withSequence, withTiming } from 'react-native-reanimated'
|
||||
import Animated, { SlideInLeft, SlideInRight, useAnimatedStyle, useSharedValue, withSequence, withTiming } from 'react-native-reanimated'
|
||||
import { scheduleOnRN } from 'react-native-worklets'
|
||||
|
||||
interface AppTabbarProps {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
import React from 'react';
|
||||
import type { FunctionComponent } from 'react';
|
||||
import type { ViewProps } from 'react-native';
|
||||
import { Svg, GProps, Path } from 'react-native-svg';
|
||||
import { getIconColor } from './helper';
|
||||
|
||||
// ✅ 关键改动:排除 GProps.color
|
||||
interface Props extends Omit<GProps, 'color'>, ViewProps {
|
||||
size?: number;
|
||||
color?: string | string[];
|
||||
}
|
||||
|
||||
const IconQq: FunctionComponent<Props> = ({
|
||||
size = 18,
|
||||
color,
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
|
||||
<Svg viewBox="0 0 1024 1024" width={size} height={size} {...rest}>
|
||||
<Path
|
||||
d="M511.09761 957.257c-80.159 0-153.737-25.019-201.11-62.386-24.057 6.702-54.831 17.489-74.252 30.864-16.617 11.439-14.546 23.106-11.55 27.816 13.15 20.689 225.583 13.211 286.912 6.767v-3.061z"
|
||||
fill={getIconColor(color, 0, '#FAAD08')}
|
||||
/>
|
||||
<Path
|
||||
d="M496.65061 957.257c80.157 0 153.737-25.019 201.11-62.386 24.057 6.702 54.83 17.489 74.253 30.864 16.616 11.439 14.543 23.106 11.55 27.816-13.15 20.689-225.584 13.211-286.914 6.767v-3.061z"
|
||||
fill={getIconColor(color, 1, '#FAAD08')}
|
||||
/>
|
||||
<Path
|
||||
d="M497.12861 474.524c131.934-0.876 237.669-25.783 273.497-35.34 8.541-2.28 13.11-6.364 13.11-6.364 0.03-1.172 0.542-20.952 0.542-31.155C784.27761 229.833 701.12561 57.173 496.64061 57.162 292.15661 57.173 209.00061 229.832 209.00061 401.665c0 10.203 0.516 29.983 0.547 31.155 0 0 3.717 3.821 10.529 5.67 33.078 8.98 140.803 35.139 276.08 36.034h0.972z"
|
||||
fill={getIconColor(color, 2, '#000000')}
|
||||
/>
|
||||
<Path
|
||||
d="M860.28261 619.782c-8.12-26.086-19.204-56.506-30.427-85.72 0 0-6.456-0.795-9.718 0.148-100.71 29.205-222.773 47.818-315.792 46.695h-0.962C410.88561 582.017 289.65061 563.617 189.27961 534.698 185.44461 533.595 177.87261 534.063 177.87261 534.063 166.64961 563.276 155.56661 593.696 147.44761 619.782 108.72961 744.168 121.27261 795.644 130.82461 796.798c20.496 2.474 79.78-93.637 79.78-93.637 0 97.66 88.324 247.617 290.576 248.996a718.01 718.01 0 0 1 5.367 0C708.80161 950.778 797.12261 800.822 797.12261 703.162c0 0 59.284 96.111 79.783 93.637 9.55-1.154 22.093-52.63-16.623-177.017"
|
||||
fill={getIconColor(color, 3, '#000000')}
|
||||
/>
|
||||
<Path
|
||||
d="M434.38261 316.917c-27.9 1.24-51.745-30.106-53.24-69.956-1.518-39.877 19.858-73.207 47.764-74.454 27.875-1.224 51.703 30.109 53.218 69.974 1.527 39.877-19.853 73.2-47.742 74.436m206.67-69.956c-1.494 39.85-25.34 71.194-53.24 69.956-27.888-1.238-49.269-34.559-47.742-74.435 1.513-39.868 25.341-71.201 53.216-69.974 27.909 1.247 49.285 34.576 47.767 74.453"
|
||||
fill={getIconColor(color, 4, '#FFFFFF')}
|
||||
/>
|
||||
<Path
|
||||
d="M683.94261 368.627c-7.323-17.609-81.062-37.227-172.353-37.227h-0.98c-91.29 0-165.031 19.618-172.352 37.227a6.244 6.244 0 0 0-0.535 2.505c0 1.269 0.393 2.414 1.006 3.386 6.168 9.765 88.054 58.018 171.882 58.018h0.98c83.827 0 165.71-48.25 171.881-58.016a6.352 6.352 0 0 0 1.002-3.395c0-0.897-0.2-1.736-0.531-2.498"
|
||||
fill={getIconColor(color, 5, '#FAAD08')}
|
||||
/>
|
||||
<Path
|
||||
d="M467.63161 256.377c1.26 15.886-7.377 30-19.266 31.542-11.907 1.544-22.569-10.083-23.836-25.978-1.243-15.895 7.381-30.008 19.25-31.538 11.927-1.549 22.607 10.088 23.852 25.974m73.097 7.935c2.533-4.118 19.827-25.77 55.62-17.886 9.401 2.07 13.75 5.116 14.668 6.316 1.355 1.77 1.726 4.29 0.352 7.684-2.722 6.725-8.338 6.542-11.454 5.226-2.01-0.85-26.94-15.889-49.905 6.553-1.579 1.545-4.405 2.074-7.085 0.242-2.678-1.834-3.786-5.553-2.196-8.135"
|
||||
fill={getIconColor(color, 6, '#000000')}
|
||||
/>
|
||||
<Path
|
||||
d="M504.33261 584.495h-0.967c-63.568 0.752-140.646-7.504-215.286-21.92-6.391 36.262-10.25 81.838-6.936 136.196 8.37 137.384 91.62 223.736 220.118 224.996H506.48461c128.498-1.26 211.748-87.612 220.12-224.996 3.314-54.362-0.547-99.938-6.94-136.203-74.654 14.423-151.745 22.684-215.332 21.927"
|
||||
fill={getIconColor(color, 7, '#FFFFFF')}
|
||||
/>
|
||||
<Path
|
||||
d="M323.27461 577.016v137.468s64.957 12.705 130.031 3.91V591.59c-41.225-2.262-85.688-7.304-130.031-14.574"
|
||||
fill={getIconColor(color, 8, '#EB1C26')}
|
||||
/>
|
||||
<Path
|
||||
d="M788.09761 432.536s-121.98 40.387-283.743 41.539h-0.962c-161.497-1.147-283.328-41.401-283.744-41.539l-40.854 106.952c102.186 32.31 228.837 53.135 324.598 51.926l0.96-0.002c95.768 1.216 222.4-19.61 324.6-51.924l-40.855-106.952z"
|
||||
fill={getIconColor(color, 9, '#EB1C26')}
|
||||
/>
|
||||
</Svg>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(IconQq);
|
||||
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
import React from 'react';
|
||||
import type { FunctionComponent } from 'react';
|
||||
import type { ViewProps } from 'react-native';
|
||||
import { Svg, GProps, Path } from 'react-native-svg';
|
||||
import { getIconColor } from './helper';
|
||||
|
||||
// ✅ 关键改动:排除 GProps.color
|
||||
interface Props extends Omit<GProps, 'color'>, ViewProps {
|
||||
size?: number;
|
||||
color?: string | string[];
|
||||
}
|
||||
|
||||
const IconWeixin: FunctionComponent<Props> = ({
|
||||
size = 18,
|
||||
color,
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
|
||||
<Svg viewBox="0 0 1024 1024" width={size} height={size} {...rest}>
|
||||
<Path
|
||||
d="M1024 619.52c0-143.36-138.24-256-307.2-256s-307.2 112.64-307.2 256 138.24 256 307.2 256c30.72 0 61.44-5.12 92.16-10.24l97.28 51.2-25.6-76.8c87.04-51.2 143.36-128 143.36-220.16z m-414.72-40.96c-30.72 0-51.2-20.48-51.2-51.2s20.48-51.2 51.2-51.2 51.2 20.48 51.2 51.2c0 25.6-25.6 51.2-51.2 51.2z m209.92 0c-30.72 0-51.2-20.48-51.2-51.2s20.48-51.2 51.2-51.2 51.2 20.48 51.2 51.2c0 25.6-25.6 51.2-51.2 51.2z"
|
||||
fill={getIconColor(color, 0, '#4CBF00')}
|
||||
/>
|
||||
<Path
|
||||
d="M358.4 609.28c0-158.72 153.6-286.72 348.16-286.72h15.36c-40.96-133.12-179.2-235.52-353.28-235.52-204.8 0-368.64 138.24-368.64 307.2 0 107.52 66.56 204.8 168.96 256l-30.72 92.16L256 686.08c35.84 10.24 71.68 15.36 112.64 15.36h10.24c-15.36-30.72-20.48-61.44-20.48-92.16z m138.24-414.72c35.84 0 66.56 30.72 66.56 66.56s-30.72 66.56-66.56 66.56C460.8 322.56 430.08 291.84 430.08 256S460.8 194.56 496.64 194.56zM245.76 322.56c-35.84 0-61.44-30.72-61.44-66.56s30.72-66.56 66.56-66.56 61.44 30.72 61.44 66.56-30.72 66.56-66.56 66.56z"
|
||||
fill={getIconColor(color, 1, '#4CBF00')}
|
||||
/>
|
||||
</Svg>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(IconWeixin);
|
||||
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
import React from 'react';
|
||||
import type { FunctionComponent } from 'react';
|
||||
import type { ViewProps } from 'react-native';
|
||||
import { Svg, GProps, Path } from 'react-native-svg';
|
||||
import { getIconColor } from './helper';
|
||||
|
||||
// ✅ 关键改动:排除 GProps.color
|
||||
interface Props extends Omit<GProps, 'color'>, ViewProps {
|
||||
size?: number;
|
||||
color?: string | string[];
|
||||
}
|
||||
|
||||
const IconZhiwen: FunctionComponent<Props> = ({
|
||||
size = 18,
|
||||
color,
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
|
||||
<Svg viewBox="0 0 1024 1024" width={size} height={size} {...rest}>
|
||||
<Path
|
||||
d="M671.232 309.248c-3.584-1.536-7.168-2.048-10.752-2.048-11.776 0-22.528 7.168-27.136 18.432-5.632 14.848 1.536 31.744 16.384 37.888 68.096 26.624 126.464 72.704 167.424 133.632 41.472 60.416 62.976 131.584 62.976 204.8 0 10.24 5.632 19.968 14.336 25.088 9.216 5.12 19.968 5.12 29.184 0 9.216-5.12 14.336-14.848 14.336-25.088 1.024-175.104-103.936-329.216-266.752-392.704zM523.776 309.76c0-15.872-13.312-29.184-29.184-29.184-232.448 0-421.376 188.928-421.376 421.376 0 10.752 5.12 20.992 14.336 26.112 9.216 5.12 20.48 5.12 29.696 0 9.216-5.12 14.848-15.36 14.336-25.6 0-200.704 162.816-363.52 363.008-363.52 15.872-0.512 29.184-13.312 29.184-29.184z"
|
||||
fill={getIconColor(color, 0, '#2E77ED')}
|
||||
/>
|
||||
<Path
|
||||
d="M506.368 438.272c-148.48 0-268.8 120.832-268.8 268.8v130.56c0 10.24 5.632 19.968 14.336 25.088 9.216 5.12 19.968 5.12 29.184 0 9.216-5.12 14.336-14.848 14.336-25.088v-130.56c-1.024-75.776 38.912-146.432 104.448-184.832 32.768-18.944 69.632-28.672 105.984-28.672 36.864 0 73.216 9.728 105.984 28.672 65.536 38.4 105.472 109.056 104.448 184.832v152.576c0 15.872 13.312 29.184 29.184 29.184s29.184-13.312 29.184-29.184v-152.576c0.512-147.968-120.32-268.8-268.288-268.8z"
|
||||
fill={getIconColor(color, 1, '#FFBB12')}
|
||||
/>
|
||||
<Path
|
||||
d="M945.664 313.856c-109.056-130.048-268.8-204.8-438.784-204.8h-1.024c-162.304 0-317.44 69.12-425.984 189.44-7.168 7.68-9.216 18.432-6.144 28.672 3.072 9.728 11.264 17.408 21.504 19.456 10.24 2.048 20.992-1.024 27.648-9.216 97.28-108.544 236.544-170.496 382.464-170.496h1.024c153.088 0 297.472 67.584 395.264 185.344 10.24 12.288 28.672 13.824 40.96 3.584 12.288-10.24 13.824-28.672 3.584-40.96l-0.512-1.024z"
|
||||
fill={getIconColor(color, 2, '#2E77ED')}
|
||||
/>
|
||||
<Path
|
||||
d="M553.472 623.616c-29.184-16.896-65.536-16.896-94.72 0-29.184 16.896-47.104 48.128-47.104 81.408l0.512 7.68c0.512 16.896 1.024 124.416 1.024 186.88 0 15.872 13.312 29.184 29.184 29.184 7.68 0 15.36-3.072 20.48-8.704 5.632-5.632 8.704-12.8 8.704-20.48-0.512-112.64-1.024-176.128-1.024-188.416l-0.512-7.168v-0.512c0-18.432 16.896-34.304 36.352-34.304 9.728 0 18.944 4.096 25.6 10.752 6.656 6.656 10.752 15.872 10.752 25.6v194.048c0 10.24 5.632 19.968 14.336 25.088 11.264 6.656 25.088 4.608 34.816-4.096 5.632-5.632 9.216-13.824 9.216-22.528v-193.024c-0.512-32.768-18.432-64.512-47.616-81.408z"
|
||||
fill={getIconColor(color, 3, '#FFBB12')}
|
||||
/>
|
||||
</Svg>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(IconZhiwen);
|
||||
@@ -0,0 +1,12 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export const getIconColor = (color: string | string[] | undefined, index: number, defaultColor: string) => {
|
||||
return color
|
||||
? (
|
||||
typeof color === 'string'
|
||||
? color
|
||||
: color[index] || defaultColor
|
||||
)
|
||||
: defaultColor;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { ViewProps } from 'react-native';
|
||||
import { GProps } from 'react-native-svg';
|
||||
import IconWeixin from './IconWeixin';
|
||||
import IconZhiwen from './IconZhiwen';
|
||||
import IconQq from './IconQq';
|
||||
export { default as IconWeixin } from './IconWeixin';
|
||||
export { default as IconZhiwen } from './IconZhiwen';
|
||||
export { default as IconQq } from './IconQq';
|
||||
|
||||
export type IconNames = 'weixin' | 'zhiwen' | 'QQ';
|
||||
|
||||
interface Props extends GProps, ViewProps {
|
||||
name: IconNames;
|
||||
size?: number;
|
||||
color?: string | string[];
|
||||
}
|
||||
|
||||
let IconFont: FunctionComponent<Props> = ({ name, ...rest }) => {
|
||||
switch (name) {
|
||||
case 'weixin':
|
||||
return <IconWeixin key="1" {...rest} />;
|
||||
case 'zhiwen':
|
||||
return <IconZhiwen key="2" {...rest} />;
|
||||
case 'QQ':
|
||||
return <IconQq key="3" {...rest} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
IconFont = React.memo ? React.memo(IconFont) : IconFont;
|
||||
|
||||
export default IconFont;
|
||||
@@ -0,0 +1,242 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { createCheckbox } from '@gluestack-ui/core/checkbox/creator';
|
||||
import { View, Pressable, Text, Platform } from 'react-native';
|
||||
import type { TextProps, ViewProps } from 'react-native';
|
||||
import { tva } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import {
|
||||
PrimitiveIcon,
|
||||
IPrimitiveIcon,
|
||||
UIIcon,
|
||||
} from '@gluestack-ui/core/icon/creator';
|
||||
import {
|
||||
withStyleContext,
|
||||
useStyleContext,
|
||||
} from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { cssInterop } from 'nativewind';
|
||||
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils';
|
||||
|
||||
const IndicatorWrapper = React.forwardRef<
|
||||
React.ComponentRef<typeof View>,
|
||||
ViewProps
|
||||
>(function IndicatorWrapper({ ...props }, ref) {
|
||||
return <View {...props} ref={ref} />;
|
||||
});
|
||||
|
||||
const LabelWrapper = React.forwardRef<
|
||||
React.ComponentRef<typeof Text>,
|
||||
TextProps
|
||||
>(function LabelWrapper({ ...props }, ref) {
|
||||
return <Text {...props} ref={ref} />;
|
||||
});
|
||||
|
||||
const IconWrapper = React.forwardRef<
|
||||
React.ComponentRef<typeof PrimitiveIcon>,
|
||||
IPrimitiveIcon
|
||||
>(function IconWrapper({ ...props }, ref) {
|
||||
return <UIIcon {...props} ref={ref} />;
|
||||
});
|
||||
|
||||
const SCOPE = 'CHECKBOX';
|
||||
const UICheckbox = createCheckbox({
|
||||
// @ts-expect-error : internal implementation for r-19/react-native-web
|
||||
Root:
|
||||
Platform.OS === 'web'
|
||||
? withStyleContext(View, SCOPE)
|
||||
: withStyleContext(Pressable, SCOPE),
|
||||
Group: View,
|
||||
Icon: IconWrapper,
|
||||
Label: LabelWrapper,
|
||||
Indicator: IndicatorWrapper,
|
||||
});
|
||||
|
||||
cssInterop(PrimitiveIcon, {
|
||||
className: {
|
||||
target: 'style',
|
||||
nativeStyleToProp: {
|
||||
height: true,
|
||||
width: true,
|
||||
fill: true,
|
||||
color: 'classNameColor',
|
||||
stroke: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxStyle = tva({
|
||||
base: 'group/checkbox flex-row items-center justify-start web:cursor-pointer data-[disabled=true]:cursor-not-allowed',
|
||||
variants: {
|
||||
size: {
|
||||
lg: 'gap-2',
|
||||
md: 'gap-2',
|
||||
sm: 'gap-1.5',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxIndicatorStyle = tva({
|
||||
base: 'justify-center items-center border-outline-400 bg-transparent rounded web:data-[focus-visible=true]:outline-none web:data-[focus-visible=true]:ring-2 web:data-[focus-visible=true]:ring-indicator-primary data-[checked=true]:bg-primary-600 data-[checked=true]:border-primary-600 data-[hover=true]:data-[checked=false]:border-outline-500 data-[hover=true]:bg-transparent data-[hover=true]:data-[invalid=true]:border-error-700 data-[hover=true]:data-[checked=true]:bg-primary-700 data-[hover=true]:data-[checked=true]:border-primary-700 data-[hover=true]:data-[checked=true]:data-[disabled=true]:border-primary-600 data-[hover=true]:data-[checked=true]:data-[disabled=true]:bg-primary-600 data-[hover=true]:data-[checked=true]:data-[disabled=true]:opacity-40 data-[hover=true]:data-[checked=true]:data-[disabled=true]:data-[invalid=true]:border-error-700 data-[hover=true]:data-[disabled=true]:border-outline-400 data-[hover=true]:data-[disabled=true]:data-[invalid=true]:border-error-700 data-[active=true]:data-[checked=true]:bg-primary-800 data-[active=true]:data-[checked=true]:border-primary-800 data-[invalid=true]:border-error-700 data-[disabled=true]:opacity-40',
|
||||
parentVariants: {
|
||||
size: {
|
||||
lg: 'w-6 h-6 border-[3px]',
|
||||
md: 'w-5 h-5 border-2',
|
||||
sm: 'w-4 h-4 border-2',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxLabelStyle = tva({
|
||||
base: 'text-typography-600 data-[checked=true]:text-typography-900 data-[hover=true]:text-typography-900 data-[hover=true]:data-[checked=true]:text-typography-900 data-[hover=true]:data-[checked=true]:data-[disabled=true]:text-typography-900 data-[hover=true]:data-[disabled=true]:text-typography-400 data-[active=true]:text-typography-900 data-[active=true]:data-[checked=true]:text-typography-900 data-[disabled=true]:opacity-40 web:select-none',
|
||||
parentVariants: {
|
||||
size: {
|
||||
lg: 'text-lg',
|
||||
md: 'text-base',
|
||||
sm: 'text-sm',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxIconStyle = tva({
|
||||
base: 'text-typography-50 fill-none',
|
||||
|
||||
parentVariants: {
|
||||
size: {
|
||||
sm: 'h-3 w-3',
|
||||
md: 'h-4 w-4',
|
||||
lg: 'h-5 w-5',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const CheckboxGroup = UICheckbox.Group;
|
||||
|
||||
type ICheckboxProps = React.ComponentPropsWithoutRef<typeof UICheckbox> &
|
||||
VariantProps<typeof checkboxStyle>;
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
React.ComponentRef<typeof UICheckbox>,
|
||||
ICheckboxProps
|
||||
>(function Checkbox({ className, size = 'md', ...props }, ref) {
|
||||
return (
|
||||
<UICheckbox
|
||||
className={checkboxStyle({
|
||||
class: className,
|
||||
size,
|
||||
})}
|
||||
{...props}
|
||||
context={{
|
||||
size,
|
||||
}}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type ICheckboxIndicatorProps = React.ComponentPropsWithoutRef<
|
||||
typeof UICheckbox.Indicator
|
||||
> &
|
||||
VariantProps<typeof checkboxIndicatorStyle>;
|
||||
|
||||
const CheckboxIndicator = React.forwardRef<
|
||||
React.ComponentRef<typeof UICheckbox.Indicator>,
|
||||
ICheckboxIndicatorProps
|
||||
>(function CheckboxIndicator({ className, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UICheckbox.Indicator
|
||||
className={checkboxIndicatorStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type ICheckboxLabelProps = React.ComponentPropsWithoutRef<
|
||||
typeof UICheckbox.Label
|
||||
> &
|
||||
VariantProps<typeof checkboxLabelStyle>;
|
||||
const CheckboxLabel = React.forwardRef<
|
||||
React.ComponentRef<typeof UICheckbox.Label>,
|
||||
ICheckboxLabelProps
|
||||
>(function CheckboxLabel({ className, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
return (
|
||||
<UICheckbox.Label
|
||||
className={checkboxLabelStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type ICheckboxIconProps = React.ComponentPropsWithoutRef<
|
||||
typeof UICheckbox.Icon
|
||||
> &
|
||||
VariantProps<typeof checkboxIconStyle>;
|
||||
|
||||
const CheckboxIcon = React.forwardRef<
|
||||
React.ComponentRef<typeof UICheckbox.Icon>,
|
||||
ICheckboxIconProps
|
||||
>(function CheckboxIcon({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
if (typeof size === 'number') {
|
||||
return (
|
||||
<UICheckbox.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={checkboxIconStyle({ class: className })}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
(props.height !== undefined || props.width !== undefined) &&
|
||||
size === undefined
|
||||
) {
|
||||
return (
|
||||
<UICheckbox.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={checkboxIconStyle({ class: className })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<UICheckbox.Icon
|
||||
className={checkboxIconStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Checkbox.displayName = 'Checkbox';
|
||||
CheckboxIndicator.displayName = 'CheckboxIndicator';
|
||||
CheckboxLabel.displayName = 'CheckboxLabel';
|
||||
CheckboxIcon.displayName = 'CheckboxIcon';
|
||||
|
||||
export {
|
||||
Checkbox,
|
||||
CheckboxIndicator,
|
||||
CheckboxLabel,
|
||||
CheckboxIcon,
|
||||
CheckboxGroup,
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { tva } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { Platform, View } from 'react-native';
|
||||
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils';
|
||||
|
||||
const dividerStyle = tva({
|
||||
base: 'bg-background-200',
|
||||
variants: {
|
||||
orientation: {
|
||||
vertical: 'w-px h-full',
|
||||
horizontal: 'h-px w-full',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type IUIDividerProps = React.ComponentPropsWithoutRef<typeof View> &
|
||||
VariantProps<typeof dividerStyle>;
|
||||
|
||||
const Divider = React.forwardRef<
|
||||
React.ComponentRef<typeof View>,
|
||||
IUIDividerProps
|
||||
>(function Divider({ className, orientation = 'horizontal', ...props }, ref) {
|
||||
return (
|
||||
<View
|
||||
ref={ref}
|
||||
{...props}
|
||||
aria-orientation={orientation}
|
||||
role={Platform.OS === 'web' ? 'separator' : undefined}
|
||||
className={dividerStyle({
|
||||
orientation,
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Divider.displayName = 'Divider';
|
||||
|
||||
export { Divider };
|
||||
@@ -0,0 +1,468 @@
|
||||
'use client';
|
||||
import { Text, View } from 'react-native';
|
||||
import React from 'react';
|
||||
import { createFormControl } from '@gluestack-ui/core/form-control/creator';
|
||||
import { tva } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import {
|
||||
withStyleContext,
|
||||
useStyleContext,
|
||||
} from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { cssInterop } from 'nativewind';
|
||||
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { PrimitiveIcon, UIIcon } from '@gluestack-ui/core/icon/creator';
|
||||
|
||||
const SCOPE = 'FORM_CONTROL';
|
||||
|
||||
const formControlStyle = tva({
|
||||
base: 'flex flex-col',
|
||||
variants: {
|
||||
size: {
|
||||
sm: '',
|
||||
md: '',
|
||||
lg: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formControlErrorIconStyle = tva({
|
||||
base: 'text-error-700 fill-none',
|
||||
variants: {
|
||||
size: {
|
||||
'2xs': 'h-3 w-3',
|
||||
'xs': 'h-3.5 w-3.5',
|
||||
'sm': 'h-4 w-4',
|
||||
'md': 'h-[18px] w-[18px]',
|
||||
'lg': 'h-5 w-5',
|
||||
'xl': 'h-6 w-6',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formControlErrorStyle = tva({
|
||||
base: 'flex flex-row justify-start items-center mt-1 gap-1',
|
||||
});
|
||||
|
||||
const formControlErrorTextStyle = tva({
|
||||
base: 'text-error-700',
|
||||
variants: {
|
||||
isTruncated: {
|
||||
true: 'web:truncate',
|
||||
},
|
||||
bold: {
|
||||
true: 'font-bold',
|
||||
},
|
||||
underline: {
|
||||
true: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
true: 'line-through',
|
||||
},
|
||||
size: {
|
||||
'2xs': 'text-2xs',
|
||||
'xs': 'text-xs',
|
||||
'sm': 'text-sm',
|
||||
'md': 'text-base',
|
||||
'lg': 'text-lg',
|
||||
'xl': 'text-xl',
|
||||
'2xl': 'text-2xl',
|
||||
'3xl': 'text-3xl',
|
||||
'4xl': 'text-4xl',
|
||||
'5xl': 'text-5xl',
|
||||
'6xl': 'text-6xl',
|
||||
},
|
||||
sub: {
|
||||
true: 'text-xs',
|
||||
},
|
||||
italic: {
|
||||
true: 'italic',
|
||||
},
|
||||
highlight: {
|
||||
true: 'bg-yellow-500',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formControlHelperStyle = tva({
|
||||
base: 'flex flex-row justify-start items-center mt-1',
|
||||
});
|
||||
|
||||
const formControlHelperTextStyle = tva({
|
||||
base: 'text-typography-500',
|
||||
variants: {
|
||||
isTruncated: {
|
||||
true: 'web:truncate',
|
||||
},
|
||||
bold: {
|
||||
true: 'font-bold',
|
||||
},
|
||||
underline: {
|
||||
true: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
true: 'line-through',
|
||||
},
|
||||
size: {
|
||||
'2xs': 'text-2xs',
|
||||
'xs': 'text-xs',
|
||||
'sm': 'text-xs',
|
||||
'md': 'text-sm',
|
||||
'lg': 'text-base',
|
||||
'xl': 'text-xl',
|
||||
'2xl': 'text-2xl',
|
||||
'3xl': 'text-3xl',
|
||||
'4xl': 'text-4xl',
|
||||
'5xl': 'text-5xl',
|
||||
'6xl': 'text-6xl',
|
||||
},
|
||||
sub: {
|
||||
true: 'text-xs',
|
||||
},
|
||||
italic: {
|
||||
true: 'italic',
|
||||
},
|
||||
highlight: {
|
||||
true: 'bg-yellow-500',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formControlLabelStyle = tva({
|
||||
base: 'flex flex-row justify-start items-center mb-1',
|
||||
});
|
||||
|
||||
const formControlLabelTextStyle = tva({
|
||||
base: 'font-medium text-typography-900',
|
||||
variants: {
|
||||
isTruncated: {
|
||||
true: 'web:truncate',
|
||||
},
|
||||
bold: {
|
||||
true: 'font-bold',
|
||||
},
|
||||
underline: {
|
||||
true: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
true: 'line-through',
|
||||
},
|
||||
size: {
|
||||
'2xs': 'text-2xs',
|
||||
'xs': 'text-xs',
|
||||
'sm': 'text-sm',
|
||||
'md': 'text-base',
|
||||
'lg': 'text-lg',
|
||||
'xl': 'text-xl',
|
||||
'2xl': 'text-2xl',
|
||||
'3xl': 'text-3xl',
|
||||
'4xl': 'text-4xl',
|
||||
'5xl': 'text-5xl',
|
||||
'6xl': 'text-6xl',
|
||||
},
|
||||
sub: {
|
||||
true: 'text-xs',
|
||||
},
|
||||
italic: {
|
||||
true: 'italic',
|
||||
},
|
||||
highlight: {
|
||||
true: 'bg-yellow-500',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formControlLabelAstrickStyle = tva({
|
||||
base: 'font-medium text-typography-900',
|
||||
variants: {
|
||||
isTruncated: {
|
||||
true: 'web:truncate',
|
||||
},
|
||||
bold: {
|
||||
true: 'font-bold',
|
||||
},
|
||||
underline: {
|
||||
true: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
true: 'line-through',
|
||||
},
|
||||
size: {
|
||||
'2xs': 'text-2xs',
|
||||
'xs': 'text-xs',
|
||||
'sm': 'text-sm',
|
||||
'md': 'text-base',
|
||||
'lg': 'text-lg',
|
||||
'xl': 'text-xl',
|
||||
'2xl': 'text-2xl',
|
||||
'3xl': 'text-3xl',
|
||||
'4xl': 'text-4xl',
|
||||
'5xl': 'text-5xl',
|
||||
'6xl': 'text-6xl',
|
||||
},
|
||||
sub: {
|
||||
true: 'text-xs',
|
||||
},
|
||||
italic: {
|
||||
true: 'italic',
|
||||
},
|
||||
highlight: {
|
||||
true: 'bg-yellow-500',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type IFormControlLabelAstrickProps = React.ComponentPropsWithoutRef<
|
||||
typeof Text
|
||||
> &
|
||||
VariantProps<typeof formControlLabelAstrickStyle>;
|
||||
|
||||
const FormControlLabelAstrick = React.forwardRef<
|
||||
React.ComponentRef<typeof Text>,
|
||||
IFormControlLabelAstrickProps
|
||||
>(function FormControlLabelAstrick({ className, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<Text
|
||||
ref={ref}
|
||||
className={formControlLabelAstrickStyle({
|
||||
parentVariants: { size: parentSize },
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const UIFormControl = createFormControl({
|
||||
Root: withStyleContext(View, SCOPE),
|
||||
Error: View,
|
||||
ErrorText: Text,
|
||||
ErrorIcon: UIIcon,
|
||||
Label: View,
|
||||
LabelText: Text,
|
||||
LabelAstrick: FormControlLabelAstrick,
|
||||
Helper: View,
|
||||
HelperText: Text,
|
||||
});
|
||||
|
||||
cssInterop(PrimitiveIcon, {
|
||||
className: {
|
||||
target: 'style',
|
||||
nativeStyleToProp: {
|
||||
height: true,
|
||||
width: true,
|
||||
fill: true,
|
||||
color: true,
|
||||
stroke: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type IFormControlProps = React.ComponentProps<typeof UIFormControl> &
|
||||
VariantProps<typeof formControlStyle>;
|
||||
|
||||
const FormControl = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl>,
|
||||
IFormControlProps
|
||||
>(function FormControl({ className, size = 'md', ...props }, ref) {
|
||||
return (
|
||||
<UIFormControl
|
||||
ref={ref}
|
||||
className={formControlStyle({ size, class: className })}
|
||||
{...props}
|
||||
context={{ size }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IFormControlErrorProps = React.ComponentProps<typeof UIFormControl.Error> &
|
||||
VariantProps<typeof formControlErrorStyle>;
|
||||
|
||||
const FormControlError = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl.Error>,
|
||||
IFormControlErrorProps
|
||||
>(function FormControlError({ className, ...props }, ref) {
|
||||
return (
|
||||
<UIFormControl.Error
|
||||
ref={ref}
|
||||
className={formControlErrorStyle({ class: className })}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IFormControlErrorTextProps = React.ComponentProps<
|
||||
typeof UIFormControl.Error.Text
|
||||
> &
|
||||
VariantProps<typeof formControlErrorTextStyle>;
|
||||
|
||||
const FormControlErrorText = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl.Error.Text>,
|
||||
IFormControlErrorTextProps
|
||||
>(function FormControlErrorText({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
return (
|
||||
<UIFormControl.Error.Text
|
||||
className={formControlErrorTextStyle({
|
||||
parentVariants: { size: parentSize },
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IFormControlErrorIconProps = React.ComponentProps<
|
||||
typeof UIFormControl.Error.Icon
|
||||
> &
|
||||
VariantProps<typeof formControlErrorIconStyle> & {
|
||||
height?: number;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
const FormControlErrorIcon = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl.Error.Icon>,
|
||||
IFormControlErrorIconProps
|
||||
>(function FormControlErrorIcon({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
if (typeof size === 'number') {
|
||||
return (
|
||||
<UIFormControl.Error.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={formControlErrorIconStyle({ class: className })}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
(props.height !== undefined || props.width !== undefined) &&
|
||||
size === undefined
|
||||
) {
|
||||
return (
|
||||
<UIFormControl.Error.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={formControlErrorIconStyle({ class: className })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<UIFormControl.Error.Icon
|
||||
className={formControlErrorIconStyle({
|
||||
parentVariants: { size: parentSize },
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IFormControlLabelProps = React.ComponentProps<typeof UIFormControl.Label> &
|
||||
VariantProps<typeof formControlLabelStyle>;
|
||||
|
||||
const FormControlLabel = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl.Label>,
|
||||
IFormControlLabelProps
|
||||
>(function FormControlLabel({ className, ...props }, ref) {
|
||||
return (
|
||||
<UIFormControl.Label
|
||||
ref={ref}
|
||||
className={formControlLabelStyle({ class: className })}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IFormControlLabelTextProps = React.ComponentProps<
|
||||
typeof UIFormControl.Label.Text
|
||||
> &
|
||||
VariantProps<typeof formControlLabelTextStyle>;
|
||||
|
||||
const FormControlLabelText = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl.Label.Text>,
|
||||
IFormControlLabelTextProps
|
||||
>(function FormControlLabelText({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIFormControl.Label.Text
|
||||
className={formControlLabelTextStyle({
|
||||
parentVariants: { size: parentSize },
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IFormControlHelperProps = React.ComponentProps<
|
||||
typeof UIFormControl.Helper
|
||||
> &
|
||||
VariantProps<typeof formControlHelperStyle>;
|
||||
|
||||
const FormControlHelper = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl.Helper>,
|
||||
IFormControlHelperProps
|
||||
>(function FormControlHelper({ className, ...props }, ref) {
|
||||
return (
|
||||
<UIFormControl.Helper
|
||||
ref={ref}
|
||||
className={formControlHelperStyle({
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IFormControlHelperTextProps = React.ComponentProps<
|
||||
typeof UIFormControl.Helper.Text
|
||||
> &
|
||||
VariantProps<typeof formControlHelperTextStyle>;
|
||||
|
||||
const FormControlHelperText = React.forwardRef<
|
||||
React.ComponentRef<typeof UIFormControl.Helper.Text>,
|
||||
IFormControlHelperTextProps
|
||||
>(function FormControlHelperText({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIFormControl.Helper.Text
|
||||
className={formControlHelperTextStyle({
|
||||
parentVariants: { size: parentSize },
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
FormControl.displayName = 'FormControl';
|
||||
FormControlError.displayName = 'FormControlError';
|
||||
FormControlErrorText.displayName = 'FormControlErrorText';
|
||||
FormControlErrorIcon.displayName = 'FormControlErrorIcon';
|
||||
FormControlLabel.displayName = 'FormControlLabel';
|
||||
FormControlLabelText.displayName = 'FormControlLabelText';
|
||||
FormControlLabelAstrick.displayName = 'FormControlLabelAstrick';
|
||||
FormControlHelper.displayName = 'FormControlHelper';
|
||||
FormControlHelperText.displayName = 'FormControlHelperText';
|
||||
|
||||
export {
|
||||
FormControl,
|
||||
FormControlError,
|
||||
FormControlErrorText,
|
||||
FormControlErrorIcon,
|
||||
FormControlLabel,
|
||||
FormControlLabelText,
|
||||
FormControlLabelAstrick,
|
||||
FormControlHelper,
|
||||
FormControlHelperText,
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,217 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { createInput } from '@gluestack-ui/core/input/creator';
|
||||
import { View, Pressable, TextInput } from 'react-native';
|
||||
import { tva } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import {
|
||||
withStyleContext,
|
||||
useStyleContext,
|
||||
} from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { cssInterop } from 'nativewind';
|
||||
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { PrimitiveIcon, UIIcon } from '@gluestack-ui/core/icon/creator';
|
||||
|
||||
const SCOPE = 'INPUT';
|
||||
|
||||
const UIInput = createInput({
|
||||
Root: withStyleContext(View, SCOPE),
|
||||
Icon: UIIcon,
|
||||
Slot: Pressable,
|
||||
Input: TextInput,
|
||||
});
|
||||
|
||||
cssInterop(PrimitiveIcon, {
|
||||
className: {
|
||||
target: 'style',
|
||||
nativeStyleToProp: {
|
||||
height: true,
|
||||
width: true,
|
||||
fill: true,
|
||||
color: 'classNameColor',
|
||||
stroke: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const inputStyle = tva({
|
||||
base: 'border-background-300 flex-row overflow-hidden content-center data-[hover=true]:border-outline-400 data-[focus=true]:border-primary-700 data-[focus=true]:hover:border-primary-700 data-[disabled=true]:opacity-40 data-[disabled=true]:hover:border-background-300 items-center',
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
xl: 'h-12',
|
||||
lg: 'h-11',
|
||||
md: 'h-10',
|
||||
sm: 'h-9',
|
||||
},
|
||||
|
||||
variant: {
|
||||
underlined:
|
||||
'rounded-none border-b data-[invalid=true]:border-b-2 data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700',
|
||||
|
||||
outline:
|
||||
'rounded border data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[focus=true]:hover:web:ring-1 data-[invalid=true]:data-[focus=true]:hover:web:ring-inset data-[invalid=true]:data-[focus=true]:hover:web:ring-indicator-error data-[invalid=true]:data-[disabled=true]:hover:web:ring-1 data-[invalid=true]:data-[disabled=true]:hover:web:ring-inset data-[invalid=true]:data-[disabled=true]:hover:web:ring-indicator-error',
|
||||
|
||||
rounded:
|
||||
'rounded-full border data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[focus=true]:hover:web:ring-1 data-[invalid=true]:data-[focus=true]:hover:web:ring-inset data-[invalid=true]:data-[focus=true]:hover:web:ring-indicator-error data-[invalid=true]:data-[disabled=true]:hover:web:ring-1 data-[invalid=true]:data-[disabled=true]:hover:web:ring-inset data-[invalid=true]:data-[disabled=true]:hover:web:ring-indicator-error',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const inputIconStyle = tva({
|
||||
base: 'justify-center items-center text-typography-400 fill-none',
|
||||
parentVariants: {
|
||||
size: {
|
||||
'2xs': 'h-3 w-3',
|
||||
'xs': 'h-3.5 w-3.5',
|
||||
'sm': 'h-4 w-4',
|
||||
'md': 'h-[18px] w-[18px]',
|
||||
'lg': 'h-5 w-5',
|
||||
'xl': 'h-6 w-6',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const inputSlotStyle = tva({
|
||||
base: 'justify-center items-center web:disabled:cursor-not-allowed',
|
||||
});
|
||||
|
||||
const inputFieldStyle = tva({
|
||||
base: 'flex-1 text-typography-900 py-0 px-3 placeholder:text-typography-500 h-full ios:leading-[0px] web:cursor-text web:data-[disabled=true]:cursor-not-allowed',
|
||||
|
||||
parentVariants: {
|
||||
variant: {
|
||||
underlined: 'web:outline-0 web:outline-none px-0',
|
||||
outline: 'web:outline-0 web:outline-none',
|
||||
rounded: 'web:outline-0 web:outline-none px-4',
|
||||
},
|
||||
|
||||
size: {
|
||||
'2xs': 'text-2xs',
|
||||
'xs': 'text-xs',
|
||||
'sm': 'text-sm',
|
||||
'md': 'text-base',
|
||||
'lg': 'text-lg',
|
||||
'xl': 'text-xl',
|
||||
'2xl': 'text-2xl',
|
||||
'3xl': 'text-3xl',
|
||||
'4xl': 'text-4xl',
|
||||
'5xl': 'text-5xl',
|
||||
'6xl': 'text-6xl',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type IInputProps = React.ComponentProps<typeof UIInput> &
|
||||
VariantProps<typeof inputStyle> & { className?: string };
|
||||
const Input = React.forwardRef<React.ComponentRef<typeof UIInput>, IInputProps>(
|
||||
function Input(
|
||||
{ className, variant = 'outline', size = 'md', ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<UIInput
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={inputStyle({ variant, size, class: className })}
|
||||
context={{ variant, size }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
type IInputIconProps = React.ComponentProps<typeof UIInput.Icon> &
|
||||
VariantProps<typeof inputIconStyle> & {
|
||||
className?: string;
|
||||
height?: number;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
const InputIcon = React.forwardRef<
|
||||
React.ComponentRef<typeof UIInput.Icon>,
|
||||
IInputIconProps
|
||||
>(function InputIcon({ className, size, ...props }, ref) {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
if (typeof size === 'number') {
|
||||
return (
|
||||
<UIInput.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={inputIconStyle({ class: className })}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
(props.height !== undefined || props.width !== undefined) &&
|
||||
size === undefined
|
||||
) {
|
||||
return (
|
||||
<UIInput.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={inputIconStyle({ class: className })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<UIInput.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={inputIconStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IInputSlotProps = React.ComponentProps<typeof UIInput.Slot> &
|
||||
VariantProps<typeof inputSlotStyle> & { className?: string };
|
||||
|
||||
const InputSlot = React.forwardRef<
|
||||
React.ComponentRef<typeof UIInput.Slot>,
|
||||
IInputSlotProps
|
||||
>(function InputSlot({ className, ...props }, ref) {
|
||||
return (
|
||||
<UIInput.Slot
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={inputSlotStyle({
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IInputFieldProps = React.ComponentProps<typeof UIInput.Input> &
|
||||
VariantProps<typeof inputFieldStyle> & { className?: string };
|
||||
|
||||
const InputField = React.forwardRef<
|
||||
React.ComponentRef<typeof UIInput.Input>,
|
||||
IInputFieldProps
|
||||
>(function InputField({ className, ...props }, ref) {
|
||||
const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIInput.Input
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={inputFieldStyle({
|
||||
parentVariants: {
|
||||
variant: parentVariant,
|
||||
size: parentSize,
|
||||
},
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Input.displayName = 'Input';
|
||||
InputIcon.displayName = 'InputIcon';
|
||||
InputSlot.displayName = 'InputSlot';
|
||||
InputField.displayName = 'InputField';
|
||||
|
||||
export { Input, InputField, InputIcon, InputSlot };
|
||||
@@ -0,0 +1,102 @@
|
||||
'use client';
|
||||
import { createLink } from '@gluestack-ui/core/link/creator';
|
||||
import { Pressable } from 'react-native';
|
||||
import { Text } from 'react-native';
|
||||
|
||||
import { tva } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { withStyleContext } from '@gluestack-ui/utils/nativewind-utils';
|
||||
import { cssInterop } from 'nativewind';
|
||||
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils';
|
||||
|
||||
import React from 'react';
|
||||
export const UILink = createLink({
|
||||
Root: withStyleContext(Pressable),
|
||||
Text: Text,
|
||||
});
|
||||
|
||||
cssInterop(UILink, { className: 'style' });
|
||||
cssInterop(UILink.Text, { className: 'style' });
|
||||
|
||||
const linkStyle = tva({
|
||||
base: 'group/link web:outline-0 data-[disabled=true]:web:cursor-not-allowed data-[focus-visible=true]:web:ring-2 data-[focus-visible=true]:web:ring-indicator-primary data-[focus-visible=true]:web:outline-0 data-[disabled=true]:opacity-4 ',
|
||||
});
|
||||
|
||||
const linkTextStyle = tva({
|
||||
base: 'underline text-info-700 data-[hover=true]:text-info-600 data-[hover=true]:no-underline data-[active=true]:text-info-700 font-normal font-body web:font-sans web:tracking-sm web:my-0 web:bg-transparent web:border-0 web:box-border web:display-inline web:list-none web:margin-0 web:padding-0 web:position-relative web:text-start web:whitespace-pre-wrap web:word-wrap-break-word',
|
||||
|
||||
variants: {
|
||||
isTruncated: {
|
||||
true: 'web:truncate',
|
||||
},
|
||||
bold: {
|
||||
true: 'font-bold',
|
||||
},
|
||||
underline: {
|
||||
true: 'underline',
|
||||
},
|
||||
strikeThrough: {
|
||||
true: 'line-through',
|
||||
},
|
||||
size: {
|
||||
'2xs': 'text-2xs',
|
||||
'xs': 'text-xs',
|
||||
'sm': 'text-sm',
|
||||
'md': 'text-base',
|
||||
'lg': 'text-lg',
|
||||
'xl': 'text-xl',
|
||||
'2xl': 'text-2xl',
|
||||
'3xl': 'text-3xl',
|
||||
'4xl': 'text-4xl',
|
||||
'5xl': 'text-5xl',
|
||||
'6xl': 'text-6xl',
|
||||
},
|
||||
sub: {
|
||||
true: 'text-xs',
|
||||
},
|
||||
italic: {
|
||||
true: 'italic',
|
||||
},
|
||||
highlight: {
|
||||
true: 'bg-yellow-500',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type ILinkProps = React.ComponentProps<typeof UILink> &
|
||||
VariantProps<typeof linkStyle> & { className?: string };
|
||||
|
||||
const Link = React.forwardRef<React.ComponentRef<typeof UILink>, ILinkProps>(
|
||||
function Link({ className, ...props }, ref) {
|
||||
return (
|
||||
<UILink
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={linkStyle({ class: className })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
type ILinkTextProps = React.ComponentProps<typeof UILink.Text> &
|
||||
VariantProps<typeof linkTextStyle> & { className?: string };
|
||||
|
||||
const LinkText = React.forwardRef<
|
||||
React.ComponentRef<typeof UILink.Text>,
|
||||
ILinkTextProps
|
||||
>(function LinkText({ className, size = 'md', ...props }, ref) {
|
||||
return (
|
||||
<UILink.Text
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={linkTextStyle({
|
||||
class: className,
|
||||
size,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Link.displayName = 'Link';
|
||||
LinkText.displayName = 'LinkText';
|
||||
|
||||
export { Link, LinkText };
|
||||
Reference in new issue
Block a user