@solostylist/ui-kit-native 1.0.0
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/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/s-form/index.d.ts +2 -0
- package/dist/s-form/index.d.ts.map +1 -0
- package/dist/s-form/index.js +1 -0
- package/dist/s-form/s-form.d.ts +26 -0
- package/dist/s-form/s-form.d.ts.map +1 -0
- package/dist/s-form/s-form.js +25 -0
- package/dist/s-icon-button/index.d.ts +3 -0
- package/dist/s-icon-button/index.d.ts.map +1 -0
- package/dist/s-icon-button/index.js +1 -0
- package/dist/s-icon-button/s-icon-button.d.ts +46 -0
- package/dist/s-icon-button/s-icon-button.d.ts.map +1 -0
- package/dist/s-icon-button/s-icon-button.js +57 -0
- package/dist/s-select/index.d.ts +2 -0
- package/dist/s-select/index.d.ts.map +1 -0
- package/dist/s-select/index.js +1 -0
- package/dist/s-select/s-select.d.ts +48 -0
- package/dist/s-select/s-select.d.ts.map +1 -0
- package/dist/s-select/s-select.js +237 -0
- package/dist/s-text/index.d.ts +2 -0
- package/dist/s-text/index.d.ts.map +1 -0
- package/dist/s-text/index.js +1 -0
- package/dist/s-text/s-text.d.ts +30 -0
- package/dist/s-text/s-text.d.ts.map +1 -0
- package/dist/s-text/s-text.js +59 -0
- package/dist/s-text-field/index.d.ts +2 -0
- package/dist/s-text-field/index.d.ts.map +1 -0
- package/dist/s-text-field/index.js +1 -0
- package/dist/s-text-field/s-text-field.d.ts +53 -0
- package/dist/s-text-field/s-text-field.d.ts.map +1 -0
- package/dist/s-text-field/s-text-field.js +69 -0
- package/dist/theme/index.d.ts +6 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +7 -0
- package/dist/theme/s-theme-provider.d.ts +28 -0
- package/dist/theme/s-theme-provider.d.ts.map +1 -0
- package/dist/theme/s-theme-provider.js +110 -0
- package/dist/theme/theme-primitives.d.ts +169 -0
- package/dist/theme/theme-primitives.d.ts.map +1 -0
- package/dist/theme/theme-primitives.js +209 -0
- package/package.json +80 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @solostylist/ui-kit-native
|
|
3
|
+
*
|
|
4
|
+
* React Native UI components for SoloStylist
|
|
5
|
+
* Synced with web ui-kit and uses @solostylist/core for theming
|
|
6
|
+
*/
|
|
7
|
+
export { SThemeProvider, useSTheme, darkTheme, lightTheme, type SThemeProviderProps, type ThemeMode, type NativeTheme, type NativeThemeColors, } from './theme';
|
|
8
|
+
export { SForm, type SFormProps } from './s-form';
|
|
9
|
+
export { SIconButton, type SIconButtonProps } from './s-icon-button';
|
|
10
|
+
export { SSelect, type SSelectProps } from './s-select';
|
|
11
|
+
export { SText, type STextProps } from './s-text';
|
|
12
|
+
export { STextField, type STextFieldProps } from './s-text-field';
|
|
13
|
+
export { brand, lightBrand, gray, lightGray, green, lightGreen, orange, lightOrange, blue, lightBlue, red, lightRed, purple, lightPurple, blackAlpha, whiteAlpha, darkGradients, lightGradients, darkPulse, lightPulse, } from './theme';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,cAAc,EACd,SAAS,EACT,SAAS,EACT,UAAU,EACV,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGlE,OAAO,EACL,KAAK,EACL,UAAU,EACV,IAAI,EACJ,SAAS,EACT,KAAK,EACL,UAAU,EACV,MAAM,EACN,WAAW,EACX,IAAI,EACJ,SAAS,EACT,GAAG,EACH,QAAQ,EACR,MAAM,EACN,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,SAAS,EACT,UAAU,GACX,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @solostylist/ui-kit-native
|
|
3
|
+
*
|
|
4
|
+
* React Native UI components for SoloStylist
|
|
5
|
+
* Synced with web ui-kit and uses @solostylist/core for theming
|
|
6
|
+
*/
|
|
7
|
+
// Theme
|
|
8
|
+
export { SThemeProvider, useSTheme, darkTheme, lightTheme, } from './theme';
|
|
9
|
+
// Components
|
|
10
|
+
export { SForm } from './s-form';
|
|
11
|
+
export { SIconButton } from './s-icon-button';
|
|
12
|
+
export { SSelect } from './s-select';
|
|
13
|
+
export { SText } from './s-text';
|
|
14
|
+
export { STextField } from './s-text-field';
|
|
15
|
+
// Re-export core colors for convenience
|
|
16
|
+
export { brand, lightBrand, gray, lightGray, green, lightGreen, orange, lightOrange, blue, lightBlue, red, lightRed, purple, lightPurple, blackAlpha, whiteAlpha, darkGradients, lightGradients, darkPulse, lightPulse, } from './theme';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/s-form/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SForm, default } from './s-form';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Props interface for the SForm component
|
|
4
|
+
*/
|
|
5
|
+
export interface SFormProps {
|
|
6
|
+
/** The form field label - can be text or a React element */
|
|
7
|
+
label?: string | React.ReactNode;
|
|
8
|
+
/** Optional hint text displayed below the input */
|
|
9
|
+
hint?: string;
|
|
10
|
+
/** Error message to display below the form field */
|
|
11
|
+
error?: string;
|
|
12
|
+
/** Whether the form field is required (adds asterisk to label) */
|
|
13
|
+
required?: boolean;
|
|
14
|
+
/** Child form elements (input, select, etc.) to wrap */
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A form wrapper component that provides consistent labeling, error handling, and help text.
|
|
19
|
+
* Synced with web SForm component from @solostylist/ui-kit.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SForm: {
|
|
22
|
+
({ label, hint, error, required, children }: SFormProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
displayName: string;
|
|
24
|
+
};
|
|
25
|
+
export default SForm;
|
|
26
|
+
//# sourceMappingURL=s-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s-form.d.ts","sourceRoot":"","sources":["../../src/s-form/s-form.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK;iDAAwD,UAAU;;CAkDnF,CAAC;AAIF,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { HelperText, Surface } from 'react-native-paper';
|
|
4
|
+
import { SText } from '../s-text';
|
|
5
|
+
import { useSTheme } from '../theme';
|
|
6
|
+
/**
|
|
7
|
+
* A form wrapper component that provides consistent labeling, error handling, and help text.
|
|
8
|
+
* Synced with web SForm component from @solostylist/ui-kit.
|
|
9
|
+
*/
|
|
10
|
+
export const SForm = ({ label, hint, error, required = false, children }) => {
|
|
11
|
+
const { theme } = useSTheme();
|
|
12
|
+
const styles = StyleSheet.create({
|
|
13
|
+
container: {
|
|
14
|
+
width: '100%',
|
|
15
|
+
},
|
|
16
|
+
labelContainer: {
|
|
17
|
+
flexDirection: 'row',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
marginBottom: theme.spacing.xs,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
return (_jsxs(Surface, { style: styles.container, elevation: 0, children: [label ? (_jsx(Surface, { style: styles.labelContainer, elevation: 0, children: typeof label === 'string' ? (_jsxs(_Fragment, { children: [_jsx(SText, { variant: "body2", fontWeight: "400", color: error ? 'error' : 'text.secondary', children: label }), required && (_jsx(SText, { variant: "body2", color: "error", style: { marginLeft: 2 }, children: "*" }))] })) : (label) })) : null, children, hint && !error ? (_jsx(HelperText, { type: "info", visible: true, children: hint })) : null, error ? (_jsx(HelperText, { type: "error", visible: true, children: error })) : null] }));
|
|
23
|
+
};
|
|
24
|
+
SForm.displayName = 'SForm';
|
|
25
|
+
export default SForm;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/s-icon-button/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SIconButton, default } from './s-icon-button';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type IconButtonProps } from 'react-native-paper';
|
|
3
|
+
/**
|
|
4
|
+
* Tooltip options for SIconButton
|
|
5
|
+
* Similar to web version but adapted for React Native
|
|
6
|
+
*/
|
|
7
|
+
export interface SIconButtonTooltipOptions {
|
|
8
|
+
/** Tooltip text content */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Whether to show enter delay (in ms) */
|
|
11
|
+
enterDelay?: number;
|
|
12
|
+
/** Whether to show leave delay (in ms) */
|
|
13
|
+
leaveDelay?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Props interface for SIconButton component
|
|
17
|
+
* Synced with web SIconButton from @solostylist/ui-kit
|
|
18
|
+
*/
|
|
19
|
+
export interface SIconButtonProps extends Omit<IconButtonProps, 'theme' | 'size'> {
|
|
20
|
+
/** Icon name from Material Community Icons */
|
|
21
|
+
icon: string;
|
|
22
|
+
/** Size of the icon button - matches web MUI sizes */
|
|
23
|
+
size?: 'small' | 'medium' | 'large';
|
|
24
|
+
/** Icon color */
|
|
25
|
+
iconColor?: string;
|
|
26
|
+
/** Container color (background) */
|
|
27
|
+
containerColor?: string;
|
|
28
|
+
/** Whether the button is disabled */
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
/** Accessibility label */
|
|
31
|
+
accessibilityLabel?: string;
|
|
32
|
+
/** Callback fired when button is pressed */
|
|
33
|
+
onPress?: () => void;
|
|
34
|
+
/** Optional tooltip configuration (excludes 'children' which is handled internally) */
|
|
35
|
+
tooltipOptions?: SIconButtonTooltipOptions;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* An enhanced icon button component with optional tooltip integration.
|
|
39
|
+
* Synced with web SIconButton from @solostylist/ui-kit.
|
|
40
|
+
*/
|
|
41
|
+
export declare const SIconButton: {
|
|
42
|
+
({ icon, size, iconColor, containerColor, disabled, accessibilityLabel, onPress, style, tooltipOptions, ...props }: SIconButtonProps): React.JSX.Element;
|
|
43
|
+
displayName: string;
|
|
44
|
+
};
|
|
45
|
+
export default SIconButton;
|
|
46
|
+
//# sourceMappingURL=s-icon-button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s-icon-button.d.ts","sourceRoot":"","sources":["../../src/s-icon-button/s-icon-button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG/E;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/E,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,uFAAuF;IACvF,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW;wHAWrB,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO;;CAuEtC,CAAC;AAIF,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { IconButton, Tooltip } from 'react-native-paper';
|
|
4
|
+
import { useSTheme } from '../theme';
|
|
5
|
+
/**
|
|
6
|
+
* An enhanced icon button component with optional tooltip integration.
|
|
7
|
+
* Synced with web SIconButton from @solostylist/ui-kit.
|
|
8
|
+
*/
|
|
9
|
+
export const SIconButton = ({ icon, size = 'medium', iconColor, containerColor, disabled = false, accessibilityLabel, onPress, style, tooltipOptions, ...props }) => {
|
|
10
|
+
const { theme } = useSTheme();
|
|
11
|
+
// Map size to icon size - matches web MUI sizes
|
|
12
|
+
// small: 12px, medium: 16px, large: 20px
|
|
13
|
+
const getIconSize = () => {
|
|
14
|
+
switch (size) {
|
|
15
|
+
case 'small':
|
|
16
|
+
return 12;
|
|
17
|
+
case 'medium':
|
|
18
|
+
return 16;
|
|
19
|
+
case 'large':
|
|
20
|
+
return 20;
|
|
21
|
+
default:
|
|
22
|
+
return 16;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const iconSize = getIconSize();
|
|
26
|
+
// Default icon color based on theme
|
|
27
|
+
const defaultIconColor = iconColor || theme.colors.text.primary;
|
|
28
|
+
// Get container color
|
|
29
|
+
const getContainerColor = () => {
|
|
30
|
+
if (containerColor)
|
|
31
|
+
return containerColor;
|
|
32
|
+
if (disabled)
|
|
33
|
+
return 'transparent';
|
|
34
|
+
return 'transparent';
|
|
35
|
+
};
|
|
36
|
+
// Get icon color
|
|
37
|
+
const getIconColor = () => {
|
|
38
|
+
if (iconColor)
|
|
39
|
+
return iconColor;
|
|
40
|
+
if (disabled)
|
|
41
|
+
return theme.colors.text.disabled;
|
|
42
|
+
return defaultIconColor;
|
|
43
|
+
};
|
|
44
|
+
const styles = StyleSheet.create({
|
|
45
|
+
button: {
|
|
46
|
+
borderRadius: theme.borderRadius.full,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
const iconButton = (_jsx(IconButton, { icon: icon, size: iconSize, iconColor: getIconColor(), containerColor: getContainerColor(), disabled: disabled, onPress: onPress, accessibilityLabel: accessibilityLabel, style: [styles.button, style], ...props }));
|
|
50
|
+
// Wrap with tooltip if tooltipOptions is provided
|
|
51
|
+
if (tooltipOptions) {
|
|
52
|
+
return (_jsx(Tooltip, { title: tooltipOptions.title, enterTouchDelay: tooltipOptions.enterDelay ?? 0, leaveTouchDelay: tooltipOptions.leaveDelay ?? 0, children: iconButton }));
|
|
53
|
+
}
|
|
54
|
+
return iconButton;
|
|
55
|
+
};
|
|
56
|
+
SIconButton.displayName = 'SIconButton';
|
|
57
|
+
export default SIconButton;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/s-select/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SSelect } from './s-select';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/** Base type constraint for select options - can be primitives or objects */
|
|
3
|
+
type BaseOption = string | number | Record<string, any>;
|
|
4
|
+
/**
|
|
5
|
+
* Props interface for SSelect component
|
|
6
|
+
* Synced with web SSelect from @solostylist/ui-kit
|
|
7
|
+
*/
|
|
8
|
+
export interface SSelectProps<T extends BaseOption = string> {
|
|
9
|
+
/** Array of options to display in the select dropdown */
|
|
10
|
+
options?: T[];
|
|
11
|
+
/** Key to use for option display text (required for object options) */
|
|
12
|
+
optionLabel?: T extends object ? keyof T : never;
|
|
13
|
+
/** Key to use for option values (required for object options) */
|
|
14
|
+
optionValue?: T extends object ? keyof T : never;
|
|
15
|
+
/** Placeholder text shown when no option is selected */
|
|
16
|
+
placeholder?: string;
|
|
17
|
+
/** Field label displayed above the select */
|
|
18
|
+
label?: string | React.ReactNode;
|
|
19
|
+
/** Error message to display below the field */
|
|
20
|
+
error?: string;
|
|
21
|
+
/** Whether the field is required (shows asterisk in label) */
|
|
22
|
+
required?: boolean;
|
|
23
|
+
/** Current value (controlled component) */
|
|
24
|
+
value?: T | T[] | null;
|
|
25
|
+
/** Callback when value changes */
|
|
26
|
+
onChange?: (value: T | T[] | null) => void;
|
|
27
|
+
/** Visual style variant */
|
|
28
|
+
variant?: 'outlined' | 'flat';
|
|
29
|
+
/** Enable search/filter functionality in the dropdown */
|
|
30
|
+
searchable?: boolean;
|
|
31
|
+
/** Placeholder text for the search input */
|
|
32
|
+
searchPlaceholder?: string;
|
|
33
|
+
/** Enable multiple selection */
|
|
34
|
+
multiple?: boolean;
|
|
35
|
+
/** Whether the field is disabled */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
/** Input size variant */
|
|
38
|
+
size?: 'small' | 'medium';
|
|
39
|
+
/** Help text shown below the input */
|
|
40
|
+
hint?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A flexible select component that handles both primitive and object options with form integration.
|
|
44
|
+
* Synced with web SSelect from @solostylist/ui-kit.
|
|
45
|
+
*/
|
|
46
|
+
export declare function SSelect<T extends BaseOption = string>({ options, optionLabel, optionValue, placeholder, label, error, required, value: controlledValue, onChange, variant, searchable, searchPlaceholder, multiple, disabled, size, hint, }: SSelectProps<T>): React.JSX.Element;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=s-select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s-select.d.ts","sourceRoot":"","sources":["../../src/s-select/s-select.tsx"],"names":[],"mappings":"AACA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAOzD,6EAA6E;AAC7E,KAAK,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,UAAU,GAAG,MAAM;IACzD,yDAAyD;IACzD,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;IACd,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC;IACjD,iEAAiE;IACjE,WAAW,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC;IACjD,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC;IACvB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,2BAA2B;IAC3B,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC9B,yDAAyD;IACzD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gCAAgC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yBAAyB;IACzB,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC1B,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,UAAU,GAAG,MAAM,EAAE,EACrD,OAAY,EACZ,WAA0D,EAC1D,WAAwD,EACxD,WAAyB,EACzB,KAAK,EACL,KAAK,EACL,QAAgB,EAChB,KAAK,EAAE,eAAe,EACtB,QAAQ,EACR,OAAoB,EACpB,UAAkB,EAClB,iBAA+B,EAC/B,QAAgB,EAChB,QAAgB,EAChB,IAAe,EACf,IAAI,GACL,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAsUrC"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Color from 'color';
|
|
3
|
+
import { useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Platform, Pressable, ScrollView, StyleSheet, View } from 'react-native';
|
|
5
|
+
import { Checkbox, Divider, Menu, Surface, TextInput } from 'react-native-paper';
|
|
6
|
+
import { SForm } from '../s-form';
|
|
7
|
+
import { SText } from '../s-text';
|
|
8
|
+
import { useSTheme } from '../theme';
|
|
9
|
+
/**
|
|
10
|
+
* A flexible select component that handles both primitive and object options with form integration.
|
|
11
|
+
* Synced with web SSelect from @solostylist/ui-kit.
|
|
12
|
+
*/
|
|
13
|
+
export function SSelect({ options = [], optionLabel = 'name', optionValue = 'id', placeholder = 'Select...', label, error, required = false, value: controlledValue, onChange, variant = 'outlined', searchable = false, searchPlaceholder = 'Search...', multiple = false, disabled = false, size = 'medium', hint, }) {
|
|
14
|
+
const { theme } = useSTheme();
|
|
15
|
+
// State management
|
|
16
|
+
const isControlled = controlledValue !== undefined;
|
|
17
|
+
const [internalValue, setInternalValue] = useState(multiple ? [] : null);
|
|
18
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
19
|
+
const [menuVisible, setMenuVisible] = useState(false);
|
|
20
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
21
|
+
const [hoveredIndex, setHoveredIndex] = useState(null);
|
|
22
|
+
const [anchorWidth, setAnchorWidth] = useState(undefined);
|
|
23
|
+
const anchorRef = useRef(null);
|
|
24
|
+
// Helper functions for option handling
|
|
25
|
+
const getOptionLabel = (item) => {
|
|
26
|
+
if (typeof item === 'string' || typeof item === 'number') {
|
|
27
|
+
return String(item);
|
|
28
|
+
}
|
|
29
|
+
return String(item[optionLabel]);
|
|
30
|
+
};
|
|
31
|
+
const getOptionValue = (item) => {
|
|
32
|
+
if (typeof item === 'string' || typeof item === 'number') {
|
|
33
|
+
return item;
|
|
34
|
+
}
|
|
35
|
+
return item[optionValue];
|
|
36
|
+
};
|
|
37
|
+
const isOptionDisabled = (item) => {
|
|
38
|
+
if (typeof item === 'object' && item !== null) {
|
|
39
|
+
return item.disabled ?? false;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
};
|
|
43
|
+
// Filtered options for search
|
|
44
|
+
const filteredOptions = useMemo(() => {
|
|
45
|
+
if (!searchable || !searchTerm) {
|
|
46
|
+
return options;
|
|
47
|
+
}
|
|
48
|
+
return options.filter((item) => {
|
|
49
|
+
const label = getOptionLabel(item).toLowerCase();
|
|
50
|
+
return label.includes(searchTerm.toLowerCase());
|
|
51
|
+
});
|
|
52
|
+
}, [options, searchTerm, searchable]);
|
|
53
|
+
// Get display value
|
|
54
|
+
const getDisplayValue = () => {
|
|
55
|
+
if (multiple) {
|
|
56
|
+
const values = value || [];
|
|
57
|
+
if (values.length === 0) {
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
return values
|
|
61
|
+
.map((val) => {
|
|
62
|
+
const option = options.find((item) => getOptionValue(item) === getOptionValue(val));
|
|
63
|
+
return option ? getOptionLabel(option) : '';
|
|
64
|
+
})
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.join(', ');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const singleValue = value;
|
|
70
|
+
if (singleValue === null || singleValue === undefined) {
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
const option = options.find((item) => getOptionValue(item) === getOptionValue(singleValue));
|
|
74
|
+
return option ? getOptionLabel(option) : '';
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
// Check if option is selected
|
|
78
|
+
const isOptionSelected = (item) => {
|
|
79
|
+
if (multiple) {
|
|
80
|
+
const values = value || [];
|
|
81
|
+
return values.some((val) => getOptionValue(val) === getOptionValue(item));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const singleValue = value;
|
|
85
|
+
if (!singleValue)
|
|
86
|
+
return false;
|
|
87
|
+
return getOptionValue(singleValue) === getOptionValue(item);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// Handle option selection
|
|
91
|
+
const handleOptionPress = (item) => {
|
|
92
|
+
if (isOptionDisabled(item))
|
|
93
|
+
return;
|
|
94
|
+
let newValue;
|
|
95
|
+
if (multiple) {
|
|
96
|
+
const currentValues = value || [];
|
|
97
|
+
const isSelected = isOptionSelected(item);
|
|
98
|
+
if (isSelected) {
|
|
99
|
+
newValue = currentValues.filter((val) => getOptionValue(val) !== getOptionValue(item));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
newValue = [...currentValues, item];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
newValue = item;
|
|
107
|
+
setMenuVisible(false);
|
|
108
|
+
setSearchTerm('');
|
|
109
|
+
}
|
|
110
|
+
if (onChange) {
|
|
111
|
+
onChange(newValue);
|
|
112
|
+
}
|
|
113
|
+
else if (!isControlled) {
|
|
114
|
+
setInternalValue(newValue);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const handleOpenMenu = () => {
|
|
118
|
+
if (!disabled) {
|
|
119
|
+
setMenuVisible(true);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const handleAnchorLayout = (event) => {
|
|
123
|
+
const { width } = event.nativeEvent.layout;
|
|
124
|
+
setAnchorWidth(width);
|
|
125
|
+
};
|
|
126
|
+
const handleCloseMenu = () => {
|
|
127
|
+
setMenuVisible(false);
|
|
128
|
+
setSearchTerm('');
|
|
129
|
+
setHoveredIndex(null);
|
|
130
|
+
};
|
|
131
|
+
const displayValue = getDisplayValue();
|
|
132
|
+
const styles = StyleSheet.create({
|
|
133
|
+
input: {
|
|
134
|
+
// Match web sizes: small = 36px, medium = 40px
|
|
135
|
+
...(size === 'small' ? { height: 36 } : { height: 40 }),
|
|
136
|
+
},
|
|
137
|
+
inputContent: {
|
|
138
|
+
// Match STextField styling: fontFamily and fontSize
|
|
139
|
+
fontFamily: theme.typography.fontFamily,
|
|
140
|
+
fontSize: size === 'small' ? theme.typography.fontSize.sm : theme.typography.fontSize.md,
|
|
141
|
+
},
|
|
142
|
+
menuContent: {
|
|
143
|
+
borderRadius: theme.borderRadius.md,
|
|
144
|
+
backgroundColor: theme.colors.background.paper,
|
|
145
|
+
paddingTop: theme.spacing.sm,
|
|
146
|
+
paddingBottom: theme.spacing.sm,
|
|
147
|
+
borderWidth: 1,
|
|
148
|
+
borderColor: theme.colors.divider,
|
|
149
|
+
borderStyle: 'solid',
|
|
150
|
+
boxShadow: 'none',
|
|
151
|
+
},
|
|
152
|
+
menuItem: {
|
|
153
|
+
paddingTop: theme.spacing.sm,
|
|
154
|
+
paddingBottom: theme.spacing.sm,
|
|
155
|
+
paddingHorizontal: theme.spacing.md,
|
|
156
|
+
minHeight: 'auto',
|
|
157
|
+
},
|
|
158
|
+
menuItemSelected: {
|
|
159
|
+
// Match web: solid blue background for selected items (matches MUI's visual appearance)
|
|
160
|
+
// Use primary color with opacity to create a visible blue background
|
|
161
|
+
backgroundColor: theme.dark
|
|
162
|
+
? Color(theme.colors.primary[500]).alpha(0.3).rgb().string()
|
|
163
|
+
: Color(theme.colors.primary[500]).alpha(0.1).rgb().string(),
|
|
164
|
+
},
|
|
165
|
+
searchContainer: {
|
|
166
|
+
padding: theme.spacing.sm,
|
|
167
|
+
backgroundColor: theme.colors.background.paper,
|
|
168
|
+
borderTopLeftRadius: theme.borderRadius.lg,
|
|
169
|
+
borderTopRightRadius: theme.borderRadius.lg,
|
|
170
|
+
},
|
|
171
|
+
searchInput: {
|
|
172
|
+
backgroundColor: theme.colors.background.default,
|
|
173
|
+
height: 36,
|
|
174
|
+
},
|
|
175
|
+
searchContent: {
|
|
176
|
+
// Match STextField styling: fontFamily and fontSize
|
|
177
|
+
fontFamily: theme.typography.fontFamily,
|
|
178
|
+
fontSize: theme.typography.fontSize.sm,
|
|
179
|
+
},
|
|
180
|
+
searchOutline: {
|
|
181
|
+
borderRadius: theme.borderRadius.md,
|
|
182
|
+
},
|
|
183
|
+
noResults: {
|
|
184
|
+
padding: theme.spacing.lg,
|
|
185
|
+
alignItems: 'center',
|
|
186
|
+
},
|
|
187
|
+
checkbox: {
|
|
188
|
+
marginRight: theme.spacing.sm,
|
|
189
|
+
},
|
|
190
|
+
scrollView: {
|
|
191
|
+
// Ensure ScrollView doesn't add extra padding that would break web alignment
|
|
192
|
+
flexGrow: 0,
|
|
193
|
+
},
|
|
194
|
+
menuItemHover: {
|
|
195
|
+
// Match web hover state: backgroundColor: theme.palette.action.hover
|
|
196
|
+
backgroundColor: theme.dark
|
|
197
|
+
? Color(theme.colors.secondary[600]).alpha(0.2).rgb().string()
|
|
198
|
+
: Color(theme.colors.secondary[300]).alpha(0.2).rgb().string(),
|
|
199
|
+
},
|
|
200
|
+
menuItemSelectedHover: {
|
|
201
|
+
// Combined style for selected + hovered items
|
|
202
|
+
// Darken the selected background on hover
|
|
203
|
+
backgroundColor: theme.dark
|
|
204
|
+
? Color(theme.colors.primary[500]).alpha(0.4).rgb().string()
|
|
205
|
+
: Color(theme.colors.primary[500]).alpha(0.15).rgb().string(),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
return (_jsx(SForm, { label: label, hint: hint, error: error, required: required, children: _jsxs(Menu, { visible: menuVisible, onDismiss: handleCloseMenu, anchorPosition: "bottom", contentStyle: [styles.menuContent, anchorWidth !== undefined && { width: anchorWidth }], anchor: _jsx(View, { ref: anchorRef, onLayout: handleAnchorLayout, children: _jsx(Pressable, { onPress: handleOpenMenu, disabled: disabled, children: _jsx(TextInput, { mode: variant, placeholder: placeholder, placeholderTextColor: theme.colors.divider, value: displayValue, error: !!error, disabled: disabled, editable: false, pointerEvents: "none", dense: size === 'small', right: _jsx(TextInput.Icon, { icon: menuVisible ? 'chevron-up' : 'chevron-down' }), style: styles.input, contentStyle: styles.inputContent,
|
|
209
|
+
// Match web focus styling: blue outline when menu is open (focused state)
|
|
210
|
+
// Use outlineColor to show blue border when menu is visible (simulating focus)
|
|
211
|
+
outlineColor: error ? theme.colors.error[500] : menuVisible ? theme.colors.primary[400] : theme.colors.divider, activeOutlineColor: theme.colors.primary[400] }) }) }), children: [searchable && (_jsxs(_Fragment, { children: [_jsx(Surface, { style: styles.searchContainer, elevation: 0, children: _jsx(TextInput, { mode: "outlined", placeholder: searchPlaceholder, placeholderTextColor: theme.colors.divider, value: searchTerm, onChangeText: setSearchTerm, dense: true, left: _jsx(TextInput.Icon, { icon: "magnify" }), style: styles.searchInput, contentStyle: styles.searchContent, outlineStyle: styles.searchOutline, autoFocus: true }) }), _jsx(Divider, {})] })), _jsx(ScrollView, { style: styles.scrollView, contentContainerStyle: { paddingHorizontal: 0 }, children: filteredOptions.length > 0 ? (filteredOptions.map((item, index) => {
|
|
212
|
+
const itemValue = getOptionValue(item);
|
|
213
|
+
const itemLabel = getOptionLabel(item);
|
|
214
|
+
const isSelected = isOptionSelected(item);
|
|
215
|
+
const isDisabled = isOptionDisabled(item);
|
|
216
|
+
const isHovered = hoveredIndex === index;
|
|
217
|
+
return (_jsxs(Pressable, { onPress: () => handleOptionPress(item), onPressIn: () => setHoveredIndex(index), onPressOut: () => setHoveredIndex(null), ...(Platform.OS === 'web' && {
|
|
218
|
+
onMouseEnter: () => setHoveredIndex(index),
|
|
219
|
+
onMouseLeave: () => setHoveredIndex(null),
|
|
220
|
+
}), disabled: isDisabled, style: ({ pressed }) => {
|
|
221
|
+
const isActive = isHovered || pressed;
|
|
222
|
+
return [
|
|
223
|
+
styles.menuItem,
|
|
224
|
+
// Apply selected style only when not active (hovered or pressed)
|
|
225
|
+
isSelected && !isActive && styles.menuItemSelected,
|
|
226
|
+
// Apply hover/press style - selected hover takes precedence
|
|
227
|
+
isActive && isSelected && styles.menuItemSelectedHover,
|
|
228
|
+
isActive && !isSelected && styles.menuItemHover,
|
|
229
|
+
];
|
|
230
|
+
}, children: [multiple && (_jsx(Checkbox.Android, { status: isSelected ? 'checked' : 'unchecked', disabled: isDisabled, style: styles.checkbox })), _jsx(SText, { variant: "body1", style: [
|
|
231
|
+
isSelected && {
|
|
232
|
+
color: theme.colors.text.primary,
|
|
233
|
+
fontWeight: theme.typography.fontWeight.medium,
|
|
234
|
+
},
|
|
235
|
+
], children: itemLabel })] }, `${itemValue}-${index}`));
|
|
236
|
+
})) : (_jsx(Surface, { style: styles.noResults, elevation: 0, children: _jsx(SText, { variant: "body1", color: "text.secondary", children: searchTerm ? 'No results found' : 'No options available' }) })) })] }) }));
|
|
237
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/s-text/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SText, default } from './s-text';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TextProps as RNTextProps, StyleProp, TextStyle } from 'react-native';
|
|
3
|
+
export interface STextProps extends Omit<RNTextProps, 'style'> {
|
|
4
|
+
/** Typography variant matching MUI's typography scale */
|
|
5
|
+
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'subtitle1' | 'subtitle2' | 'body1' | 'body2' | 'caption';
|
|
6
|
+
/** Text color - supports theme colors or custom color string */
|
|
7
|
+
color?: 'primary' | 'secondary' | 'text.primary' | 'text.secondary' | 'text.disabled' | 'error' | 'warning' | 'success' | 'info' | string;
|
|
8
|
+
/** Font weight override */
|
|
9
|
+
fontWeight?: '400' | '500' | '600' | '700';
|
|
10
|
+
/** Font size override (in pixels) */
|
|
11
|
+
fontSize?: number;
|
|
12
|
+
/** Text alignment */
|
|
13
|
+
align?: 'left' | 'center' | 'right' | 'justify';
|
|
14
|
+
/** Text transform */
|
|
15
|
+
textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase';
|
|
16
|
+
/** Line height multiplier */
|
|
17
|
+
lineHeight?: number;
|
|
18
|
+
/** Letter spacing */
|
|
19
|
+
letterSpacing?: number;
|
|
20
|
+
/** Additional custom styles */
|
|
21
|
+
style?: StyleProp<TextStyle>;
|
|
22
|
+
/** Child content */
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export declare const SText: {
|
|
26
|
+
({ variant, color, fontWeight, fontSize, align, textTransform, lineHeight, letterSpacing, style, children, ...props }: STextProps): React.JSX.Element;
|
|
27
|
+
displayName: string;
|
|
28
|
+
};
|
|
29
|
+
export default SText;
|
|
30
|
+
//# sourceMappingURL=s-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s-text.d.ts","sourceRoot":"","sources":["../../src/s-text/s-text.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9E,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;IAC5D,yDAAyD;IACzD,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;IAE9G,gEAAgE;IAChE,KAAK,CAAC,EACF,SAAS,GACT,WAAW,GACX,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,OAAO,GACP,SAAS,GACT,SAAS,GACT,MAAM,GACN,MAAM,CAAC;IAEX,2BAA2B;IAC3B,UAAU,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAE3C,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAEhD,qBAAqB;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,CAAC;IAElE,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,qBAAqB;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE7B,oBAAoB;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AA8BD,eAAO,MAAM,KAAK;2HAYf,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO;;CAoChC,CAAC;AAIF,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { Text } from 'react-native-paper';
|
|
4
|
+
import { useSTheme } from '../theme';
|
|
5
|
+
const getFontFamily = (weight) => {
|
|
6
|
+
const fontMap = {
|
|
7
|
+
'400': 'Outfit_400Regular',
|
|
8
|
+
'500': 'Outfit_500Medium',
|
|
9
|
+
'600': 'Outfit_600SemiBold',
|
|
10
|
+
'700': 'Outfit_700Bold',
|
|
11
|
+
};
|
|
12
|
+
return fontMap[weight] || 'Outfit_400Regular';
|
|
13
|
+
};
|
|
14
|
+
const resolveColor = (colorProp, theme) => {
|
|
15
|
+
if (!colorProp)
|
|
16
|
+
return theme.colors.text.primary;
|
|
17
|
+
const colorMap = {
|
|
18
|
+
primary: theme.colors.primary[500],
|
|
19
|
+
secondary: theme.colors.secondary[500],
|
|
20
|
+
'text.primary': theme.colors.text.primary,
|
|
21
|
+
'text.secondary': theme.colors.text.secondary,
|
|
22
|
+
'text.disabled': theme.colors.text.disabled,
|
|
23
|
+
error: theme.colors.error[500],
|
|
24
|
+
warning: theme.colors.warning[500],
|
|
25
|
+
success: theme.colors.success[500],
|
|
26
|
+
info: theme.colors.info[500],
|
|
27
|
+
};
|
|
28
|
+
return colorMap[colorProp] || colorProp;
|
|
29
|
+
};
|
|
30
|
+
export const SText = ({ variant = 'body1', color, fontWeight, fontSize, align = 'left', textTransform = 'none', lineHeight, letterSpacing, style, children, ...props }) => {
|
|
31
|
+
const { theme } = useSTheme();
|
|
32
|
+
const computedStyle = useMemo(() => {
|
|
33
|
+
const variantStyle = theme.typography.scale[variant];
|
|
34
|
+
const weight = fontWeight || variantStyle.fontWeight;
|
|
35
|
+
// Apply default color for caption variant (matches MUI theme behavior)
|
|
36
|
+
const resolvedColor = color !== undefined ? color : variant === 'caption' ? 'text.secondary' : undefined;
|
|
37
|
+
const baseStyle = {
|
|
38
|
+
fontSize: fontSize || variantStyle.fontSize,
|
|
39
|
+
fontWeight: weight,
|
|
40
|
+
fontFamily: getFontFamily(weight),
|
|
41
|
+
lineHeight: lineHeight
|
|
42
|
+
? (fontSize || variantStyle.fontSize) * lineHeight
|
|
43
|
+
: (fontSize || variantStyle.fontSize) * (variantStyle.lineHeight || 1.5),
|
|
44
|
+
color: resolveColor(resolvedColor, theme),
|
|
45
|
+
textAlign: align,
|
|
46
|
+
textTransform,
|
|
47
|
+
};
|
|
48
|
+
if (letterSpacing !== undefined) {
|
|
49
|
+
baseStyle.letterSpacing = letterSpacing;
|
|
50
|
+
}
|
|
51
|
+
else if (variantStyle.letterSpacing !== undefined) {
|
|
52
|
+
baseStyle.letterSpacing = variantStyle.letterSpacing;
|
|
53
|
+
}
|
|
54
|
+
return baseStyle;
|
|
55
|
+
}, [variant, color, fontWeight, fontSize, align, textTransform, lineHeight, letterSpacing, theme]);
|
|
56
|
+
return (_jsx(Text, { style: [computedStyle, style], ...props, children: children }));
|
|
57
|
+
};
|
|
58
|
+
SText.displayName = 'SText';
|
|
59
|
+
export default SText;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/s-text-field/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
|