@korsolutions/ui 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.d.mts +84 -0
- package/dist/components/index.mjs +288 -0
- package/dist/index-Dafk8ZGv.d.mts +265 -0
- package/dist/index.d.mts +45 -0
- package/dist/index.mjs +11 -0
- package/dist/portal-DoPaAohb.mjs +61 -0
- package/dist/primitives/index.d.mts +2 -0
- package/dist/primitives/index.mjs +4 -0
- package/dist/primitives-C2enZ5Ku.mjs +441 -0
- package/dist/themes-BrLbh9h6.mjs +86 -0
- package/package.json +20 -8
- package/src/components/button/button.tsx +24 -0
- package/src/components/button/variants/default.tsx +52 -0
- package/src/components/button/variants/index.ts +5 -0
- package/src/components/card/card.tsx +26 -0
- package/src/components/card/variants/default.tsx +38 -0
- package/src/components/card/variants/index.ts +5 -0
- package/src/components/field/field.tsx +27 -0
- package/src/components/field/variants/default.tsx +29 -0
- package/src/components/field/variants/index.ts +5 -0
- package/src/components/index.ts +5 -0
- package/src/components/input/input.tsx +14 -0
- package/src/components/input/variants/default.tsx +34 -0
- package/src/components/input/variants/index.ts +5 -0
- package/src/components/select/select.tsx +35 -0
- package/src/components/select/variants/default.tsx +81 -0
- package/src/components/select/variants/index.ts +5 -0
- package/src/index.tsx +13 -0
- package/src/primitives/button/button-context.tsx +5 -5
- package/src/primitives/button/button-label.tsx +7 -5
- package/src/primitives/button/button-root.tsx +12 -8
- package/src/primitives/button/button-spinner.tsx +14 -0
- package/src/primitives/button/index.ts +5 -3
- package/src/primitives/button/types.ts +6 -5
- package/src/primitives/card/card-body.tsx +1 -1
- package/src/primitives/card/card-footer.tsx +1 -1
- package/src/primitives/card/card-header.tsx +1 -1
- package/src/primitives/card/card-root.tsx +1 -1
- package/src/primitives/card/card-title.tsx +1 -1
- package/src/primitives/card/index.ts +1 -1
- package/src/primitives/field/context.ts +5 -19
- package/src/primitives/field/field-description.tsx +17 -0
- package/src/primitives/field/field-error.tsx +17 -0
- package/src/primitives/field/field-label.tsx +7 -3
- package/src/primitives/field/field-root.tsx +13 -59
- package/src/primitives/field/index.ts +9 -6
- package/src/primitives/field/types.ts +8 -7
- package/src/primitives/index.ts +0 -2
- package/src/primitives/input/index.ts +1 -1
- package/src/primitives/input/input.tsx +48 -13
- package/src/primitives/input/types.ts +2 -4
- package/src/primitives/select/context.ts +3 -3
- package/src/primitives/select/index.ts +2 -2
- package/src/primitives/select/select-content.tsx +2 -2
- package/src/primitives/select/select-option.tsx +27 -4
- package/src/primitives/select/select-overlay.tsx +1 -1
- package/src/primitives/select/select-root.tsx +13 -11
- package/src/primitives/select/select-trigger.tsx +3 -2
- package/src/primitives/select/select-value.tsx +4 -1
- package/src/primitives/select/types.ts +3 -1
- package/src/themes/default/colors.ts +45 -0
- package/src/themes/default/index.ts +11 -0
- package/src/themes/index.ts +2 -0
- package/src/themes/provider.tsx +56 -0
- package/src/themes/themes.ts +6 -0
- package/src/themes/types.ts +30 -0
- package/src/utils/hsla-utils.ts +10 -0
- package/src/utils/use-themed-styles.ts +13 -0
- package/tsconfig.json +8 -0
- package/tsdown.config.ts +8 -0
- package/src/composites/card.tsx +0 -23
- package/src/composites/index.ts +0 -1
- package/src/index.ts +0 -1
- package/src/primitives/field/field-control.tsx +0 -29
- package/src/primitives/provider.tsx +0 -10
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleProp, Text, TextStyle } from "react-native";
|
|
3
|
+
import { useField } from "./context";
|
|
4
|
+
|
|
5
|
+
export interface FieldErrorProps {
|
|
6
|
+
children: string;
|
|
7
|
+
|
|
8
|
+
render?: (props: FieldErrorProps) => React.ReactNode;
|
|
9
|
+
|
|
10
|
+
style?: StyleProp<TextStyle>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function FieldError(props: FieldErrorProps) {
|
|
14
|
+
const field = useField();
|
|
15
|
+
const Component = props.render ?? Text;
|
|
16
|
+
return <Component {...props} style={[field.styles?.error, props.style]} />;
|
|
17
|
+
}
|
|
@@ -4,13 +4,17 @@ import { Text, TextProps } from "react-native";
|
|
|
4
4
|
|
|
5
5
|
export interface FieldLabelProps {
|
|
6
6
|
children: string;
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
htmlFor?: string;
|
|
9
|
+
|
|
8
10
|
render?: (props: FieldLabelProps) => React.ReactNode;
|
|
11
|
+
|
|
12
|
+
style?: TextProps["style"];
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
export function FieldLabel(props: FieldLabelProps) {
|
|
12
16
|
const field = useField();
|
|
13
|
-
const calculatedStyle = [field.styles?.label?.default, field.styles?.label?.[field.state], props.style];
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
const Component = props.render ?? Text;
|
|
19
|
+
return <Component {...props} htmlFor={props.htmlFor} style={[field.styles?.label, props.style]} />;
|
|
16
20
|
}
|
|
@@ -1,69 +1,23 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleProp, View, ViewStyle } from "react-native";
|
|
3
3
|
import { FieldContext } from "./context";
|
|
4
|
-
import {
|
|
4
|
+
import { FieldStyles } from "./types";
|
|
5
5
|
|
|
6
|
-
export interface
|
|
7
|
-
value: string | null;
|
|
8
|
-
onChange: (value: string) => void;
|
|
9
|
-
|
|
10
|
-
required?: boolean;
|
|
11
|
-
disabled?: boolean;
|
|
12
|
-
error?: string | null;
|
|
6
|
+
export interface FieldPrimitiveRootProps {
|
|
13
7
|
children?: React.ReactNode;
|
|
14
|
-
style?: StyleProp<ViewStyle>;
|
|
15
|
-
|
|
16
|
-
styles?: FieldStyles<TControlStyles>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const calculateState = (props: FieldRootProps, focused: boolean, hovered: boolean): FieldState => {
|
|
20
|
-
if (props.disabled) {
|
|
21
|
-
return "disabled";
|
|
22
|
-
}
|
|
23
|
-
if (props.error) {
|
|
24
|
-
return "error";
|
|
25
|
-
}
|
|
26
|
-
if (focused) {
|
|
27
|
-
return "focused";
|
|
28
|
-
}
|
|
29
|
-
if (hovered) {
|
|
30
|
-
return "hovered";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return "default";
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export function FieldRoot<TControlStyles = unknown>(props: FieldRootProps<TControlStyles>) {
|
|
37
|
-
const [focused, setFocused] = useState(false);
|
|
38
|
-
const [hovered, setHovered] = useState(false);
|
|
39
8
|
|
|
40
|
-
|
|
9
|
+
render?: (props: FieldPrimitiveRootProps) => React.ReactNode;
|
|
41
10
|
|
|
42
|
-
|
|
11
|
+
style?: StyleProp<ViewStyle>;
|
|
12
|
+
styles?: FieldStyles;
|
|
13
|
+
}
|
|
43
14
|
|
|
15
|
+
export function FieldRoot(props: FieldPrimitiveRootProps) {
|
|
16
|
+
const composedStyles = [props.styles?.root, props.style];
|
|
17
|
+
const Component = props.render ?? View;
|
|
44
18
|
return (
|
|
45
|
-
<FieldContext.Provider
|
|
46
|
-
|
|
47
|
-
value: props.value,
|
|
48
|
-
onChange: props.onChange,
|
|
49
|
-
|
|
50
|
-
focused,
|
|
51
|
-
setFocused,
|
|
52
|
-
|
|
53
|
-
hovered,
|
|
54
|
-
setHovered,
|
|
55
|
-
|
|
56
|
-
required: props.required,
|
|
57
|
-
disabled: props.disabled,
|
|
58
|
-
error: props.error ?? null,
|
|
59
|
-
|
|
60
|
-
state: state,
|
|
61
|
-
styles: props.styles,
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
64
|
-
<Pressable onHoverIn={() => setHovered(true)} onHoverOut={() => setHovered(false)} style={calculatedStyle}>
|
|
65
|
-
{props.children}
|
|
66
|
-
</Pressable>
|
|
19
|
+
<FieldContext.Provider value={{ styles: props.styles }}>
|
|
20
|
+
<Component {...props} style={composedStyles} />
|
|
67
21
|
</FieldContext.Provider>
|
|
68
22
|
);
|
|
69
23
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { FieldRoot } from "./field-root";
|
|
2
2
|
import { FieldLabel } from "./field-label";
|
|
3
|
-
import {
|
|
3
|
+
import { FieldDescription } from "./field-description";
|
|
4
|
+
import { FieldError } from "./field-error";
|
|
4
5
|
|
|
5
|
-
export const
|
|
6
|
+
export const FieldPrimitive = {
|
|
6
7
|
Root: FieldRoot,
|
|
7
8
|
Label: FieldLabel,
|
|
8
|
-
|
|
9
|
+
Description: FieldDescription,
|
|
10
|
+
Error: FieldError,
|
|
9
11
|
};
|
|
10
12
|
|
|
11
|
-
export type {
|
|
13
|
+
export type { FieldPrimitiveRootProps } from "./field-root";
|
|
12
14
|
export type { FieldLabelProps } from "./field-label";
|
|
13
|
-
export type {
|
|
14
|
-
export
|
|
15
|
+
export type { FieldDescriptionProps } from "./field-description";
|
|
16
|
+
export type { FieldErrorProps } from "./field-error";
|
|
17
|
+
export type { FieldStyles } from "./types";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { FieldPrimitiveRootProps } from "./field-root";
|
|
1
2
|
import { FieldLabelProps } from "./field-label";
|
|
2
|
-
import {
|
|
3
|
+
import { FieldDescriptionProps } from "./field-description";
|
|
4
|
+
import { FieldErrorProps } from "./field-error";
|
|
3
5
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
control?: Partial<Record<FieldState, TControlStyles>>;
|
|
6
|
+
export interface FieldStyles {
|
|
7
|
+
root?: FieldPrimitiveRootProps["style"];
|
|
8
|
+
label?: FieldLabelProps["style"];
|
|
9
|
+
description?: FieldDescriptionProps["style"];
|
|
10
|
+
error?: FieldErrorProps["style"];
|
|
10
11
|
}
|
package/src/primitives/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./input";
|
|
2
|
-
export { InputStyles } from "./types";
|
|
2
|
+
export type { InputStyles } from "./types";
|
|
@@ -1,20 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { InputStyles } from "./types";
|
|
3
|
-
import {
|
|
1
|
+
import { TextInput, TextInputProps } from "react-native";
|
|
2
|
+
import { InputState, InputStyles } from "./types";
|
|
3
|
+
import { useState } from "react";
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
value: string | null;
|
|
8
|
-
onChange: (value: string) => void;
|
|
5
|
+
export type InputPrimitiveBaseProps = Omit<TextInputProps, "onChange"> & {
|
|
6
|
+
onChange?: TextInputProps["onChangeText"];
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
isDisabled?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface InputPrimitiveProps extends InputPrimitiveBaseProps {
|
|
12
|
+
render?: (props: InputPrimitiveProps) => React.ReactNode;
|
|
12
13
|
|
|
13
|
-
style?: StyleProp<TextStyle>;
|
|
14
14
|
styles?: InputStyles;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const calculateState = (props: InputPrimitiveProps, isFocused: boolean): InputState => {
|
|
18
|
+
if (props.isDisabled) {
|
|
19
|
+
return "disabled";
|
|
20
|
+
}
|
|
21
|
+
if (isFocused) {
|
|
22
|
+
return "focused";
|
|
23
|
+
}
|
|
24
|
+
return "default";
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function InputPrimitive(props: InputPrimitiveProps) {
|
|
28
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
29
|
+
const state = calculateState(props, isFocused);
|
|
30
|
+
|
|
31
|
+
const composedStyles = [props.styles?.default?.style, props.styles?.[state]?.style, props.style];
|
|
32
|
+
const composedProps = {
|
|
33
|
+
...props.styles?.default,
|
|
34
|
+
...props.styles?.[state],
|
|
35
|
+
...props,
|
|
36
|
+
};
|
|
37
|
+
const Component = props.render ?? TextInput;
|
|
38
|
+
return (
|
|
39
|
+
<Component
|
|
40
|
+
{...composedProps}
|
|
41
|
+
onChange={undefined}
|
|
42
|
+
onChangeText={props.onChange}
|
|
43
|
+
onFocus={(e) => {
|
|
44
|
+
setIsFocused(true);
|
|
45
|
+
props.onFocus?.(e);
|
|
46
|
+
}}
|
|
47
|
+
onBlur={(e) => {
|
|
48
|
+
setIsFocused(false);
|
|
49
|
+
props.onBlur?.(e);
|
|
50
|
+
}}
|
|
51
|
+
readOnly={props.isDisabled || props.readOnly}
|
|
52
|
+
style={composedStyles}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
20
55
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InputPrimitiveBaseProps } from "./input";
|
|
2
2
|
|
|
3
3
|
export type InputState = "default" | "focused" | "disabled";
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
root: Partial<Record<InputState, InputProps["style"]>>;
|
|
7
|
-
}
|
|
5
|
+
export type InputStyles = Partial<Record<InputState, InputPrimitiveBaseProps>>;
|
|
@@ -3,9 +3,9 @@ import { SelectOption, SelectState, SelectStyles } from "./types";
|
|
|
3
3
|
import { LayoutRectangle } from "react-native";
|
|
4
4
|
|
|
5
5
|
export interface SelectContext {
|
|
6
|
-
value
|
|
6
|
+
value?: string;
|
|
7
7
|
onChange?: (value: string) => void;
|
|
8
|
-
placeholder
|
|
8
|
+
placeholder?: string;
|
|
9
9
|
|
|
10
10
|
isOpen: boolean;
|
|
11
11
|
setIsOpen: Dispatch<React.SetStateAction<boolean>>;
|
|
@@ -14,7 +14,7 @@ export interface SelectContext {
|
|
|
14
14
|
options: Array<SelectOption>;
|
|
15
15
|
setOptions: Dispatch<React.SetStateAction<Array<SelectOption>>>;
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
isDisabled: boolean;
|
|
18
18
|
|
|
19
19
|
state: SelectState;
|
|
20
20
|
styles: SelectStyles | null;
|
|
@@ -6,7 +6,7 @@ import { SelectOverlay } from "./select-overlay";
|
|
|
6
6
|
import { SelectContent } from "./select-content";
|
|
7
7
|
import { SelectOption } from "./select-option";
|
|
8
8
|
|
|
9
|
-
export const
|
|
9
|
+
export const SelectPrimitive = {
|
|
10
10
|
Root: SelectRoot,
|
|
11
11
|
Trigger: SelectTrigger,
|
|
12
12
|
Value: SelectValue,
|
|
@@ -16,7 +16,7 @@ export const Select = {
|
|
|
16
16
|
Option: SelectOption,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export type { SelectRootProps } from "./select-root";
|
|
19
|
+
export type { SelectRootProps, SelectRootBaseProps } from "./select-root";
|
|
20
20
|
export type { SelectTriggerProps } from "./select-trigger";
|
|
21
21
|
export type { SelectValueProps } from "./select-value";
|
|
22
22
|
export type { SelectPortalProps } from "./select-portal";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { StyleProp, View, ViewStyle } from "react-native";
|
|
3
|
-
import { calculateComposedStyles } from "
|
|
3
|
+
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
4
4
|
import { useSelect } from "./context";
|
|
5
5
|
|
|
6
6
|
export interface SelectContentProps {
|
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
import { StyleProp, Text, TextStyle } from "react-native";
|
|
2
|
-
import { calculateComposedStyles } from "
|
|
2
|
+
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
3
3
|
import { useSelect } from "./context";
|
|
4
|
-
import { useEffect } from "react";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { SelectOptionState, SelectState } from "./types";
|
|
5
6
|
|
|
6
7
|
export interface SelectOptionProps {
|
|
7
8
|
children: string;
|
|
8
9
|
value: string;
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
onMouseEnter?: () => void;
|
|
12
|
+
onMouseLeave?: () => void;
|
|
11
13
|
|
|
12
14
|
render?: (props: SelectOptionProps) => React.ReactElement;
|
|
15
|
+
|
|
16
|
+
style?: StyleProp<TextStyle>;
|
|
13
17
|
}
|
|
14
18
|
|
|
19
|
+
const calculateState = (selectState: SelectState, hovered: boolean, selected: boolean): SelectOptionState => {
|
|
20
|
+
if (selectState === "disabled") {
|
|
21
|
+
return "disabled";
|
|
22
|
+
}
|
|
23
|
+
if (selected) {
|
|
24
|
+
return "selected";
|
|
25
|
+
}
|
|
26
|
+
if (hovered) {
|
|
27
|
+
return "hovered";
|
|
28
|
+
}
|
|
29
|
+
return "default";
|
|
30
|
+
};
|
|
31
|
+
|
|
15
32
|
export function SelectOption(props: SelectOptionProps) {
|
|
33
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
16
34
|
const select = useSelect();
|
|
17
|
-
const
|
|
35
|
+
const isSelected = select.value === props.value;
|
|
36
|
+
|
|
37
|
+
const optionState = calculateState(select.state, isHovered, isSelected);
|
|
38
|
+
const composedStyles = calculateComposedStyles(select.styles, optionState, "option", props.style);
|
|
18
39
|
|
|
19
40
|
useEffect(() => {
|
|
20
41
|
select.setOptions((prev) => {
|
|
@@ -33,6 +54,8 @@ export function SelectOption(props: SelectOptionProps) {
|
|
|
33
54
|
select.onChange?.(props.value);
|
|
34
55
|
select.setIsOpen(false);
|
|
35
56
|
}}
|
|
57
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
58
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
36
59
|
style={composedStyles}
|
|
37
60
|
>
|
|
38
61
|
{props.children}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useSelect } from "./context";
|
|
3
3
|
import { Pressable, StyleProp, StyleSheet, ViewStyle } from "react-native";
|
|
4
|
-
import { calculateComposedStyles } from "
|
|
4
|
+
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
5
5
|
|
|
6
6
|
export interface SelectOverlayProps {
|
|
7
7
|
children?: React.ReactNode;
|
|
@@ -2,20 +2,22 @@ import React, { useState } from "react";
|
|
|
2
2
|
import { LayoutRectangle, StyleProp, View, ViewStyle } from "react-native";
|
|
3
3
|
import { SelectContext } from "./context";
|
|
4
4
|
import { SelectOption, SelectState, SelectStyles } from "./types";
|
|
5
|
-
import { calculateComposedStyles } from "
|
|
5
|
+
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
6
6
|
|
|
7
7
|
interface SelectRootInjectedProps {
|
|
8
8
|
style?: StyleProp<ViewStyle>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export interface
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
value: string | null;
|
|
15
|
-
onChange: (value: string) => void;
|
|
11
|
+
export interface SelectRootBaseProps {
|
|
12
|
+
value?: string;
|
|
13
|
+
onChange?: (value: string) => void;
|
|
16
14
|
placeholder?: string;
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
isDisabled?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SelectRootProps extends SelectRootBaseProps {
|
|
20
|
+
children?: React.ReactNode;
|
|
19
21
|
|
|
20
22
|
render?: (props: SelectRootInjectedProps) => React.ReactElement;
|
|
21
23
|
|
|
@@ -24,7 +26,7 @@ export interface SelectRootProps {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
const calculateState = (props: SelectRootProps): SelectState => {
|
|
27
|
-
if (props.
|
|
29
|
+
if (props.isDisabled) {
|
|
28
30
|
return "disabled";
|
|
29
31
|
}
|
|
30
32
|
return "default";
|
|
@@ -42,9 +44,9 @@ export function SelectRoot(props: SelectRootProps) {
|
|
|
42
44
|
return (
|
|
43
45
|
<SelectContext.Provider
|
|
44
46
|
value={{
|
|
45
|
-
value: props.value
|
|
47
|
+
value: props.value,
|
|
46
48
|
onChange: props.onChange,
|
|
47
|
-
placeholder: props.placeholder
|
|
49
|
+
placeholder: props.placeholder,
|
|
48
50
|
isOpen,
|
|
49
51
|
setIsOpen,
|
|
50
52
|
triggerLayout,
|
|
@@ -52,7 +54,7 @@ export function SelectRoot(props: SelectRootProps) {
|
|
|
52
54
|
options,
|
|
53
55
|
setOptions,
|
|
54
56
|
state,
|
|
55
|
-
|
|
57
|
+
isDisabled: props.isDisabled ?? false,
|
|
56
58
|
styles: props.styles ?? null,
|
|
57
59
|
}}
|
|
58
60
|
>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Pressable, StyleProp, ViewStyle } from "react-native";
|
|
3
3
|
import { useSelect } from "./context";
|
|
4
|
-
import { calculateComposedStyles } from "
|
|
5
|
-
import { normalizeLayout } from "
|
|
4
|
+
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
5
|
+
import { normalizeLayout } from "@/utils/normalize-layout";
|
|
6
6
|
|
|
7
7
|
interface SelectTriggerInjectionProps {
|
|
8
8
|
onPress?: () => void;
|
|
@@ -29,6 +29,7 @@ export function SelectTrigger(props: SelectTriggerProps) {
|
|
|
29
29
|
const layout = normalizeLayout(e.nativeEvent.layout);
|
|
30
30
|
select.setTriggerLayout(layout);
|
|
31
31
|
}}
|
|
32
|
+
disabled={select.isDisabled}
|
|
32
33
|
style={composedStyles}
|
|
33
34
|
>
|
|
34
35
|
{props.children}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { StyleProp, Text, TextStyle } from "react-native";
|
|
3
3
|
import { useSelect } from "./context";
|
|
4
|
+
import { calculateComposedStyles } from "@/utils/calculate-styles";
|
|
4
5
|
|
|
5
6
|
export interface SelectValueProps {
|
|
6
7
|
style?: StyleProp<TextStyle>;
|
|
@@ -12,7 +13,9 @@ export function SelectValue(props: SelectValueProps) {
|
|
|
12
13
|
const select = useSelect();
|
|
13
14
|
|
|
14
15
|
const selectedOption = select.options.find((option) => option.value === select.value);
|
|
16
|
+
const selectedOptionLabel = selectedOption?.label;
|
|
15
17
|
|
|
18
|
+
const composedStyles = calculateComposedStyles(select.styles, select.state, selectedOptionLabel ? "value" : "placeholder", props.style);
|
|
16
19
|
const Component = props.render ?? Text;
|
|
17
|
-
return <Component style={
|
|
20
|
+
return <Component style={composedStyles}>{selectedOption?.label ?? select.placeholder}</Component>;
|
|
18
21
|
}
|
|
@@ -6,14 +6,16 @@ import { SelectContentProps } from "./select-content";
|
|
|
6
6
|
import { SelectOptionProps } from "./select-option";
|
|
7
7
|
|
|
8
8
|
export type SelectState = "default" | "disabled";
|
|
9
|
+
export type SelectOptionState = SelectState | "hovered" | "selected";
|
|
9
10
|
|
|
10
11
|
export interface SelectStyles {
|
|
11
12
|
root?: Partial<Record<SelectState, SelectRootProps["style"]>>;
|
|
12
13
|
trigger?: Partial<Record<SelectState, SelectTriggerProps["style"]>>;
|
|
13
14
|
value?: Partial<Record<SelectState, SelectValueProps["style"]>>;
|
|
15
|
+
placeholder?: Partial<Record<SelectState, SelectValueProps["style"]>>;
|
|
14
16
|
overlay?: Partial<Record<SelectState, SelectOverlayProps["style"]>>;
|
|
15
17
|
content?: Partial<Record<SelectState, SelectContentProps["style"]>>;
|
|
16
|
-
option?: Partial<Record<
|
|
18
|
+
option?: Partial<Record<SelectOptionState, SelectOptionProps["style"]>>;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface SelectOption {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Colors } from "../types";
|
|
2
|
+
|
|
3
|
+
export const lightColors: Colors = {
|
|
4
|
+
background: "hsla(223.81, 100%, 100%, 1)",
|
|
5
|
+
foreground: "hsla(223.81, 0%, 3.94%, 1)",
|
|
6
|
+
|
|
7
|
+
primary: "hsla(223.81, 0%, 9.05%, 1)",
|
|
8
|
+
primaryForeground: "hsla(223.81, 0%, 98.03%, 1)",
|
|
9
|
+
|
|
10
|
+
secondary: "hsla(223.81, 0%, 96.06%, 1)",
|
|
11
|
+
secondaryForeground: "hsla(223.81, 0%, 9.05%, 1)",
|
|
12
|
+
|
|
13
|
+
muted: "hsla(223.81, 0%, 96.06%, 1)",
|
|
14
|
+
mutedForeground: "hsla(223.81, 0%, 45.15%, 1)",
|
|
15
|
+
|
|
16
|
+
border: "hsla(223.81, 0%, 89.82%, 1)",
|
|
17
|
+
surface: "hsla(223.81, 100%, 100%, 1)",
|
|
18
|
+
|
|
19
|
+
danger: "hsla(0, 84.2%, 60.2%, 1)",
|
|
20
|
+
success: "hsla(173, 58%, 39%, 1)",
|
|
21
|
+
warning: "hsla(43, 74%, 66%, 1)",
|
|
22
|
+
info: "hsla(197, 37%, 24%, 1)",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const darkColors: Colors = {
|
|
26
|
+
background: "hsla(223.81, 0%, 3.94%, 1)",
|
|
27
|
+
foreground: "hsla(223.81, 0%, 98.03%, 1)",
|
|
28
|
+
|
|
29
|
+
primary: "hsla(223.81, 0%, 89.82%, 1)",
|
|
30
|
+
primaryForeground: "hsla(223.81, 0%, 9.05%, 1)",
|
|
31
|
+
|
|
32
|
+
secondary: "hsla(223.81, 0%, 14.94%, 1)",
|
|
33
|
+
secondaryForeground: "hsla(223.81, 0%, 98.03%, 1)",
|
|
34
|
+
|
|
35
|
+
muted: "hsla(223.81, 0%, 14.94%, 1)",
|
|
36
|
+
mutedForeground: "hsla(223.81, 0%, 63.02%, 1)",
|
|
37
|
+
|
|
38
|
+
border: "hsla(223.81, 0%, 15.51%, 1)",
|
|
39
|
+
surface: "hsla(223.81, 0%, 9.05%, 1)",
|
|
40
|
+
|
|
41
|
+
danger: "hsla(0, 62.8%, 30.6%, 1)",
|
|
42
|
+
success: "hsla(160, 60%, 45%, 1)",
|
|
43
|
+
warning: "hsla(30, 80%, 55%, 1)",
|
|
44
|
+
info: "hsla(220, 70%, 50%, 1)",
|
|
45
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createContext, PropsWithChildren, useContext, useEffect, useState } from "react";
|
|
2
|
+
import { Colors, ColorScheme, Radius, ThemeName } from "./types";
|
|
3
|
+
import { themes } from "./themes";
|
|
4
|
+
import { useColorScheme } from "react-native";
|
|
5
|
+
|
|
6
|
+
interface ThemeContext {
|
|
7
|
+
colors: Colors;
|
|
8
|
+
radius: Radius;
|
|
9
|
+
fontFamily: string;
|
|
10
|
+
colorScheme: ColorScheme;
|
|
11
|
+
setColorScheme: (scheme: ColorScheme) => void;
|
|
12
|
+
setTheme: (themeName: ThemeName) => void;
|
|
13
|
+
themeName: ThemeName;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ThemeContext = createContext<ThemeContext | null>(null);
|
|
17
|
+
|
|
18
|
+
export const ThemeProvider = (props: PropsWithChildren) => {
|
|
19
|
+
const [themeName, setTheme] = useState<ThemeName>("default");
|
|
20
|
+
|
|
21
|
+
const systemColorScheme = useColorScheme();
|
|
22
|
+
const [colorScheme, setColorScheme] = useState<ColorScheme>(systemColorScheme ?? "light");
|
|
23
|
+
|
|
24
|
+
const themesAssets = themes[themeName];
|
|
25
|
+
const colors = themesAssets.colors[colorScheme];
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (systemColorScheme) {
|
|
29
|
+
setColorScheme(systemColorScheme);
|
|
30
|
+
}
|
|
31
|
+
}, [systemColorScheme]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<ThemeContext.Provider
|
|
35
|
+
value={{
|
|
36
|
+
themeName,
|
|
37
|
+
setTheme,
|
|
38
|
+
colorScheme,
|
|
39
|
+
setColorScheme,
|
|
40
|
+
colors,
|
|
41
|
+
radius: themesAssets.radius,
|
|
42
|
+
fontFamily: themesAssets.fontFamily,
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
{props.children}
|
|
46
|
+
</ThemeContext.Provider>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const useTheme = () => {
|
|
51
|
+
const context = useContext(ThemeContext);
|
|
52
|
+
if (!context) {
|
|
53
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
54
|
+
}
|
|
55
|
+
return context;
|
|
56
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type ThemeName = "default";
|
|
2
|
+
export type ColorScheme = "light" | "dark";
|
|
3
|
+
|
|
4
|
+
type Color = `hsla(${number}, ${number}%, ${number}%, ${number})`;
|
|
5
|
+
|
|
6
|
+
export interface Colors {
|
|
7
|
+
background: Color;
|
|
8
|
+
foreground: Color;
|
|
9
|
+
primary: Color;
|
|
10
|
+
primaryForeground: Color;
|
|
11
|
+
secondary: Color;
|
|
12
|
+
secondaryForeground: Color;
|
|
13
|
+
muted: Color;
|
|
14
|
+
mutedForeground: Color;
|
|
15
|
+
border: Color;
|
|
16
|
+
surface: Color;
|
|
17
|
+
success: Color;
|
|
18
|
+
warning: Color;
|
|
19
|
+
danger: Color;
|
|
20
|
+
info: Color;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type Radius = number;
|
|
24
|
+
export type FontFamily = string;
|
|
25
|
+
|
|
26
|
+
export interface ThemeAssets {
|
|
27
|
+
colors: Record<ColorScheme, Colors>;
|
|
28
|
+
radius: Radius;
|
|
29
|
+
fontFamily: FontFamily;
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const hslaSetAlpha = (hsla: string, alpha: number): string => {
|
|
2
|
+
const parts = hsla.replace(/^hsla?\(|\s+|\)$/g, "").split(",");
|
|
3
|
+
if (parts.length < 3) {
|
|
4
|
+
throw new Error("Invalid HSLA color format");
|
|
5
|
+
}
|
|
6
|
+
const h = parseInt(parts[0], 10);
|
|
7
|
+
const s = parseInt(parts[1], 10);
|
|
8
|
+
const l = parseInt(parts[2], 10);
|
|
9
|
+
return `hsla(${h}, ${s}%, ${l}%, ${alpha})`;
|
|
10
|
+
};
|