nova-design-model-testing 0.0.1 → 0.0.3
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/guidelines/components/action-sheet.md +30 -0
- package/guidelines/components/avatar.md +30 -0
- package/guidelines/components/badge.md +45 -0
- package/guidelines/components/bottom-navigation.md +30 -0
- package/guidelines/components/bottom-sheet.md +30 -0
- package/guidelines/components/button-group.md +56 -0
- package/guidelines/components/button.md +76 -0
- package/guidelines/components/card.md +30 -0
- package/guidelines/components/checkbox.md +30 -0
- package/guidelines/components/chips.md +30 -0
- package/guidelines/components/confirm-sheet.md +30 -0
- package/guidelines/components/date-picker.md +30 -0
- package/guidelines/components/date-wheel.md +30 -0
- package/guidelines/components/icon.md +29 -0
- package/guidelines/components/inline-notification.md +30 -0
- package/guidelines/components/list-item.md +30 -0
- package/guidelines/components/list.md +30 -0
- package/guidelines/components/loading-indicator.md +30 -0
- package/guidelines/components/menu.md +57 -0
- package/guidelines/components/passcode.md +35 -0
- package/guidelines/components/radio.md +62 -0
- package/guidelines/components/tabs.md +30 -0
- package/guidelines/components/text-field.md +68 -0
- package/guidelines/components/toast.md +30 -0
- package/guidelines/components/toggle.md +30 -0
- package/guidelines/components/top-navigation.md +30 -0
- package/guidelines/foundations/colors.md +43 -0
- package/guidelines/foundations/radius.md +16 -0
- package/guidelines/foundations/spacing.md +16 -0
- package/guidelines/guidelines.md +36 -0
- package/guidelines/overview-components.md +16 -0
- package/guidelines/overview-icons.md +18 -0
- package/guidelines/overview-setup.md +15 -0
- package/package.json +1 -1
- package/public/vite.svg +1 -0
- package/src/App.css +42 -0
- package/src/App.jsx +6328 -0
- package/src/assets/fonts/inter_bold.otf +0 -0
- package/src/assets/fonts/inter_italic.otf +0 -0
- package/src/assets/fonts/inter_medium.otf +0 -0
- package/src/assets/fonts/inter_regular.otf +0 -0
- package/src/assets/fonts/inter_semi_bold.otf +0 -0
- package/src/assets/fonts/space_grotesk_bold.otf +0 -0
- package/src/assets/fonts/space_grotesk_medium.otf +0 -0
- package/src/assets/fonts/space_grotesk_regular.otf +0 -0
- package/src/assets/fonts/space_grotesk_semi_bold.otf +0 -0
- package/src/assets/icons/Style=Line-1.svg +5 -0
- package/src/assets/icons/Style=Line-2.svg +5 -0
- package/src/assets/icons/Style=Line-3.svg +5 -0
- package/src/assets/icons/Style=Line.svg +5 -0
- package/src/assets/icons/Style=Solid.svg +7 -0
- package/src/assets/icons/line/Style=Line-1.svg +5 -0
- package/src/assets/icons/line/Style=Line-2.svg +5 -0
- package/src/assets/icons/line/Style=Line-3.svg +5 -0
- package/src/assets/icons/line/Style=Line.svg +5 -0
- package/src/assets/icons/line/arrow_circle_down.svg +5 -0
- package/src/assets/icons/line/arrow_circle_down_left_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_circle_down_right_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_circle_left_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_circle_right.svg +3 -0
- package/src/assets/icons/line/arrow_circle_right_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_circle_up.svg +5 -0
- package/src/assets/icons/line/arrow_circle_up_left_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_circle_up_right_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_down.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_down_left_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_down_left_vturn.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_down_right_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_left_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_up.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_up_left_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_narrow_up_right_ltr.svg +5 -0
- package/src/assets/icons/line/arrow_turn_down_right_ltr.svg +5 -0
- package/src/assets/icons/line/arrows_down.svg +5 -0
- package/src/assets/icons/line/arrows_left_ltr.svg +5 -0
- package/src/assets/icons/line/arrows_right_ltr.svg +5 -0
- package/src/assets/icons/line/arrows_up.svg +5 -0
- package/src/assets/icons/line/chevron-selector-vertical.svg +5 -0
- package/src/assets/icons/line/chevron_down.svg +5 -0
- package/src/assets/icons/line/chevron_down_double.svg +5 -0
- package/src/assets/icons/line/chevron_left_double_ltr.svg +5 -0
- package/src/assets/icons/line/chevron_left_ltr.svg +5 -0
- package/src/assets/icons/line/chevron_right_double_ltr.svg +5 -0
- package/src/assets/icons/line/chevron_right_ltr.svg +5 -0
- package/src/assets/icons/line/chevron_selector_vertical.svg +5 -0
- package/src/assets/icons/line/chevron_up.svg +5 -0
- package/src/assets/icons/line/chevron_up_double.svg +5 -0
- package/src/assets/icons/line/coin_swap.svg +3 -0
- package/src/assets/icons/line/credit_card_edit.svg +5 -0
- package/src/assets/icons/line/dev.svg +5 -0
- package/src/assets/icons/line/expand_01.svg +5 -0
- package/src/assets/icons/line/expand_02.svg +5 -0
- package/src/assets/icons/line/flip_backward_ltr.svg +5 -0
- package/src/assets/icons/line/flip_forward_ltr.svg +5 -0
- package/src/assets/icons/line/home_05.svg +3 -0
- package/src/assets/icons/line/infinity.svg +5 -0
- package/src/assets/icons/line/minimise.svg +6 -0
- package/src/assets/icons/line/moon_01.svg +3 -0
- package/src/assets/icons/line/paint.svg +3 -0
- package/src/assets/icons/line/plus_circle.svg +3 -0
- package/src/assets/icons/line/refresh.svg +5 -0
- package/src/assets/icons/line/reset.svg +5 -0
- package/src/assets/icons/line/reverse_left_ltr.svg +5 -0
- package/src/assets/icons/line/reverse_right_ltr.svg +5 -0
- package/src/assets/icons/line/safe_01.svg +3 -0
- package/src/assets/icons/line/search.svg +3 -0
- package/src/assets/icons/line/shield_star.svg +3 -0
- package/src/assets/icons/line/sun.svg +3 -0
- package/src/assets/icons/line/switch_horizontal.svg +5 -0
- package/src/assets/icons/line/switch_vertical.svg +5 -0
- package/src/assets/icons/solid/arrow_circle_right.svg +3 -0
- package/src/assets/icons/solid/coin_swap.svg +6 -0
- package/src/assets/icons/solid/credit_card_edit.svg +6 -0
- package/src/assets/icons/solid/safe_01.svg +5 -0
- package/src/assets/images/New user_gold.png +0 -0
- package/src/assets/images/Sheet-1.png +0 -0
- package/src/assets/images/Sheet.png +0 -0
- package/src/assets/logos/botim_credit_dark.svg +22 -0
- package/src/assets/logos/botim_credit_light.svg +31 -0
- package/src/assets/logos/botim_invest_dark.svg +22 -0
- package/src/assets/logos/botim_invest_light.svg +31 -0
- package/src/assets/logos/botim_main_dark.svg +18 -0
- package/src/assets/logos/botim_main_light.svg +17 -0
- package/src/assets/logos/botim_money_dark.svg +26 -0
- package/src/assets/logos/botim_money_light.svg +28 -0
- package/src/assets/logos/botim_pay_dark.svg +24 -0
- package/src/assets/logos/botim_pay_light.svg +35 -0
- package/src/assets/react.svg +1 -0
- package/src/design-system/components/action-sheet/ActionSheet.jsx +3 -0
- package/src/design-system/components/avatar/Avatar.jsx +3 -0
- package/src/design-system/components/badge/Badge.jsx +68 -0
- package/src/design-system/components/bottom-navigation/BottomNavigation.jsx +3 -0
- package/src/design-system/components/bottom-sheet/BottomSheet.jsx +3 -0
- package/src/design-system/components/button/Button.jsx +310 -0
- package/src/design-system/components/button-group/ButtonGroup.jsx +150 -0
- package/src/design-system/components/card/Card.jsx +3 -0
- package/src/design-system/components/checkbox/Checkbox.jsx +3 -0
- package/src/design-system/components/chips/Chips.jsx +3 -0
- package/src/design-system/components/confirm-sheet/ConfirmSheet.jsx +3 -0
- package/src/design-system/components/date-picker/DatePicker.jsx +3 -0
- package/src/design-system/components/date-wheel/DateWheel.jsx +3 -0
- package/src/design-system/components/digit-field/DigitField.jsx +191 -0
- package/src/design-system/components/icon/Icon.jsx +154 -0
- package/src/design-system/components/inline-notification/InlineNotification.jsx +3 -0
- package/src/design-system/components/list/List.jsx +3 -0
- package/src/design-system/components/list-item/ListItem.jsx +3 -0
- package/src/design-system/components/loading-indicator/LoadingIndicator.jsx +3 -0
- package/src/design-system/components/logo/AppLogo.jsx +35 -0
- package/src/design-system/components/logo/BrandingLogo.jsx +39 -0
- package/src/design-system/components/logo/Logo.jsx +39 -0
- package/src/design-system/components/menu/Menu.jsx +100 -0
- package/src/design-system/components/menu/MenuItem.jsx +184 -0
- package/src/design-system/components/passcode/Passcode.jsx +165 -0
- package/src/design-system/components/radio/Radio.jsx +135 -0
- package/src/design-system/components/section-header-footer/SectionHeaderFooter.jsx +7 -0
- package/src/design-system/components/tabs/Tabs.jsx +3 -0
- package/src/design-system/components/text-field/TextField.jsx +308 -0
- package/src/design-system/components/toast/Toast.jsx +3 -0
- package/src/design-system/components/toggle/Toggle.jsx +3 -0
- package/src/design-system/components/top-navigation/TopNavigation.jsx +3 -0
- package/src/design-system/theme/ThemeContext.jsx +11 -0
- package/src/design-system/tokens/primitives/colors.json +122 -0
- package/src/design-system/tokens/primitives/radius.json +9 -0
- package/src/design-system/tokens/primitives/sizes.json +10 -0
- package/src/design-system/tokens/primitives/spacing.json +10 -0
- package/src/design-system/tokens/semantic/colors.dark.json +139 -0
- package/src/design-system/tokens/semantic/colors.light.json +139 -0
- package/src/design-system/tokens/semantic/spacing.json +18 -0
- package/src/index.css +99 -0
- package/src/main.jsx +10 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import primitiveColors from '../../tokens/primitives/colors.json'
|
|
2
|
+
import semanticLight from '../../tokens/semantic/colors.light.json'
|
|
3
|
+
import semanticDark from '../../tokens/semantic/colors.dark.json'
|
|
4
|
+
import sizeTokens from '../../tokens/primitives/sizes.json'
|
|
5
|
+
import { useTheme } from '../../theme/ThemeContext.jsx'
|
|
6
|
+
|
|
7
|
+
import arrowCircleRightLine from '../../../assets/icons/line/arrow_circle_right.svg?raw'
|
|
8
|
+
import chevronRightLine from '../../../assets/icons/line/chevron_right_ltr.svg?raw'
|
|
9
|
+
import coinSwapLine from '../../../assets/icons/line/coin_swap.svg?raw'
|
|
10
|
+
import creditCardEditLine from '../../../assets/icons/line/credit_card_edit.svg?raw'
|
|
11
|
+
import devLine from '../../../assets/icons/line/dev.svg?raw'
|
|
12
|
+
import expand01Line from '../../../assets/icons/line/expand_01.svg?raw'
|
|
13
|
+
import expand02Line from '../../../assets/icons/line/expand_02.svg?raw'
|
|
14
|
+
import home05Line from '../../../assets/icons/line/home_05.svg?raw'
|
|
15
|
+
import infinityLine from '../../../assets/icons/line/infinity.svg?raw'
|
|
16
|
+
import moon01Line from '../../../assets/icons/line/moon_01.svg?raw'
|
|
17
|
+
import paintLine from '../../../assets/icons/line/paint.svg?raw'
|
|
18
|
+
import plusCircleLine from '../../../assets/icons/line/plus_circle.svg?raw'
|
|
19
|
+
import safeLine from '../../../assets/icons/line/safe_01.svg?raw'
|
|
20
|
+
import searchLine from '../../../assets/icons/line/search.svg?raw'
|
|
21
|
+
import shieldStarLine from '../../../assets/icons/line/shield_star.svg?raw'
|
|
22
|
+
import sunLine from '../../../assets/icons/line/sun.svg?raw'
|
|
23
|
+
|
|
24
|
+
import arrowCircleRightSolid from '../../../assets/icons/solid/arrow_circle_right.svg?raw'
|
|
25
|
+
import coinSwapSolid from '../../../assets/icons/solid/coin_swap.svg?raw'
|
|
26
|
+
import creditCardEditSolid from '../../../assets/icons/solid/credit_card_edit.svg?raw'
|
|
27
|
+
import safeSolid from '../../../assets/icons/solid/safe_01.svg?raw'
|
|
28
|
+
|
|
29
|
+
const ICON_SET = {
|
|
30
|
+
line: {
|
|
31
|
+
arrow_circle_right: arrowCircleRightLine,
|
|
32
|
+
chevron_right_ltr: chevronRightLine,
|
|
33
|
+
coin_swap: coinSwapLine,
|
|
34
|
+
credit_card_edit: creditCardEditLine,
|
|
35
|
+
dev: devLine,
|
|
36
|
+
expand_01: expand01Line,
|
|
37
|
+
expand_02: expand02Line,
|
|
38
|
+
home_05: home05Line,
|
|
39
|
+
infinity: infinityLine,
|
|
40
|
+
moon_01: moon01Line,
|
|
41
|
+
paint: paintLine,
|
|
42
|
+
plus_circle: plusCircleLine,
|
|
43
|
+
safe_01: safeLine,
|
|
44
|
+
search: searchLine,
|
|
45
|
+
shield_star: shieldStarLine,
|
|
46
|
+
sun: sunLine,
|
|
47
|
+
},
|
|
48
|
+
solid: {
|
|
49
|
+
arrow_circle_right: arrowCircleRightSolid,
|
|
50
|
+
coin_swap: coinSwapSolid,
|
|
51
|
+
credit_card_edit: creditCardEditSolid,
|
|
52
|
+
safe_01: safeSolid,
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const SIZE_MAP = {
|
|
57
|
+
xxs: sizeTokens['size-xxs'],
|
|
58
|
+
xs: sizeTokens['size-xs'],
|
|
59
|
+
sm: sizeTokens['size-xs'],
|
|
60
|
+
md: sizeTokens['size-md'],
|
|
61
|
+
lg: sizeTokens['size-lg'],
|
|
62
|
+
xl: sizeTokens['size-xl'],
|
|
63
|
+
'2xl': sizeTokens['size-2xl'],
|
|
64
|
+
'3xl': sizeTokens['size-3xl'],
|
|
65
|
+
'4xl': sizeTokens['size-4xl'],
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveColorToken(color, themeTokens) {
|
|
69
|
+
if (!color || typeof color !== 'string') {
|
|
70
|
+
return getSemanticToken(themeTokens, 'text icon/neutral/primary')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (color === 'currentColor' || color.startsWith('#') || color.startsWith('rgb')) {
|
|
74
|
+
return color
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (color.includes('/')) {
|
|
78
|
+
return getSemanticToken(themeTokens, color)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return getSemanticToken(themeTokens, 'text icon/neutral/primary')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolvePrimitiveReference(reference) {
|
|
85
|
+
if (typeof reference !== 'string' || !reference.startsWith('primitives.')) {
|
|
86
|
+
return reference
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const path = reference.replace('primitives.', '').split('.')
|
|
90
|
+
let current = primitiveColors
|
|
91
|
+
|
|
92
|
+
for (const key of path) {
|
|
93
|
+
if (!current || typeof current !== 'object') {
|
|
94
|
+
return reference
|
|
95
|
+
}
|
|
96
|
+
current = current[key]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return current ?? reference
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getSemanticToken(themeTokens, token) {
|
|
103
|
+
const reference = themeTokens[token]
|
|
104
|
+
return resolvePrimitiveReference(reference)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default function Icon({
|
|
108
|
+
name = 'chevron_right_ltr',
|
|
109
|
+
variant = 'line',
|
|
110
|
+
size = 'xs',
|
|
111
|
+
color = 'text icon/neutral/primary',
|
|
112
|
+
className,
|
|
113
|
+
...rest
|
|
114
|
+
}) {
|
|
115
|
+
const theme = useTheme()
|
|
116
|
+
const themeTokens = theme === 'dark' ? semanticDark : semanticLight
|
|
117
|
+
const resolvedSize =
|
|
118
|
+
typeof size === 'number' ? size : SIZE_MAP[size] || sizeTokens['size-xs']
|
|
119
|
+
const resolvedColor = resolveColorToken(color, themeTokens)
|
|
120
|
+
const iconSvg = ICON_SET[variant]?.[name] || ICON_SET.line.chevron_right_ltr
|
|
121
|
+
|
|
122
|
+
const tintedSvg = iconSvg
|
|
123
|
+
.replace(/<svg[^>]*>/i, (match) => {
|
|
124
|
+
// Remove hardcoded width/height from svg tag, we set them on the container
|
|
125
|
+
return match
|
|
126
|
+
.replace(/width="[^"]*"/gi, 'width="100%"')
|
|
127
|
+
.replace(/height="[^"]*"/gi, 'height="100%"')
|
|
128
|
+
.replace(/fill="(?!none|transparent)[^"]*"/gi, 'fill="none"') // SVG tag should usually not have a fill
|
|
129
|
+
})
|
|
130
|
+
.replace(/fill="(?!none|transparent)[^"]*"/gi, 'fill="currentColor"')
|
|
131
|
+
.replace(/stroke="(?!none|transparent)[^"]*"/gi, 'stroke="currentColor"')
|
|
132
|
+
.replace(/style="[^"]*"/gi, '')
|
|
133
|
+
.replace(/<path(?![^>]*fill=)/gi, '<path fill="none"')
|
|
134
|
+
.replace(/<rect(?![^>]*fill=)/gi, '<rect fill="none"')
|
|
135
|
+
.replace(/<circle(?![^>]*fill=)/gi, '<circle fill="none"')
|
|
136
|
+
.replace(/<ellipse(?![^>]*fill=)/gi, '<ellipse fill="none"')
|
|
137
|
+
.replace(/<polygon(?![^>]*fill=)/gi, '<polygon fill="none"')
|
|
138
|
+
.replace(/<polyline(?![^>]*fill=)/gi, '<polyline fill="none"')
|
|
139
|
+
.replace(/id="[^"]*"/gi, '')
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<span
|
|
143
|
+
className={className}
|
|
144
|
+
style={{
|
|
145
|
+
width: resolvedSize,
|
|
146
|
+
height: resolvedSize,
|
|
147
|
+
color: resolvedColor,
|
|
148
|
+
display: 'inline-flex',
|
|
149
|
+
}}
|
|
150
|
+
dangerouslySetInnerHTML={{ __html: tintedSvg }}
|
|
151
|
+
{...rest}
|
|
152
|
+
/>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export const AppLogo = ({ size = 40, className = '' }) => {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
className={`overflow-hidden relative rounded-[22%] ${className}`}
|
|
7
|
+
style={{
|
|
8
|
+
width: `${size}px`,
|
|
9
|
+
height: `${size}px`,
|
|
10
|
+
backgroundImage: "linear-gradient(120.8deg, #000000 9.76%, #011FE5 90.24%)"
|
|
11
|
+
}}
|
|
12
|
+
>
|
|
13
|
+
<div className="absolute inset-0 bg-[#011FE5]" />
|
|
14
|
+
{/* SVG Icon implementation based on Figma paths */}
|
|
15
|
+
<svg
|
|
16
|
+
viewBox="0 0 240 240"
|
|
17
|
+
className="absolute inset-0 w-full h-full p-[24%]"
|
|
18
|
+
fill="none"
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
>
|
|
21
|
+
<path
|
|
22
|
+
d="M117.8 181.4C152.9 181.4 181.4 152.9 181.4 117.8C181.4 82.7 152.9 54.2 117.8 54.2C82.7 54.2 54.2 82.7 54.2 117.8C54.2 152.9 82.7 181.4 117.8 181.4Z"
|
|
23
|
+
fill="white"
|
|
24
|
+
/>
|
|
25
|
+
<path
|
|
26
|
+
d="M117.8 117.8L117.8 54.2C100.8 54.2 84.5 60.9 72.5 72.9C60.5 84.9 53.8 101.2 53.8 118.2L117.8 117.8Z"
|
|
27
|
+
fill="white"
|
|
28
|
+
/>
|
|
29
|
+
<circle cx="117.8" cy="117.8" r="15" fill="#011FE5" />
|
|
30
|
+
</svg>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default AppLogo;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTheme } from '../../theme/ThemeContext';
|
|
3
|
+
|
|
4
|
+
import botimMainLight from '../../../assets/logos/botim_main_light.svg';
|
|
5
|
+
import botimMainDark from '../../../assets/logos/botim_main_dark.svg';
|
|
6
|
+
import botimPayLight from '../../../assets/logos/botim_pay_light.svg';
|
|
7
|
+
import botimPayDark from '../../../assets/logos/botim_pay_dark.svg';
|
|
8
|
+
import botimMoneyLight from '../../../assets/logos/botim_money_light.svg';
|
|
9
|
+
import botimMoneyDark from '../../../assets/logos/botim_money_dark.svg';
|
|
10
|
+
import botimInvestLight from '../../../assets/logos/botim_invest_light.svg';
|
|
11
|
+
import botimInvestDark from '../../../assets/logos/botim_invest_dark.svg';
|
|
12
|
+
import botimCreditLight from '../../../assets/logos/botim_credit_light.svg';
|
|
13
|
+
import botimCreditDark from '../../../assets/logos/botim_credit_dark.svg';
|
|
14
|
+
|
|
15
|
+
const LOGO_MAP = {
|
|
16
|
+
'Botim Main': { light: botimMainLight, dark: botimMainDark },
|
|
17
|
+
'Botim Pay': { light: botimPayLight, dark: botimPayDark },
|
|
18
|
+
'Botim Money': { light: botimMoneyLight, dark: botimMoneyDark },
|
|
19
|
+
'Botim Invest': { light: botimInvestLight, dark: botimInvestDark },
|
|
20
|
+
'Botim Credit': { light: botimCreditLight, dark: botimCreditDark },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const BrandingLogo = ({ type = 'Botim Main', height = 24, className = '' }) => {
|
|
24
|
+
const theme = useTheme();
|
|
25
|
+
const isDark = theme === 'dark';
|
|
26
|
+
const logo = LOGO_MAP[type] || LOGO_MAP['Botim Main'];
|
|
27
|
+
const src = isDark ? logo.dark : logo.light;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<img
|
|
31
|
+
src={src}
|
|
32
|
+
alt={`${type} logo`}
|
|
33
|
+
style={{ height: `${height}px` }}
|
|
34
|
+
className={className}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default BrandingLogo;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTheme } from '../../theme/ThemeContext';
|
|
3
|
+
|
|
4
|
+
import botimMainLight from '../../../assets/logos/botim_main_light.svg';
|
|
5
|
+
import botimMainDark from '../../../assets/logos/botim_main_dark.svg';
|
|
6
|
+
import botimPayLight from '../../../assets/logos/botim_pay_light.svg';
|
|
7
|
+
import botimPayDark from '../../../assets/logos/botim_pay_dark.svg';
|
|
8
|
+
import botimMoneyLight from '../../../assets/logos/botim_money_light.svg';
|
|
9
|
+
import botimMoneyDark from '../../../assets/logos/botim_money_dark.svg';
|
|
10
|
+
import botimInvestLight from '../../../assets/logos/botim_invest_light.svg';
|
|
11
|
+
import botimInvestDark from '../../../assets/logos/botim_invest_dark.svg';
|
|
12
|
+
import botimCreditLight from '../../../assets/logos/botim_credit_light.svg';
|
|
13
|
+
import botimCreditDark from '../../../assets/logos/botim_credit_dark.svg';
|
|
14
|
+
|
|
15
|
+
const LOGO_MAP = {
|
|
16
|
+
main: { light: botimMainLight, dark: botimMainDark },
|
|
17
|
+
pay: { light: botimPayLight, dark: botimPayDark },
|
|
18
|
+
money: { light: botimMoneyLight, dark: botimMoneyDark },
|
|
19
|
+
invest: { light: botimInvestLight, dark: botimInvestDark },
|
|
20
|
+
credit: { light: botimCreditLight, dark: botimCreditDark },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Logo = ({ type = 'main', height = 24, className = '' }) => {
|
|
24
|
+
const theme = useTheme();
|
|
25
|
+
const isDark = theme === 'dark';
|
|
26
|
+
const logo = LOGO_MAP[type] || LOGO_MAP.main;
|
|
27
|
+
const src = isDark ? logo.dark : logo.light;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<img
|
|
31
|
+
src={src}
|
|
32
|
+
alt={`Botim ${type} logo`}
|
|
33
|
+
style={{ height: `${height}px` }}
|
|
34
|
+
className={className}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default Logo;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import primitiveColors from '../../tokens/primitives/colors.json'
|
|
2
|
+
import semanticLight from '../../tokens/semantic/colors.light.json'
|
|
3
|
+
import semanticDark from '../../tokens/semantic/colors.dark.json'
|
|
4
|
+
import { useTheme } from '../../theme/ThemeContext.jsx'
|
|
5
|
+
import MenuItem from './MenuItem.jsx'
|
|
6
|
+
|
|
7
|
+
function resolvePrimitiveReference(reference) {
|
|
8
|
+
if (typeof reference !== 'string' || !reference.startsWith('primitives.')) {
|
|
9
|
+
return reference
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const path = reference.replace('primitives.', '').split('.')
|
|
13
|
+
let current = primitiveColors
|
|
14
|
+
|
|
15
|
+
for (const key of path) {
|
|
16
|
+
if (!current || typeof current !== 'object') {
|
|
17
|
+
return reference
|
|
18
|
+
}
|
|
19
|
+
current = current[key]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return current ?? reference
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getSemanticToken(themeTokens, token) {
|
|
26
|
+
const reference = themeTokens[token]
|
|
27
|
+
return resolvePrimitiveReference(reference)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default function Menu({
|
|
31
|
+
items = [],
|
|
32
|
+
count,
|
|
33
|
+
itemProps = {},
|
|
34
|
+
style: customStyle = {},
|
|
35
|
+
...rest
|
|
36
|
+
}) {
|
|
37
|
+
const theme = useTheme()
|
|
38
|
+
const themeTokens = theme === 'dark' ? semanticDark : semanticLight
|
|
39
|
+
const background = getSemanticToken(themeTokens, 'background/neutral/action sheet')
|
|
40
|
+
const divider = getSemanticToken(themeTokens, 'background/neutral/secondary')
|
|
41
|
+
const shadowColor = theme === 'dark' ? 'rgba(5, 5, 5, 0.4)' : 'rgba(157, 173, 197, 0.5)'
|
|
42
|
+
const resolvedCount = count === undefined || count === null ? items.length : Number(count)
|
|
43
|
+
const normalizedCount = Number.isFinite(resolvedCount) ? resolvedCount : items.length
|
|
44
|
+
const safeCount = Math.max(0, normalizedCount)
|
|
45
|
+
const menuItems = items.slice(0, safeCount)
|
|
46
|
+
if (menuItems.length < safeCount) {
|
|
47
|
+
const placeholders = Array.from({ length: safeCount - menuItems.length }).map((_, index) => ({
|
|
48
|
+
id: `menu-placeholder-${index}`,
|
|
49
|
+
labelText: 'Label',
|
|
50
|
+
showTrailingIcon: true,
|
|
51
|
+
}))
|
|
52
|
+
menuItems.push(...placeholders)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div
|
|
57
|
+
style={{
|
|
58
|
+
width: 260,
|
|
59
|
+
display: 'flex',
|
|
60
|
+
flexDirection: 'column',
|
|
61
|
+
boxShadow: `0 8px 22px 0 ${shadowColor}`,
|
|
62
|
+
backgroundColor: background,
|
|
63
|
+
borderRadius: 12,
|
|
64
|
+
padding: 8,
|
|
65
|
+
overflow: 'hidden',
|
|
66
|
+
...customStyle,
|
|
67
|
+
}}
|
|
68
|
+
{...rest}
|
|
69
|
+
>
|
|
70
|
+
<div
|
|
71
|
+
style={{
|
|
72
|
+
display: 'flex',
|
|
73
|
+
flexDirection: 'column',
|
|
74
|
+
gap: 0,
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{menuItems.map((item, index) => (
|
|
78
|
+
<div key={item.id || `${item.labelText}-${index}`}>
|
|
79
|
+
<MenuItem {...itemProps} {...item} />
|
|
80
|
+
{index < menuItems.length - 1 ? (
|
|
81
|
+
<div
|
|
82
|
+
style={{
|
|
83
|
+
padding: '0 16px',
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<div
|
|
87
|
+
style={{
|
|
88
|
+
height: 1,
|
|
89
|
+
width: '100%',
|
|
90
|
+
backgroundColor: divider,
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
) : null}
|
|
95
|
+
</div>
|
|
96
|
+
))}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import primitiveColors from '../../tokens/primitives/colors.json'
|
|
3
|
+
import semanticLight from '../../tokens/semantic/colors.light.json'
|
|
4
|
+
import semanticDark from '../../tokens/semantic/colors.dark.json'
|
|
5
|
+
import { useTheme } from '../../theme/ThemeContext.jsx'
|
|
6
|
+
|
|
7
|
+
const STATE_ALIASES = {
|
|
8
|
+
Default: 'default',
|
|
9
|
+
Pressed: 'pressed',
|
|
10
|
+
Error: 'error',
|
|
11
|
+
Disabled: 'disabled',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function resolvePrimitiveReference(reference) {
|
|
15
|
+
if (typeof reference !== 'string' || !reference.startsWith('primitives.')) {
|
|
16
|
+
return reference
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const path = reference.replace('primitives.', '').split('.')
|
|
20
|
+
let current = primitiveColors
|
|
21
|
+
|
|
22
|
+
for (const key of path) {
|
|
23
|
+
if (!current || typeof current !== 'object') {
|
|
24
|
+
return reference
|
|
25
|
+
}
|
|
26
|
+
current = current[key]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return current ?? reference
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getSemanticToken(themeTokens, token) {
|
|
33
|
+
const reference = themeTokens[token]
|
|
34
|
+
return resolvePrimitiveReference(reference)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getMenuItemColors(themeTokens, state, pressedActive) {
|
|
38
|
+
if (state === 'disabled') {
|
|
39
|
+
return {
|
|
40
|
+
background: getSemanticToken(themeTokens, 'button/secondary/bg-disabled'),
|
|
41
|
+
text: getSemanticToken(themeTokens, 'text icon/neutral/disabled'),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (pressedActive) {
|
|
46
|
+
return {
|
|
47
|
+
background: getSemanticToken(themeTokens, 'background/neutral/secondary'),
|
|
48
|
+
text:
|
|
49
|
+
state === 'error'
|
|
50
|
+
? getSemanticToken(themeTokens, 'text icon/functional/danger')
|
|
51
|
+
: getSemanticToken(themeTokens, 'text icon/neutral/primary'),
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (state === 'error') {
|
|
56
|
+
return {
|
|
57
|
+
background: 'transparent',
|
|
58
|
+
text: getSemanticToken(themeTokens, 'text icon/functional/danger'),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
background: 'transparent',
|
|
64
|
+
text: getSemanticToken(themeTokens, 'text icon/neutral/primary'),
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function DotsIcon({ color }) {
|
|
69
|
+
return (
|
|
70
|
+
<svg aria-hidden="true" height="24" viewBox="0 0 24 24" width="24">
|
|
71
|
+
<rect
|
|
72
|
+
x="3"
|
|
73
|
+
y="3"
|
|
74
|
+
width="18"
|
|
75
|
+
height="18"
|
|
76
|
+
rx="4"
|
|
77
|
+
fill="none"
|
|
78
|
+
stroke={color}
|
|
79
|
+
strokeWidth="2"
|
|
80
|
+
/>
|
|
81
|
+
<circle cx="9" cy="12" r="1" fill={color} />
|
|
82
|
+
<circle cx="12" cy="12" r="1" fill={color} />
|
|
83
|
+
<circle cx="15" cy="12" r="1" fill={color} />
|
|
84
|
+
</svg>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default function MenuItem({
|
|
89
|
+
labelText = 'Label',
|
|
90
|
+
showTrailingIcon = true,
|
|
91
|
+
trailingIcon = null,
|
|
92
|
+
count = '',
|
|
93
|
+
state = 'default',
|
|
94
|
+
pressed = false,
|
|
95
|
+
enableInteraction = true,
|
|
96
|
+
...rest
|
|
97
|
+
}) {
|
|
98
|
+
const [isPressed, setIsPressed] = useState(false)
|
|
99
|
+
const theme = useTheme()
|
|
100
|
+
const themeTokens = theme === 'dark' ? semanticDark : semanticLight
|
|
101
|
+
const normalizedState = STATE_ALIASES[state] || state
|
|
102
|
+
const allowPress = enableInteraction && normalizedState !== 'disabled'
|
|
103
|
+
const pressedActive =
|
|
104
|
+
allowPress &&
|
|
105
|
+
(normalizedState === 'default' || normalizedState === 'error') &&
|
|
106
|
+
(pressed || isPressed)
|
|
107
|
+
const colors = getMenuItemColors(themeTokens, normalizedState, pressedActive)
|
|
108
|
+
const resolvedTrailing =
|
|
109
|
+
trailingIcon || <DotsIcon color={colors.text} />
|
|
110
|
+
const hasCount = count !== undefined && count !== null && String(count).length > 0
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
onMouseDown={() => {
|
|
115
|
+
if (allowPress) {
|
|
116
|
+
setIsPressed(true)
|
|
117
|
+
}
|
|
118
|
+
}}
|
|
119
|
+
onMouseUp={() => setIsPressed(false)}
|
|
120
|
+
onMouseLeave={() => setIsPressed(false)}
|
|
121
|
+
onKeyDown={(event) => {
|
|
122
|
+
if (allowPress && (event.key === 'Enter' || event.key === ' ')) {
|
|
123
|
+
setIsPressed(true)
|
|
124
|
+
}
|
|
125
|
+
}}
|
|
126
|
+
onKeyUp={() => setIsPressed(false)}
|
|
127
|
+
role="button"
|
|
128
|
+
tabIndex={0}
|
|
129
|
+
style={{
|
|
130
|
+
width: '100%',
|
|
131
|
+
display: 'flex',
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
gap: 12,
|
|
134
|
+
padding: 16,
|
|
135
|
+
borderRadius: 16,
|
|
136
|
+
backgroundColor: colors.background,
|
|
137
|
+
cursor: normalizedState === 'disabled' ? 'not-allowed' : 'pointer',
|
|
138
|
+
}}
|
|
139
|
+
{...rest}
|
|
140
|
+
>
|
|
141
|
+
<span
|
|
142
|
+
style={{
|
|
143
|
+
flex: 1,
|
|
144
|
+
fontSize: 16,
|
|
145
|
+
fontWeight: 600,
|
|
146
|
+
lineHeight: 1,
|
|
147
|
+
color: colors.text,
|
|
148
|
+
whiteSpace: 'nowrap',
|
|
149
|
+
overflow: 'hidden',
|
|
150
|
+
textOverflow: 'ellipsis',
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
{labelText}
|
|
154
|
+
</span>
|
|
155
|
+
{hasCount ? (
|
|
156
|
+
<span
|
|
157
|
+
style={{
|
|
158
|
+
fontSize: 14,
|
|
159
|
+
fontWeight: 600,
|
|
160
|
+
lineHeight: 1,
|
|
161
|
+
color: colors.text,
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
{count}
|
|
165
|
+
</span>
|
|
166
|
+
) : null}
|
|
167
|
+
{showTrailingIcon ? (
|
|
168
|
+
<span
|
|
169
|
+
aria-hidden="true"
|
|
170
|
+
style={{
|
|
171
|
+
width: 24,
|
|
172
|
+
height: 24,
|
|
173
|
+
display: 'inline-flex',
|
|
174
|
+
alignItems: 'center',
|
|
175
|
+
justifyContent: 'center',
|
|
176
|
+
color: colors.text,
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{resolvedTrailing}
|
|
180
|
+
</span>
|
|
181
|
+
) : null}
|
|
182
|
+
</div>
|
|
183
|
+
)
|
|
184
|
+
}
|