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.
Files changed (170) hide show
  1. package/guidelines/components/action-sheet.md +30 -0
  2. package/guidelines/components/avatar.md +30 -0
  3. package/guidelines/components/badge.md +45 -0
  4. package/guidelines/components/bottom-navigation.md +30 -0
  5. package/guidelines/components/bottom-sheet.md +30 -0
  6. package/guidelines/components/button-group.md +56 -0
  7. package/guidelines/components/button.md +76 -0
  8. package/guidelines/components/card.md +30 -0
  9. package/guidelines/components/checkbox.md +30 -0
  10. package/guidelines/components/chips.md +30 -0
  11. package/guidelines/components/confirm-sheet.md +30 -0
  12. package/guidelines/components/date-picker.md +30 -0
  13. package/guidelines/components/date-wheel.md +30 -0
  14. package/guidelines/components/icon.md +29 -0
  15. package/guidelines/components/inline-notification.md +30 -0
  16. package/guidelines/components/list-item.md +30 -0
  17. package/guidelines/components/list.md +30 -0
  18. package/guidelines/components/loading-indicator.md +30 -0
  19. package/guidelines/components/menu.md +57 -0
  20. package/guidelines/components/passcode.md +35 -0
  21. package/guidelines/components/radio.md +62 -0
  22. package/guidelines/components/tabs.md +30 -0
  23. package/guidelines/components/text-field.md +68 -0
  24. package/guidelines/components/toast.md +30 -0
  25. package/guidelines/components/toggle.md +30 -0
  26. package/guidelines/components/top-navigation.md +30 -0
  27. package/guidelines/foundations/colors.md +43 -0
  28. package/guidelines/foundations/radius.md +16 -0
  29. package/guidelines/foundations/spacing.md +16 -0
  30. package/guidelines/guidelines.md +36 -0
  31. package/guidelines/overview-components.md +16 -0
  32. package/guidelines/overview-icons.md +18 -0
  33. package/guidelines/overview-setup.md +15 -0
  34. package/package.json +1 -1
  35. package/public/vite.svg +1 -0
  36. package/src/App.css +42 -0
  37. package/src/App.jsx +6328 -0
  38. package/src/assets/fonts/inter_bold.otf +0 -0
  39. package/src/assets/fonts/inter_italic.otf +0 -0
  40. package/src/assets/fonts/inter_medium.otf +0 -0
  41. package/src/assets/fonts/inter_regular.otf +0 -0
  42. package/src/assets/fonts/inter_semi_bold.otf +0 -0
  43. package/src/assets/fonts/space_grotesk_bold.otf +0 -0
  44. package/src/assets/fonts/space_grotesk_medium.otf +0 -0
  45. package/src/assets/fonts/space_grotesk_regular.otf +0 -0
  46. package/src/assets/fonts/space_grotesk_semi_bold.otf +0 -0
  47. package/src/assets/icons/Style=Line-1.svg +5 -0
  48. package/src/assets/icons/Style=Line-2.svg +5 -0
  49. package/src/assets/icons/Style=Line-3.svg +5 -0
  50. package/src/assets/icons/Style=Line.svg +5 -0
  51. package/src/assets/icons/Style=Solid.svg +7 -0
  52. package/src/assets/icons/line/Style=Line-1.svg +5 -0
  53. package/src/assets/icons/line/Style=Line-2.svg +5 -0
  54. package/src/assets/icons/line/Style=Line-3.svg +5 -0
  55. package/src/assets/icons/line/Style=Line.svg +5 -0
  56. package/src/assets/icons/line/arrow_circle_down.svg +5 -0
  57. package/src/assets/icons/line/arrow_circle_down_left_ltr.svg +5 -0
  58. package/src/assets/icons/line/arrow_circle_down_right_ltr.svg +5 -0
  59. package/src/assets/icons/line/arrow_circle_left_ltr.svg +5 -0
  60. package/src/assets/icons/line/arrow_circle_right.svg +3 -0
  61. package/src/assets/icons/line/arrow_circle_right_ltr.svg +5 -0
  62. package/src/assets/icons/line/arrow_circle_up.svg +5 -0
  63. package/src/assets/icons/line/arrow_circle_up_left_ltr.svg +5 -0
  64. package/src/assets/icons/line/arrow_circle_up_right_ltr.svg +5 -0
  65. package/src/assets/icons/line/arrow_narrow_down.svg +5 -0
  66. package/src/assets/icons/line/arrow_narrow_down_left_ltr.svg +5 -0
  67. package/src/assets/icons/line/arrow_narrow_down_left_vturn.svg +5 -0
  68. package/src/assets/icons/line/arrow_narrow_down_right_ltr.svg +5 -0
  69. package/src/assets/icons/line/arrow_narrow_left_ltr.svg +5 -0
  70. package/src/assets/icons/line/arrow_narrow_up.svg +5 -0
  71. package/src/assets/icons/line/arrow_narrow_up_left_ltr.svg +5 -0
  72. package/src/assets/icons/line/arrow_narrow_up_right_ltr.svg +5 -0
  73. package/src/assets/icons/line/arrow_turn_down_right_ltr.svg +5 -0
  74. package/src/assets/icons/line/arrows_down.svg +5 -0
  75. package/src/assets/icons/line/arrows_left_ltr.svg +5 -0
  76. package/src/assets/icons/line/arrows_right_ltr.svg +5 -0
  77. package/src/assets/icons/line/arrows_up.svg +5 -0
  78. package/src/assets/icons/line/chevron-selector-vertical.svg +5 -0
  79. package/src/assets/icons/line/chevron_down.svg +5 -0
  80. package/src/assets/icons/line/chevron_down_double.svg +5 -0
  81. package/src/assets/icons/line/chevron_left_double_ltr.svg +5 -0
  82. package/src/assets/icons/line/chevron_left_ltr.svg +5 -0
  83. package/src/assets/icons/line/chevron_right_double_ltr.svg +5 -0
  84. package/src/assets/icons/line/chevron_right_ltr.svg +5 -0
  85. package/src/assets/icons/line/chevron_selector_vertical.svg +5 -0
  86. package/src/assets/icons/line/chevron_up.svg +5 -0
  87. package/src/assets/icons/line/chevron_up_double.svg +5 -0
  88. package/src/assets/icons/line/coin_swap.svg +3 -0
  89. package/src/assets/icons/line/credit_card_edit.svg +5 -0
  90. package/src/assets/icons/line/dev.svg +5 -0
  91. package/src/assets/icons/line/expand_01.svg +5 -0
  92. package/src/assets/icons/line/expand_02.svg +5 -0
  93. package/src/assets/icons/line/flip_backward_ltr.svg +5 -0
  94. package/src/assets/icons/line/flip_forward_ltr.svg +5 -0
  95. package/src/assets/icons/line/home_05.svg +3 -0
  96. package/src/assets/icons/line/infinity.svg +5 -0
  97. package/src/assets/icons/line/minimise.svg +6 -0
  98. package/src/assets/icons/line/moon_01.svg +3 -0
  99. package/src/assets/icons/line/paint.svg +3 -0
  100. package/src/assets/icons/line/plus_circle.svg +3 -0
  101. package/src/assets/icons/line/refresh.svg +5 -0
  102. package/src/assets/icons/line/reset.svg +5 -0
  103. package/src/assets/icons/line/reverse_left_ltr.svg +5 -0
  104. package/src/assets/icons/line/reverse_right_ltr.svg +5 -0
  105. package/src/assets/icons/line/safe_01.svg +3 -0
  106. package/src/assets/icons/line/search.svg +3 -0
  107. package/src/assets/icons/line/shield_star.svg +3 -0
  108. package/src/assets/icons/line/sun.svg +3 -0
  109. package/src/assets/icons/line/switch_horizontal.svg +5 -0
  110. package/src/assets/icons/line/switch_vertical.svg +5 -0
  111. package/src/assets/icons/solid/arrow_circle_right.svg +3 -0
  112. package/src/assets/icons/solid/coin_swap.svg +6 -0
  113. package/src/assets/icons/solid/credit_card_edit.svg +6 -0
  114. package/src/assets/icons/solid/safe_01.svg +5 -0
  115. package/src/assets/images/New user_gold.png +0 -0
  116. package/src/assets/images/Sheet-1.png +0 -0
  117. package/src/assets/images/Sheet.png +0 -0
  118. package/src/assets/logos/botim_credit_dark.svg +22 -0
  119. package/src/assets/logos/botim_credit_light.svg +31 -0
  120. package/src/assets/logos/botim_invest_dark.svg +22 -0
  121. package/src/assets/logos/botim_invest_light.svg +31 -0
  122. package/src/assets/logos/botim_main_dark.svg +18 -0
  123. package/src/assets/logos/botim_main_light.svg +17 -0
  124. package/src/assets/logos/botim_money_dark.svg +26 -0
  125. package/src/assets/logos/botim_money_light.svg +28 -0
  126. package/src/assets/logos/botim_pay_dark.svg +24 -0
  127. package/src/assets/logos/botim_pay_light.svg +35 -0
  128. package/src/assets/react.svg +1 -0
  129. package/src/design-system/components/action-sheet/ActionSheet.jsx +3 -0
  130. package/src/design-system/components/avatar/Avatar.jsx +3 -0
  131. package/src/design-system/components/badge/Badge.jsx +68 -0
  132. package/src/design-system/components/bottom-navigation/BottomNavigation.jsx +3 -0
  133. package/src/design-system/components/bottom-sheet/BottomSheet.jsx +3 -0
  134. package/src/design-system/components/button/Button.jsx +310 -0
  135. package/src/design-system/components/button-group/ButtonGroup.jsx +150 -0
  136. package/src/design-system/components/card/Card.jsx +3 -0
  137. package/src/design-system/components/checkbox/Checkbox.jsx +3 -0
  138. package/src/design-system/components/chips/Chips.jsx +3 -0
  139. package/src/design-system/components/confirm-sheet/ConfirmSheet.jsx +3 -0
  140. package/src/design-system/components/date-picker/DatePicker.jsx +3 -0
  141. package/src/design-system/components/date-wheel/DateWheel.jsx +3 -0
  142. package/src/design-system/components/digit-field/DigitField.jsx +191 -0
  143. package/src/design-system/components/icon/Icon.jsx +154 -0
  144. package/src/design-system/components/inline-notification/InlineNotification.jsx +3 -0
  145. package/src/design-system/components/list/List.jsx +3 -0
  146. package/src/design-system/components/list-item/ListItem.jsx +3 -0
  147. package/src/design-system/components/loading-indicator/LoadingIndicator.jsx +3 -0
  148. package/src/design-system/components/logo/AppLogo.jsx +35 -0
  149. package/src/design-system/components/logo/BrandingLogo.jsx +39 -0
  150. package/src/design-system/components/logo/Logo.jsx +39 -0
  151. package/src/design-system/components/menu/Menu.jsx +100 -0
  152. package/src/design-system/components/menu/MenuItem.jsx +184 -0
  153. package/src/design-system/components/passcode/Passcode.jsx +165 -0
  154. package/src/design-system/components/radio/Radio.jsx +135 -0
  155. package/src/design-system/components/section-header-footer/SectionHeaderFooter.jsx +7 -0
  156. package/src/design-system/components/tabs/Tabs.jsx +3 -0
  157. package/src/design-system/components/text-field/TextField.jsx +308 -0
  158. package/src/design-system/components/toast/Toast.jsx +3 -0
  159. package/src/design-system/components/toggle/Toggle.jsx +3 -0
  160. package/src/design-system/components/top-navigation/TopNavigation.jsx +3 -0
  161. package/src/design-system/theme/ThemeContext.jsx +11 -0
  162. package/src/design-system/tokens/primitives/colors.json +122 -0
  163. package/src/design-system/tokens/primitives/radius.json +9 -0
  164. package/src/design-system/tokens/primitives/sizes.json +10 -0
  165. package/src/design-system/tokens/primitives/spacing.json +10 -0
  166. package/src/design-system/tokens/semantic/colors.dark.json +139 -0
  167. package/src/design-system/tokens/semantic/colors.light.json +139 -0
  168. package/src/design-system/tokens/semantic/spacing.json +18 -0
  169. package/src/index.css +99 -0
  170. package/src/main.jsx +10 -0
@@ -0,0 +1,165 @@
1
+ import { useEffect, useRef, 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
+ Enabled: 'enabled',
9
+ Error: 'error',
10
+ Disabled: 'disabled',
11
+ }
12
+
13
+ const TYPING_ALIASES = {
14
+ 'To type': 'to-type',
15
+ Typing: 'typing',
16
+ Typed: 'typed',
17
+ }
18
+
19
+ function resolvePrimitiveReference(reference) {
20
+ if (typeof reference !== 'string' || !reference.startsWith('primitives.')) {
21
+ return reference
22
+ }
23
+
24
+ const path = reference.replace('primitives.', '').split('.')
25
+ let current = primitiveColors
26
+
27
+ for (const key of path) {
28
+ if (!current || typeof current !== 'object') {
29
+ return reference
30
+ }
31
+ current = current[key]
32
+ }
33
+
34
+ return current ?? reference
35
+ }
36
+
37
+ function getSemanticToken(themeTokens, token) {
38
+ const reference = themeTokens[token]
39
+ return resolvePrimitiveReference(reference)
40
+ }
41
+
42
+ function getPasscodeColors(themeTokens, state) {
43
+ if (state === 'error') {
44
+ return {
45
+ idle: getSemanticToken(themeTokens, 'Input/error/bg'),
46
+ active: getSemanticToken(themeTokens, 'text icon/functional/danger'),
47
+ }
48
+ }
49
+
50
+ if (state === 'disabled') {
51
+ return {
52
+ idle: getSemanticToken(themeTokens, 'background/neutral/disabled'),
53
+ active: getSemanticToken(themeTokens, 'text icon/neutral/disabled'),
54
+ }
55
+ }
56
+
57
+ return {
58
+ idle: getSemanticToken(themeTokens, 'background/neutral/tertiary'),
59
+ active: getSemanticToken(themeTokens, 'text icon/functional/link'),
60
+ }
61
+ }
62
+
63
+ export default function Passcode({
64
+ length = 6,
65
+ value,
66
+ defaultValue = '',
67
+ onChange,
68
+ state = 'enabled',
69
+ typingState = 'to-type',
70
+ enableInteraction = true,
71
+ }) {
72
+ const theme = useTheme()
73
+ const themeTokens = theme === 'dark' ? semanticDark : semanticLight
74
+ const normalizedState = STATE_ALIASES[state] || state
75
+ const normalizedTyping = TYPING_ALIASES[typingState] || typingState
76
+ const isDisabled = normalizedState === 'disabled'
77
+ const isControlled = value !== undefined && value !== null
78
+ const [internalValue, setInternalValue] = useState(defaultValue)
79
+ const [isFocused, setIsFocused] = useState(false)
80
+ const inputRef = useRef(null)
81
+ const digitsValue = (isControlled ? value : internalValue) || ''
82
+ const sanitizedValue = String(digitsValue).replace(/\D/g, '').slice(0, length)
83
+ const colors = getPasscodeColors(themeTokens, normalizedState)
84
+ const activeColor = colors.active
85
+ const idleColor = colors.idle
86
+
87
+ useEffect(() => {
88
+ if (!isControlled) {
89
+ setInternalValue(sanitizedValue)
90
+ }
91
+ }, [sanitizedValue, isControlled])
92
+
93
+ const handleChange = (nextValue) => {
94
+ if (!enableInteraction || isDisabled) {
95
+ return
96
+ }
97
+ const next = String(nextValue).replace(/\D/g, '').slice(0, length)
98
+ if (!isControlled) {
99
+ setInternalValue(next)
100
+ }
101
+ if (onChange) {
102
+ onChange(next)
103
+ }
104
+ }
105
+
106
+ const showActive = enableInteraction && isFocused
107
+ const renderActiveDots = normalizedTyping !== 'to-type' || showActive
108
+
109
+ return (
110
+ <div
111
+ onClick={() => {
112
+ if (!enableInteraction || isDisabled) {
113
+ return
114
+ }
115
+ if (inputRef.current) {
116
+ inputRef.current.focus()
117
+ }
118
+ }}
119
+ style={{
120
+ display: 'flex',
121
+ alignItems: 'center',
122
+ gap: 24,
123
+ }}
124
+ >
125
+ {Array.from({ length }).map((_, index) => {
126
+ const isFilled = renderActiveDots && index < sanitizedValue.length
127
+ return (
128
+ <span
129
+ key={`passcode-dot-${index}`}
130
+ style={{
131
+ width: 16,
132
+ height: 16,
133
+ borderRadius: 999,
134
+ backgroundColor: isFilled ? activeColor : idleColor,
135
+ display: 'inline-block',
136
+ }}
137
+ />
138
+ )
139
+ })}
140
+ <input
141
+ ref={inputRef}
142
+ inputMode="numeric"
143
+ autoComplete="one-time-code"
144
+ pattern="[0-9]*"
145
+ type="text"
146
+ value={sanitizedValue}
147
+ onChange={(event) => handleChange(event.target.value)}
148
+ onFocus={() => {
149
+ if (!enableInteraction || isDisabled) {
150
+ return
151
+ }
152
+ setIsFocused(true)
153
+ }}
154
+ onBlur={() => setIsFocused(false)}
155
+ style={{
156
+ position: 'absolute',
157
+ opacity: 0,
158
+ pointerEvents: 'none',
159
+ }}
160
+ aria-label="Passcode"
161
+ disabled={isDisabled}
162
+ />
163
+ </div>
164
+ )
165
+ }
@@ -0,0 +1,135 @@
1
+ import { useEffect, useMemo, 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
+ Enabled: 'enabled',
9
+ Disabled: 'disabled',
10
+ }
11
+
12
+ function resolvePrimitiveReference(reference) {
13
+ if (typeof reference !== 'string' || !reference.startsWith('primitives.')) {
14
+ return reference
15
+ }
16
+
17
+ const path = reference.replace('primitives.', '').split('.')
18
+ let current = primitiveColors
19
+
20
+ for (const key of path) {
21
+ if (!current || typeof current !== 'object') {
22
+ return reference
23
+ }
24
+ current = current[key]
25
+ }
26
+
27
+ return current ?? reference
28
+ }
29
+
30
+ function getSemanticToken(themeTokens, token) {
31
+ const reference = themeTokens[token]
32
+ return resolvePrimitiveReference(reference)
33
+ }
34
+
35
+ export default function Radio({
36
+ selected,
37
+ defaultSelected = false,
38
+ onChange,
39
+ state = 'enabled',
40
+ size = 24,
41
+ enableInteraction = true,
42
+ ariaLabel = 'Radio',
43
+ }) {
44
+ const theme = useTheme()
45
+ const themeTokens = theme === 'dark' ? semanticDark : semanticLight
46
+ const normalizedState = STATE_ALIASES[state] || state
47
+ const isDisabled = normalizedState === 'disabled'
48
+ const isControlled = selected !== undefined && selected !== null
49
+ const [internalSelected, setInternalSelected] = useState(defaultSelected)
50
+ const isSelected = isControlled ? selected : internalSelected
51
+
52
+ useEffect(() => {
53
+ if (!isControlled) {
54
+ setInternalSelected(defaultSelected)
55
+ }
56
+ }, [defaultSelected, isControlled])
57
+
58
+ const tokenSet = useMemo(() => {
59
+ if (isDisabled) {
60
+ return {
61
+ selected: 'radio/disabled/selected',
62
+ unselected: 'radio/disabled/unselected',
63
+ }
64
+ }
65
+ return {
66
+ selected: 'radio/active/selected',
67
+ unselected: 'radio/active/unselected',
68
+ }
69
+ }, [isDisabled])
70
+
71
+ const borderColor = getSemanticToken(
72
+ themeTokens,
73
+ isSelected ? tokenSet.selected : tokenSet.unselected,
74
+ )
75
+ const dotColor = getSemanticToken(themeTokens, tokenSet.selected)
76
+ const indicatorSize = Math.max(0, size - 4)
77
+ const dotSize = Math.round(indicatorSize * 0.5)
78
+
79
+ return (
80
+ <button
81
+ aria-checked={Boolean(isSelected)}
82
+ aria-disabled={isDisabled || !enableInteraction}
83
+ aria-label={ariaLabel}
84
+ onClick={() => {
85
+ if (!enableInteraction || isDisabled || isSelected) {
86
+ return
87
+ }
88
+ if (!isControlled) {
89
+ setInternalSelected(true)
90
+ }
91
+ if (onChange) {
92
+ onChange(true)
93
+ }
94
+ }}
95
+ role="radio"
96
+ style={{
97
+ width: size,
98
+ height: size,
99
+ display: 'inline-flex',
100
+ alignItems: 'center',
101
+ justifyContent: 'center',
102
+ background: 'transparent',
103
+ border: 'none',
104
+ padding: 0,
105
+ cursor: enableInteraction && !isDisabled ? 'pointer' : 'default',
106
+ }}
107
+ type="button"
108
+ >
109
+ <span
110
+ style={{
111
+ width: indicatorSize,
112
+ height: indicatorSize,
113
+ borderRadius: 999,
114
+ border: `2px solid ${borderColor}`,
115
+ display: 'inline-flex',
116
+ alignItems: 'center',
117
+ justifyContent: 'center',
118
+ boxSizing: 'border-box',
119
+ }}
120
+ >
121
+ {isSelected && (
122
+ <span
123
+ style={{
124
+ width: dotSize,
125
+ height: dotSize,
126
+ borderRadius: 999,
127
+ backgroundColor: dotColor,
128
+ display: 'inline-block',
129
+ }}
130
+ />
131
+ )}
132
+ </span>
133
+ </button>
134
+ )
135
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+
3
+ export const SectionHeaderFooter = () => {
4
+ return null;
5
+ };
6
+
7
+ export default SectionHeaderFooter;
@@ -0,0 +1,3 @@
1
+ export const Tabs = () => {
2
+ return null;
3
+ };
@@ -0,0 +1,308 @@
1
+ import { useEffect, useRef, 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
+ import Icon from '../icon/Icon.jsx'
7
+
8
+ const STATE_ALIASES = {
9
+ Enabled: 'enabled',
10
+ Error: 'error',
11
+ Disabled: 'disabled',
12
+ }
13
+
14
+ const TYPING_ALIASES = {
15
+ 'To type': 'to-type',
16
+ Typing: 'typing',
17
+ Typed: 'typed',
18
+ }
19
+
20
+ function resolvePrimitiveReference(reference) {
21
+ if (typeof reference !== 'string' || !reference.startsWith('primitives.')) {
22
+ return reference
23
+ }
24
+
25
+ const path = reference.replace('primitives.', '').split('.')
26
+ let current = primitiveColors
27
+
28
+ for (const key of path) {
29
+ if (!current || typeof current !== 'object') {
30
+ return reference
31
+ }
32
+ current = current[key]
33
+ }
34
+
35
+ return current ?? reference
36
+ }
37
+
38
+ function getSemanticToken(themeTokens, token) {
39
+ const reference = themeTokens[token]
40
+ return resolvePrimitiveReference(reference)
41
+ }
42
+
43
+ function getBaseColors(themeTokens, state, typingState) {
44
+ if (state === 'disabled') {
45
+ return {
46
+ background: getSemanticToken(themeTokens, 'background/neutral/disabled'),
47
+ text: getSemanticToken(themeTokens, 'text icon/neutral/disabled'),
48
+ placeholder: getSemanticToken(themeTokens, 'text icon/neutral/disabled'),
49
+ supporting: getSemanticToken(themeTokens, 'text icon/neutral/disabled'),
50
+ }
51
+ }
52
+
53
+ if (state === 'error') {
54
+ return {
55
+ background: getSemanticToken(themeTokens, 'Input/error/bg'),
56
+ text: getSemanticToken(themeTokens, 'text icon/neutral/primary'),
57
+ placeholder: getSemanticToken(themeTokens, 'text icon/neutral/placeholder'),
58
+ supporting: getSemanticToken(themeTokens, 'text icon/functional/danger'),
59
+ }
60
+ }
61
+
62
+ return {
63
+ background:
64
+ typingState === 'typing'
65
+ ? getSemanticToken(themeTokens, 'background/neutral/tertiary')
66
+ : getSemanticToken(themeTokens, 'background/neutral/secondary'),
67
+ text: getSemanticToken(themeTokens, 'text icon/neutral/primary'),
68
+ placeholder: getSemanticToken(themeTokens, 'text icon/neutral/placeholder'),
69
+ supporting: getSemanticToken(themeTokens, 'text icon/neutral/secondary'),
70
+ }
71
+ }
72
+
73
+ export default function TextField({
74
+ labelText = 'Label',
75
+ showOptional = true,
76
+ showLabelArea = true,
77
+ supportingText = 'Supporting text',
78
+ showSupportingTextArea = true,
79
+ state = 'enabled',
80
+ typingState = 'to-type',
81
+ prefixText = 'Prefix',
82
+ hintText = 'Hint text',
83
+ valueText = 'Entered text',
84
+ showLeadingIcon = true,
85
+ leadingIcon = null,
86
+ showTrailingIcon = true,
87
+ trailingIcon = null,
88
+ showActionButton = true,
89
+ actionLabel = 'Button',
90
+ showActionTrailingIcon = true,
91
+ actionTrailingIcon = null,
92
+ showClearButton = true,
93
+ enableInteraction = true,
94
+ }) {
95
+ const theme = useTheme()
96
+ const themeTokens = theme === 'dark' ? semanticDark : semanticLight
97
+ const normalizedState = STATE_ALIASES[state] || state
98
+ const normalizedTyping = TYPING_ALIASES[typingState] || typingState
99
+ const isDisabled = normalizedState === 'disabled'
100
+ const [isFocused, setIsFocused] = useState(false)
101
+ const [currentTyping, setCurrentTyping] = useState(normalizedTyping)
102
+ const [inputValue, setInputValue] = useState(
103
+ normalizedTyping === 'typed' ? valueText || '' : '',
104
+ )
105
+ const inputRef = useRef(null)
106
+ const colors = getBaseColors(themeTokens, normalizedState, currentTyping)
107
+ const showClear = showClearButton && !isDisabled && isFocused && inputValue.length > 0
108
+
109
+ const resolvedLeading =
110
+ leadingIcon || <Icon name="chevron_right_ltr" variant="line" size={20} color={colors.text} />
111
+ const resolvedTrailing =
112
+ trailingIcon || <Icon name="chevron_right_ltr" variant="line" size={20} color={colors.text} />
113
+ const resolvedActionTrailing =
114
+ actionTrailingIcon || <Icon name="chevron_right_ltr" variant="line" size={20} color={colors.text} />
115
+
116
+ useEffect(() => {
117
+ if (!enableInteraction) {
118
+ setCurrentTyping(normalizedTyping)
119
+ setInputValue(normalizedTyping === 'typed' ? valueText || '' : '')
120
+ }
121
+ }, [enableInteraction, normalizedTyping, valueText])
122
+
123
+ return (
124
+ <div style={{ width: 353, display: 'flex', flexDirection: 'column', gap: 8 }}>
125
+ {showLabelArea ? (
126
+ <div style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%' }}>
127
+ <div style={{ display: 'flex', alignItems: 'center', gap: 4, flex: 1 }}>
128
+ <span
129
+ style={{
130
+ fontSize: 16,
131
+ fontWeight: 500,
132
+ color:
133
+ normalizedState === 'disabled'
134
+ ? getSemanticToken(themeTokens, 'text icon/neutral/disabled')
135
+ : getSemanticToken(themeTokens, 'text icon/neutral/primary'),
136
+ }}
137
+ >
138
+ {labelText}
139
+ </span>
140
+ {showOptional ? (
141
+ <span
142
+ style={{
143
+ fontSize: 16,
144
+ fontWeight: 400,
145
+ color:
146
+ normalizedState === 'disabled'
147
+ ? getSemanticToken(themeTokens, 'text icon/neutral/disabled')
148
+ : getSemanticToken(themeTokens, 'text icon/neutral/secondary'),
149
+ }}
150
+ >
151
+ (optional)
152
+ </span>
153
+ ) : null}
154
+ </div>
155
+ </div>
156
+ ) : null}
157
+
158
+ <div
159
+ style={{
160
+ display: 'flex',
161
+ alignItems: 'center',
162
+ gap: 8,
163
+ minHeight: 48,
164
+ padding: '12px 16px',
165
+ borderRadius: 24,
166
+ backgroundColor: colors.background,
167
+ overflow: 'hidden',
168
+ }}
169
+ >
170
+ {showLeadingIcon ? (
171
+ <span style={{ width: 20, height: 20, display: 'inline-flex' }}>{resolvedLeading}</span>
172
+ ) : null}
173
+ {showActionButton ? (
174
+ <div
175
+ style={{
176
+ display: 'inline-flex',
177
+ alignItems: 'center',
178
+ gap: 4,
179
+ borderRadius: 32,
180
+ opacity: isDisabled ? 0.2 : 1,
181
+ }}
182
+ >
183
+ <span
184
+ style={{
185
+ fontSize: 14,
186
+ fontWeight: 600,
187
+ color: isDisabled ? colors.supporting : colors.text,
188
+ }}
189
+ >
190
+ {actionLabel}
191
+ </span>
192
+ {showActionTrailingIcon ? (
193
+ <span style={{ width: 20, height: 20, display: 'inline-flex' }}>
194
+ {resolvedActionTrailing}
195
+ </span>
196
+ ) : null}
197
+ </div>
198
+ ) : null}
199
+ <div
200
+ style={{
201
+ flex: 1,
202
+ minWidth: 0,
203
+ display: 'flex',
204
+ alignItems: 'center',
205
+ gap: 8,
206
+ paddingRight: 4,
207
+ }}
208
+ >
209
+ <span
210
+ style={{
211
+ fontSize: 14,
212
+ fontWeight: 400,
213
+ color: isDisabled ? colors.placeholder : colors.text,
214
+ }}
215
+ >
216
+ {prefixText}
217
+ </span>
218
+ <input
219
+ ref={inputRef}
220
+ disabled={isDisabled}
221
+ onBlur={() => {
222
+ setIsFocused(false)
223
+ if (enableInteraction && !isDisabled) {
224
+ setCurrentTyping(inputValue.length > 0 ? 'typed' : 'to-type')
225
+ }
226
+ }}
227
+ onChange={(event) => {
228
+ if (!enableInteraction || isDisabled) {
229
+ return
230
+ }
231
+ setInputValue(event.target.value)
232
+ setCurrentTyping('typing')
233
+ }}
234
+ onFocus={() => {
235
+ setIsFocused(true)
236
+ if (enableInteraction && !isDisabled) {
237
+ setCurrentTyping('typing')
238
+ }
239
+ }}
240
+ placeholder={hintText}
241
+ style={{
242
+ flex: 1,
243
+ minWidth: 0,
244
+ border: 'none',
245
+ outline: 'none',
246
+ backgroundColor: 'transparent',
247
+ color: colors.text,
248
+ fontSize: 14,
249
+ fontWeight: 400,
250
+ lineHeight: 1.4,
251
+ }}
252
+ type="text"
253
+ value={inputValue}
254
+ />
255
+ {showClear ? (
256
+ <span
257
+ aria-hidden="true"
258
+ style={{
259
+ width: 20,
260
+ height: 20,
261
+ display: 'inline-flex',
262
+ alignItems: 'center',
263
+ justifyContent: 'center',
264
+ color: colors.text,
265
+ cursor: 'pointer',
266
+ }}
267
+ onMouseDown={(event) => {
268
+ event.preventDefault()
269
+ if (inputRef.current) {
270
+ inputRef.current.focus()
271
+ }
272
+ }}
273
+ onClick={() => {
274
+ if (!enableInteraction || isDisabled) {
275
+ return
276
+ }
277
+ setInputValue('')
278
+ setCurrentTyping('typing')
279
+ if (inputRef.current) {
280
+ inputRef.current.focus()
281
+ }
282
+ }}
283
+ >
284
+ ×
285
+ </span>
286
+ ) : null}
287
+ </div>
288
+ {showTrailingIcon ? (
289
+ <span style={{ width: 20, height: 20, display: 'inline-flex' }}>{resolvedTrailing}</span>
290
+ ) : null}
291
+ </div>
292
+
293
+ {showSupportingTextArea ? (
294
+ <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
295
+ <span
296
+ style={{
297
+ fontSize: 12,
298
+ fontWeight: 400,
299
+ color: colors.supporting,
300
+ }}
301
+ >
302
+ {supportingText}
303
+ </span>
304
+ </div>
305
+ ) : null}
306
+ </div>
307
+ )
308
+ }
@@ -0,0 +1,3 @@
1
+ export const Toast = () => {
2
+ return null;
3
+ };
@@ -0,0 +1,3 @@
1
+ export const Toggle = () => {
2
+ return null;
3
+ };
@@ -0,0 +1,3 @@
1
+ export const TopNavigation = () => {
2
+ return null;
3
+ };
@@ -0,0 +1,11 @@
1
+ import { createContext, useContext } from 'react'
2
+
3
+ const ThemeContext = createContext('light')
4
+
5
+ export function ThemeProvider({ value, children }) {
6
+ return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
7
+ }
8
+
9
+ export function useTheme() {
10
+ return useContext(ThemeContext)
11
+ }