@nativetail/ui 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativetail/ui",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {},
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@hookform/resolvers": "^3.6.0",
30
- "@nativetail/core": "^0.0.5",
30
+ "@nativetail/core": "^0.0.6",
31
31
  "@shopify/flash-list": "^1.7.0",
32
32
  "countries-list": "^3.1.0",
33
33
  "expo-blur": "^13.0.2",
@@ -38,6 +38,7 @@
38
38
  "react-hook-form": "^7.51.0",
39
39
  "react-native": "0.74.3",
40
40
  "react-native-actions-sheet": "^0.9.6",
41
+ "react-native-circular-progress-indicator": "^4.4.2",
41
42
  "react-native-gesture-handler": "^2.17.1",
42
43
  "react-native-mask-text": "^0.14.2",
43
44
  "react-native-reanimated": "~3.10.1",
@@ -37,6 +37,7 @@ export type FormBuilderProps<
37
37
  onError?: (values: Partial<Record<keyof T, any>>) => void;
38
38
  isSubmitting?: boolean;
39
39
  defaultValues?: DefaultValues<IValues>;
40
+ inputContainerClassname?: string;
40
41
  };
41
42
  export function FormBuilder<T extends z.ZodRawShape>({
42
43
  schema,
@@ -47,6 +48,7 @@ export function FormBuilder<T extends z.ZodRawShape>({
47
48
  onError,
48
49
  isSubmitting,
49
50
  defaultValues,
51
+ inputContainerClassname,
50
52
  }: FormBuilderProps<T>) {
51
53
  const shape = schema.shape;
52
54
  const keys = Object.keys(shape);
@@ -56,22 +58,31 @@ export function FormBuilder<T extends z.ZodRawShape>({
56
58
  defaultValues: defaultValues,
57
59
  });
58
60
  return (
59
- <View className={cn("gap-2", containerClassname)}>
60
- {keys.map((inputKey) => {
61
- const Input = inputs[inputKey];
62
- const Render = Input.render;
63
- if (!Render) {
61
+ <View className={cn("gap-4", containerClassname)}>
62
+ <View className={cn("flex-1 gap-2", inputContainerClassname)}>
63
+ {keys.map((inputKey) => {
64
+ const Input = inputs[inputKey] || {
65
+ type: "text",
66
+ props: {
67
+ placeholder: inputKey,
68
+ },
69
+ };
70
+ const Render = Input.render;
71
+ if (!Render) {
72
+ return (
73
+ <InputComponent
74
+ control={form.control}
75
+ name={inputKey}
76
+ {...Input}
77
+ key={inputKey}
78
+ />
79
+ );
80
+ }
64
81
  return (
65
- <InputComponent
66
- control={form.control}
67
- name={inputKey}
68
- {...Input}
69
- key={inputKey}
70
- />
82
+ <Render control={form.control} name={inputKey} key={inputKey} />
71
83
  );
72
- }
73
- return <Render control={form.control} name={inputKey} key={inputKey} />;
74
- })}
84
+ })}
85
+ </View>
75
86
  <Button
76
87
  className="w-full"
77
88
  children={"Submit"}
@@ -110,27 +121,27 @@ const InputComponent = ({
110
121
 
111
122
  type TextInputType = {
112
123
  type: "text";
113
- props: InputProps;
124
+ props?: InputProps;
114
125
  };
115
126
  type FloatingInputType = {
116
127
  type: "floating";
117
- props: FloatingInputProps;
128
+ props?: FloatingInputProps;
118
129
  };
119
130
  type SelectInputType = {
120
131
  type: "select";
121
- props: SelectProps;
132
+ props?: SelectProps;
122
133
  };
123
134
  type MultiSelectInputType = {
124
135
  type: "multi-select";
125
- props: MultiSelectProps;
136
+ props?: MultiSelectProps;
126
137
  };
127
138
  type PinInputType = {
128
139
  type: "pin";
129
- props: PinInputProps;
140
+ props?: PinInputProps;
130
141
  };
131
142
  type PhoneInputType = {
132
143
  type: "phone";
133
- props: TextInputProps;
144
+ props?: TextInputProps;
134
145
  };
135
146
  type InputType =
136
147
  | TextInputType
@@ -18,3 +18,5 @@ export * from "./check";
18
18
  export * from "./card";
19
19
  export * from "./indicator";
20
20
  export * from "./loader-dialog";
21
+ export * from "./stepper";
22
+ export * from "./multi-step-form";
@@ -133,18 +133,18 @@ const BaseInput = <T extends Record<string, any>>({
133
133
  </Pressable>
134
134
  )}
135
135
 
136
+ {isSecretToggleable && (
137
+ <ShowPassword
138
+ showPassword={showPassword}
139
+ setShowPassword={setShowPassword}
140
+ />
141
+ )}
142
+
136
143
  {rightElement}
137
144
  </View>
138
145
  {helperText && <Text className="text-muted text-sm">{helperText}</Text>}
139
146
 
140
147
  {error && <Text className="text-danger text-sm">{error}</Text>}
141
-
142
- {isSecretToggleable && (
143
- <ShowPassword
144
- showPassword={showPassword}
145
- setShowPassword={setShowPassword}
146
- />
147
- )}
148
148
  </View>
149
149
  );
150
150
  };
@@ -11,7 +11,7 @@ export default function ShowPassword({
11
11
  return (
12
12
  <Pressable
13
13
  onPress={() => setShowPassword(!showPassword)}
14
- className="absolute right-2 bottom-2"
14
+ className="absolute right-0 bottom-0 items-center justify-center h-full p-2"
15
15
  >
16
16
  {showPassword ? (
17
17
  <Eye size={20} color={useColor("foreground")} />
@@ -0,0 +1,27 @@
1
+ import { View } from "@nativetail/core";
2
+ import { FormBuilder, FormBuilderProps } from "../form-builder";
3
+ import { Stepper } from "../stepper";
4
+ import { z } from "zod";
5
+ type FormStep<
6
+ T extends string,
7
+ ISchema extends z.ZodRawShape,
8
+ > = FormBuilderProps<ISchema> & {
9
+ name: T;
10
+ };
11
+
12
+ type MultiStepFormProps<T extends string, ISchema extends z.ZodRawShape> = {
13
+ steps: FormStep<T, any>[];
14
+ activeStep: T;
15
+ };
16
+ export function MultiStepForm<T extends string, ISchema extends z.ZodRawShape>({
17
+ activeStep,
18
+ steps,
19
+ }: MultiStepFormProps<T, ISchema>) {
20
+ const currentStep = steps.find((step) => step.name === activeStep);
21
+ return (
22
+ <View className="flex-1">
23
+ <Stepper steps={steps.map((step) => step.name)} activeStep={activeStep} />
24
+ <FormBuilder containerClassname="mt-4 flex-1" {...currentStep} />
25
+ </View>
26
+ );
27
+ }
@@ -1,31 +1,45 @@
1
- import { cn, View } from "@nativetail/core";
2
- import { useCallback } from "react";
1
+ import CircularProgress from "react-native-circular-progress-indicator";
3
2
 
4
- export type StepperProps = {
5
- steps: string[];
6
- activeStepIndex: number;
7
- setActiveStepIndex?: (index: number) => void;
3
+ import { Text, useColor, View } from "@nativetail/core";
4
+ import { CircularProgressBaseProps } from "react-native-circular-progress-indicator/lib/typescript/types";
5
+
6
+ export type StepperProps<T extends string> = {
7
+ steps: T[];
8
+ activeStep: T;
9
+ progressProps?: CircularProgressBaseProps;
8
10
  };
9
- export const Stepper = (props: StepperProps) => {
10
- const renderSteps = useCallback(() => {
11
- return props.steps.map((step, index) => {
12
- const isActive = index === props.activeStepIndex;
13
- const isCompleted = index < props.activeStepIndex;
14
- return (
15
- <View key={index} className="flex-row items-center">
16
- <View
17
- className={cn("w-4 h-4 rounded-full", {
18
- "bg-primary": isActive,
19
- "bg-gray-300": !isActive && !isCompleted,
20
- "bg-success": isCompleted,
21
- })}
22
- />
23
- {index < props.steps.length - 1 && (
24
- <View className="h-0.5 bg-gray-300 w-4" />
25
- )}
11
+ export function Stepper<T extends string>({
12
+ steps,
13
+ activeStep,
14
+ progressProps,
15
+ }: StepperProps<T>) {
16
+ const activeStepIndex = steps.findIndex((step) => step === activeStep);
17
+ const stepProgresss = ((activeStepIndex + 1) / steps.length) * 100;
18
+ const nextStep = steps[activeStepIndex + 1];
19
+ const nextStepData = nextStep ? nextStep : "Completed";
20
+
21
+ return (
22
+ <View className="flex-row items-center gap-2">
23
+ <View className="w-17 h-17 ">
24
+ <CircularProgress
25
+ value={stepProgresss}
26
+ activeStrokeWidth={8}
27
+ radius={33}
28
+ showProgressValue={false}
29
+ activeStrokeColor={useColor("primary")}
30
+ inActiveStrokeColor={useColor("primary/15")}
31
+ {...progressProps}
32
+ />
33
+ <View className="absolute top-0 left-0 w-full h-full text-center flex items-center justify-center ">
34
+ <Text className="text-sm foreground ">
35
+ {activeStepIndex + 1} of {steps.length}
36
+ </Text>
26
37
  </View>
27
- );
28
- });
29
- }, [props.steps, props.activeStepIndex]);
30
- return <View className="flex-row items-center">{renderSteps()}</View>;
31
- };
38
+ </View>
39
+ <View className="gap-0.5">
40
+ <Text className="text-[16px] font-medium">{activeStep}</Text>
41
+ <Text className="text-sm text-muted">Next: {nextStepData}</Text>
42
+ </View>
43
+ </View>
44
+ );
45
+ }