@utilitywarehouse/hearth-react-native 0.31.0 → 0.32.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.
Files changed (123) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +13 -13
  3. package/CHANGELOG.md +71 -0
  4. package/build/components/Rating/Rating.d.ts +6 -0
  5. package/build/components/Rating/Rating.js +76 -0
  6. package/build/components/Rating/Rating.props.d.ts +18 -0
  7. package/build/components/Rating/Rating.props.js +1 -0
  8. package/build/components/Rating/RatingStarEmpty.d.ts +6 -0
  9. package/build/components/Rating/RatingStarEmpty.js +9 -0
  10. package/build/components/Rating/RatingStarFilled.d.ts +6 -0
  11. package/build/components/Rating/RatingStarFilled.js +9 -0
  12. package/build/components/Rating/index.d.ts +2 -0
  13. package/build/components/Rating/index.js +1 -0
  14. package/build/components/Roundel/Roundel.d.ts +6 -0
  15. package/build/components/Roundel/Roundel.js +40 -0
  16. package/build/components/Roundel/Roundel.props.d.ts +6 -0
  17. package/build/components/Roundel/Roundel.props.js +1 -0
  18. package/build/components/Roundel/index.d.ts +2 -0
  19. package/build/components/Roundel/index.js +1 -0
  20. package/build/components/StepperInput/StepperButton.d.ts +22 -0
  21. package/build/components/StepperInput/StepperButton.js +55 -0
  22. package/build/components/StepperInput/StepperInput.d.ts +6 -0
  23. package/build/components/StepperInput/StepperInput.js +196 -0
  24. package/build/components/StepperInput/StepperInput.props.d.ts +31 -0
  25. package/build/components/StepperInput/StepperInput.props.js +1 -0
  26. package/build/components/StepperInput/index.d.ts +2 -0
  27. package/build/components/StepperInput/index.js +1 -0
  28. package/build/components/Table/TableHeaderCell.js +10 -1
  29. package/build/components/Textarea/Textarea.d.ts +1 -1
  30. package/build/components/Textarea/Textarea.js +10 -3
  31. package/build/components/Textarea/Textarea.props.d.ts +11 -0
  32. package/build/components/index.d.ts +3 -0
  33. package/build/components/index.js +3 -0
  34. package/build/core/themes.d.ts +92 -88
  35. package/build/tokens/color.d.ts +82 -80
  36. package/build/tokens/color.js +41 -40
  37. package/build/tokens/components/dark/alert.d.ts +6 -6
  38. package/build/tokens/components/dark/alert.js +6 -6
  39. package/build/tokens/components/dark/bottom-navigation.d.ts +2 -2
  40. package/build/tokens/components/dark/bottom-navigation.js +2 -2
  41. package/build/tokens/components/dark/checkbox.d.ts +1 -1
  42. package/build/tokens/components/dark/checkbox.js +1 -1
  43. package/build/tokens/components/dark/icon-button.d.ts +3 -3
  44. package/build/tokens/components/dark/icon-button.js +3 -3
  45. package/build/tokens/components/dark/inline-link.d.ts +1 -1
  46. package/build/tokens/components/dark/inline-link.js +1 -1
  47. package/build/tokens/components/dark/link.d.ts +3 -3
  48. package/build/tokens/components/dark/link.js +3 -3
  49. package/build/tokens/components/dark/navigation.d.ts +2 -2
  50. package/build/tokens/components/dark/navigation.js +2 -2
  51. package/build/tokens/components/dark/parts.d.ts +2 -2
  52. package/build/tokens/components/dark/parts.js +2 -2
  53. package/build/tokens/components/dark/progress-bar.d.ts +3 -3
  54. package/build/tokens/components/dark/progress-bar.js +3 -3
  55. package/build/tokens/components/dark/progress-stepper.d.ts +1 -1
  56. package/build/tokens/components/dark/progress-stepper.js +1 -1
  57. package/build/tokens/components/dark/spinner.d.ts +1 -1
  58. package/build/tokens/components/dark/spinner.js +1 -1
  59. package/build/tokens/components/dark/table.d.ts +2 -0
  60. package/build/tokens/components/dark/table.js +2 -0
  61. package/build/tokens/components/dark/time-picker.d.ts +1 -0
  62. package/build/tokens/components/dark/time-picker.js +1 -0
  63. package/build/tokens/components/light/parts.d.ts +3 -3
  64. package/build/tokens/components/light/parts.js +3 -3
  65. package/build/tokens/components/light/table.d.ts +2 -0
  66. package/build/tokens/components/light/table.js +2 -0
  67. package/build/tokens/components/light/time-picker.d.ts +1 -0
  68. package/build/tokens/components/light/time-picker.js +1 -0
  69. package/build/tokens/semantic-dark.d.ts +40 -40
  70. package/build/tokens/semantic-dark.js +40 -40
  71. package/docs/adding-shadows.mdx +2 -2
  72. package/docs/changelog.mdx +165 -0
  73. package/docs/components/AllComponents.web.tsx +30 -1
  74. package/docs/dark-mode-best-practice.mdx +328 -0
  75. package/package.json +1 -1
  76. package/src/components/Modal/Modal.docs.mdx +58 -4
  77. package/src/components/NavModal/NavModal.docs.mdx +2 -2
  78. package/src/components/Rating/Rating.docs.mdx +178 -0
  79. package/src/components/Rating/Rating.figma.tsx +20 -0
  80. package/src/components/Rating/Rating.props.ts +22 -0
  81. package/src/components/Rating/Rating.stories.tsx +95 -0
  82. package/src/components/Rating/Rating.tsx +140 -0
  83. package/src/components/Rating/RatingStarEmpty.tsx +22 -0
  84. package/src/components/Rating/RatingStarFilled.tsx +27 -0
  85. package/src/components/Rating/index.ts +2 -0
  86. package/src/components/Roundel/Roundel.docs.mdx +48 -0
  87. package/src/components/Roundel/Roundel.figma.tsx +17 -0
  88. package/src/components/Roundel/Roundel.props.ts +8 -0
  89. package/src/components/Roundel/Roundel.stories.tsx +49 -0
  90. package/src/components/Roundel/Roundel.tsx +51 -0
  91. package/src/components/Roundel/index.ts +2 -0
  92. package/src/components/StepperInput/StepperButton.tsx +83 -0
  93. package/src/components/StepperInput/StepperInput.docs.mdx +121 -0
  94. package/src/components/StepperInput/StepperInput.figma.tsx +45 -0
  95. package/src/components/StepperInput/StepperInput.props.ts +39 -0
  96. package/src/components/StepperInput/StepperInput.stories.tsx +270 -0
  97. package/src/components/StepperInput/StepperInput.tsx +349 -0
  98. package/src/components/StepperInput/index.ts +2 -0
  99. package/src/components/Table/TableHeaderCell.tsx +10 -1
  100. package/src/components/Textarea/Textarea.docs.mdx +2 -0
  101. package/src/components/Textarea/Textarea.props.ts +11 -0
  102. package/src/components/Textarea/Textarea.stories.tsx +14 -0
  103. package/src/components/Textarea/Textarea.tsx +11 -2
  104. package/src/components/index.ts +3 -0
  105. package/src/tokens/color.ts +41 -40
  106. package/src/tokens/components/dark/alert.ts +6 -6
  107. package/src/tokens/components/dark/bottom-navigation.ts +2 -2
  108. package/src/tokens/components/dark/checkbox.ts +1 -1
  109. package/src/tokens/components/dark/icon-button.ts +3 -3
  110. package/src/tokens/components/dark/inline-link.ts +1 -1
  111. package/src/tokens/components/dark/link.ts +3 -3
  112. package/src/tokens/components/dark/navigation.ts +2 -2
  113. package/src/tokens/components/dark/parts.ts +2 -2
  114. package/src/tokens/components/dark/progress-bar.ts +3 -3
  115. package/src/tokens/components/dark/progress-stepper.ts +1 -1
  116. package/src/tokens/components/dark/spinner.ts +1 -1
  117. package/src/tokens/components/dark/table.ts +2 -0
  118. package/src/tokens/components/dark/time-picker.ts +1 -0
  119. package/src/tokens/components/light/parts.ts +3 -3
  120. package/src/tokens/components/light/table.ts +2 -0
  121. package/src/tokens/components/light/time-picker.ts +1 -0
  122. package/src/tokens/semantic-dark.ts +40 -40
  123. package/vercel.json +0 -21
@@ -0,0 +1,349 @@
1
+ import { AddSmallIcon, MinusSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
2
+ import { useEffect, useImperativeHandle, useRef, useState } from 'react';
3
+ import type { TextInput, TextInputFocusEvent } from 'react-native';
4
+ import { View } from 'react-native';
5
+ import { StyleSheet } from 'react-native-unistyles';
6
+ import { FormField } from '../FormField';
7
+ import { InputComponent, InputField } from '../Input/Input';
8
+ import StepperButton from './StepperButton';
9
+ import StepperInputProps from './StepperInput.props';
10
+
11
+ const normalizeValue = (value?: string | number) => {
12
+ if (value === undefined || value === null || value === '') {
13
+ return '';
14
+ }
15
+
16
+ return `${value}`;
17
+ };
18
+
19
+ const getDecimalPlaces = (value?: number | string) => {
20
+ if (value === undefined || value === null || value === '') {
21
+ return 0;
22
+ }
23
+
24
+ const normalizedValue = `${value}`;
25
+ const decimalPart = normalizedValue.split('.')[1];
26
+
27
+ return decimalPart ? decimalPart.length : 0;
28
+ };
29
+
30
+ const formatNumber = (value: number, precision: number) => {
31
+ if (precision <= 0) {
32
+ return `${Math.trunc(value)}`;
33
+ }
34
+
35
+ return value
36
+ .toFixed(precision)
37
+ .replace(/\.0+$/, '')
38
+ .replace(/(\.\d*?)0+$/, '$1');
39
+ };
40
+
41
+ const sanitizeValue = (value: string, allowNegative: boolean, allowDecimal: boolean) => {
42
+ const strippedValue = value.replace(
43
+ allowDecimal ? /[^\d,.-]/g : allowNegative ? /[^\d-]/g : /\D/g,
44
+ ''
45
+ );
46
+ const normalizedValue = allowDecimal ? strippedValue.replace(/,/g, '.') : strippedValue;
47
+
48
+ if (!allowNegative) {
49
+ const unsignedValue = normalizedValue.replace(/-/g, '');
50
+
51
+ if (!allowDecimal) {
52
+ return unsignedValue;
53
+ }
54
+
55
+ const [integerPart = '', ...decimalParts] = unsignedValue.split('.');
56
+ const decimalPart = decimalParts.join('');
57
+
58
+ return decimalParts.length > 0 ? `${integerPart}.${decimalPart}` : integerPart;
59
+ }
60
+
61
+ const hasLeadingMinus = normalizedValue.startsWith('-');
62
+ const unsignedValue = normalizedValue.replace(/-/g, '');
63
+
64
+ if (!allowDecimal) {
65
+ return `${hasLeadingMinus ? '-' : ''}${unsignedValue}`;
66
+ }
67
+
68
+ const [integerPart = '', ...decimalParts] = unsignedValue.split('.');
69
+ const decimalPart = decimalParts.join('');
70
+ const composedValue = decimalParts.length > 0 ? `${integerPart}.${decimalPart}` : integerPart;
71
+
72
+ return `${hasLeadingMinus ? '-' : ''}${composedValue}`;
73
+ };
74
+
75
+ const parseValue = (value: string) => {
76
+ if (!value || value === '-' || value === '.' || value === '-.') {
77
+ return null;
78
+ }
79
+
80
+ const parsedValue = Number(value);
81
+ return Number.isNaN(parsedValue) ? null : parsedValue;
82
+ };
83
+
84
+ const clampValue = (value: number, min?: number, max?: number) => {
85
+ let nextValue = value;
86
+
87
+ if (typeof min === 'number') {
88
+ nextValue = Math.max(min, nextValue);
89
+ }
90
+
91
+ if (typeof max === 'number') {
92
+ nextValue = Math.min(max, nextValue);
93
+ }
94
+
95
+ return nextValue;
96
+ };
97
+
98
+ const StepperInput = ({
99
+ value,
100
+ defaultValue,
101
+ onChangeText,
102
+ onChangeValue,
103
+ min,
104
+ max,
105
+ step = 1,
106
+ focusInputOnStepPress = false,
107
+ validationStatus = 'initial',
108
+ disabled = false,
109
+ readonly = false,
110
+ focused = false,
111
+ inBottomSheet = false,
112
+ required = true,
113
+ label,
114
+ labelVariant = 'body',
115
+ helperText,
116
+ helperIcon,
117
+ validText,
118
+ invalidText,
119
+ style,
120
+ decrementAccessibilityLabel = 'Decrease value',
121
+ incrementAccessibilityLabel = 'Increase value',
122
+ onFocus,
123
+ onBlur,
124
+ ref,
125
+ ...props
126
+ }: StepperInputProps) => {
127
+ const inputRef = useRef<TextInput>(null);
128
+ const isControlled = value !== undefined;
129
+ const [internalValue, setInternalValue] = useState(() => normalizeValue(defaultValue));
130
+ const [isInputFocused, setIsInputFocused] = useState(false);
131
+
132
+ const displayValue = isControlled ? normalizeValue(value) : internalValue;
133
+ const parsedValue = parseValue(displayValue);
134
+ const resolvedFocused = focused || isInputFocused;
135
+ const allowNegative = typeof min !== 'number' || min < 0 || (typeof max === 'number' && max < 0);
136
+ const decimalPrecision = Math.max(
137
+ getDecimalPlaces(value),
138
+ getDecimalPlaces(defaultValue),
139
+ getDecimalPlaces(min),
140
+ getDecimalPlaces(max),
141
+ getDecimalPlaces(step)
142
+ );
143
+ const allowDecimal = decimalPrecision > 0;
144
+ const keyboardType = allowNegative || allowDecimal ? 'numeric' : 'number-pad';
145
+ const inputMode = allowDecimal ? 'decimal' : 'numeric';
146
+
147
+ useImperativeHandle(ref, () => inputRef.current as TextInput, []);
148
+
149
+ useEffect(() => {
150
+ if (!isControlled && defaultValue !== undefined) {
151
+ setInternalValue(normalizeValue(defaultValue));
152
+ }
153
+ }, [defaultValue, isControlled]);
154
+
155
+ const updateValue = (nextValue: string) => {
156
+ if (!isControlled) {
157
+ setInternalValue(nextValue);
158
+ }
159
+
160
+ onChangeText?.(nextValue);
161
+
162
+ const nextParsedValue = parseValue(nextValue);
163
+ if (nextParsedValue !== null) {
164
+ onChangeValue?.(clampValue(nextParsedValue, min, max));
165
+ }
166
+ };
167
+
168
+ const handleChangeText = (nextText: string) => {
169
+ const sanitizedValue = sanitizeValue(nextText, allowNegative, allowDecimal);
170
+
171
+ if (
172
+ sanitizedValue === '' ||
173
+ sanitizedValue === '-' ||
174
+ sanitizedValue === '.' ||
175
+ sanitizedValue === '-.' ||
176
+ (allowDecimal && sanitizedValue.endsWith('.'))
177
+ ) {
178
+ updateValue(sanitizedValue);
179
+ return;
180
+ }
181
+
182
+ const nextParsedValue = parseValue(sanitizedValue);
183
+
184
+ if (nextParsedValue === null) {
185
+ updateValue(sanitizedValue);
186
+ return;
187
+ }
188
+
189
+ const clampedText = formatNumber(clampValue(nextParsedValue, min, max), decimalPrecision);
190
+ updateValue(clampedText);
191
+ };
192
+
193
+ const handleStepPress = (direction: 1 | -1) => {
194
+ const baseValue = parsedValue ?? (typeof min === 'number' ? min : 0);
195
+ const nextValue = clampValue(baseValue + direction * step, min, max);
196
+ const normalizedValue = formatNumber(nextValue, decimalPrecision);
197
+
198
+ updateValue(normalizedValue);
199
+ if (focusInputOnStepPress) {
200
+ inputRef.current?.focus();
201
+ }
202
+ };
203
+
204
+ const decrementDisabled =
205
+ disabled || readonly || (typeof min === 'number' && parsedValue !== null && parsedValue <= min);
206
+ const incrementDisabled =
207
+ disabled || readonly || (typeof max === 'number' && parsedValue !== null && parsedValue >= max);
208
+
209
+ const handleFocus = (event: TextInputFocusEvent) => {
210
+ setIsInputFocused(true);
211
+ onFocus?.(event);
212
+ };
213
+
214
+ const handleBlur = (event: TextInputFocusEvent) => {
215
+ setIsInputFocused(false);
216
+ onBlur?.(event);
217
+ };
218
+
219
+ const getAccessibilityLabel = () => {
220
+ let accessibilityLabel = '';
221
+
222
+ if (label) {
223
+ accessibilityLabel = accessibilityLabel + label;
224
+ }
225
+
226
+ if (required) {
227
+ accessibilityLabel = accessibilityLabel + ', required';
228
+ }
229
+
230
+ return accessibilityLabel || props.accessibilityLabel;
231
+ };
232
+
233
+ const getAccessibilityHint = () => {
234
+ let accessibilityHint = '';
235
+
236
+ if (helperText) {
237
+ accessibilityHint = accessibilityHint + helperText;
238
+ }
239
+
240
+ if (validationStatus !== 'initial') {
241
+ if (accessibilityHint.length > 0) {
242
+ accessibilityHint = accessibilityHint + ', ';
243
+ }
244
+
245
+ if (validationStatus === 'invalid' && invalidText) {
246
+ accessibilityHint = accessibilityHint + invalidText;
247
+ }
248
+
249
+ if (validationStatus === 'valid' && validText) {
250
+ accessibilityHint = accessibilityHint + validText;
251
+ }
252
+ }
253
+
254
+ return accessibilityHint || props.accessibilityHint;
255
+ };
256
+
257
+ return (
258
+ <FormField
259
+ label={label}
260
+ labelVariant={labelVariant}
261
+ helperText={helperText}
262
+ helperIcon={helperIcon}
263
+ validText={validText}
264
+ invalidText={invalidText}
265
+ required={required}
266
+ validationStatus={validationStatus}
267
+ disabled={disabled}
268
+ readonly={readonly}
269
+ accessibilityHandledByChildren
270
+ style={[styles.root, style]}
271
+ >
272
+ <View style={styles.controls}>
273
+ <StepperButton
274
+ icon={MinusSmallIcon}
275
+ disabled={decrementDisabled}
276
+ accessibilityLabel={decrementAccessibilityLabel}
277
+ onPress={() => handleStepPress(-1)}
278
+ />
279
+ <InputComponent
280
+ validationStatus={validationStatus}
281
+ isInvalid={validationStatus === 'invalid'}
282
+ isReadOnly={readonly}
283
+ isDisabled={disabled}
284
+ isFocused={resolvedFocused}
285
+ isRequired={required}
286
+ style={styles.inputRoot}
287
+ >
288
+ <InputField
289
+ // @ts-expect-error - ref forwarding issue mirrors the base Input component
290
+ ref={inputRef}
291
+ inputMode={inputMode}
292
+ keyboardType={keyboardType}
293
+ inBottomSheet={inBottomSheet}
294
+ editable={!disabled && !readonly}
295
+ textAlign="center"
296
+ value={displayValue}
297
+ onFocus={handleFocus}
298
+ onBlur={handleBlur}
299
+ onChangeText={handleChangeText}
300
+ accessibilityLabel={getAccessibilityLabel()}
301
+ accessibilityHint={getAccessibilityHint()}
302
+ accessibilityState={{
303
+ ...(props.accessibilityState ?? {}),
304
+ disabled: disabled || readonly,
305
+ }}
306
+ aria-disabled={disabled || readonly}
307
+ aria-readonly={readonly}
308
+ aria-required={required}
309
+ aria-invalid={validationStatus === 'invalid'}
310
+ {...props}
311
+ style={styles.inputField}
312
+ />
313
+ </InputComponent>
314
+ <StepperButton
315
+ icon={AddSmallIcon}
316
+ disabled={incrementDisabled}
317
+ accessibilityLabel={incrementAccessibilityLabel}
318
+ onPress={() => handleStepPress(1)}
319
+ />
320
+ </View>
321
+ </FormField>
322
+ );
323
+ };
324
+
325
+ StepperInput.displayName = 'StepperInput';
326
+
327
+ const styles = StyleSheet.create(theme => ({
328
+ root: {
329
+ width: '100%',
330
+ maxWidth: theme.components.input.maxWidth,
331
+ },
332
+ controls: {
333
+ flexDirection: 'row',
334
+ alignItems: 'center',
335
+ gap: theme.components.input.stepper.gap,
336
+ },
337
+ inputRoot: {
338
+ width: 80,
339
+ minWidth: 80,
340
+ paddingHorizontal: 0,
341
+ justifyContent: 'center',
342
+ },
343
+ inputField: {
344
+ textAlign: 'center',
345
+ paddingHorizontal: 0,
346
+ },
347
+ }));
348
+
349
+ export default StepperInput;
@@ -0,0 +1,2 @@
1
+ export { default as StepperInput } from './StepperInput';
2
+ export type { StepperInputProps } from './StepperInput.props';
@@ -11,9 +11,10 @@ const renderContent = (
11
11
  weight: 'regular' | 'semibold' = 'semibold',
12
12
  color: 'purple' | 'white' = 'white'
13
13
  ) => {
14
+ styles.useVariants({ color });
14
15
  if (typeof children === 'string' || typeof children === 'number') {
15
16
  return (
16
- <BodyText size="md" weight={weight} style={styles.text} inverted={color === 'purple'}>
17
+ <BodyText size="md" weight={weight} style={styles.text}>
17
18
  {children}
18
19
  </BodyText>
19
20
  );
@@ -129,6 +130,14 @@ const styles = StyleSheet.create(theme => ({
129
130
  color: theme.color.text.primary,
130
131
  },
131
132
  },
133
+ color: {
134
+ purple: {
135
+ color: theme.components.table.headerCell.foregoundColorInverted,
136
+ },
137
+ white: {
138
+ color: theme.components.table.headerCell.foregoundColor,
139
+ },
140
+ },
132
141
  },
133
142
  },
134
143
  }));
@@ -72,6 +72,7 @@ all of the React Native [`View` props](https://reactnative.dev/docs/view).
72
72
  | validText | `string` | `-` | Text to display when validation status is 'valid'. **(Only to be used if the input has no children)** |
73
73
  | invalidText | `string` | `-` | Text to display when validation status is 'invalid'. |
74
74
  | required | `boolean` | `true` | Whether the input is required. **(Only to be used if the input has no children)** |
75
+ | defaultHeight | `number` | `96` | The initial height of the textarea in pixels when `resizable` is `true`. |
75
76
  | resizable | `boolean` | `false` | Adds a bottom-right drag handle so the textarea can be resized vertically. |
76
77
  | value | `string` | `-` | The value of the input. **(Only to be used if the input has no children)** |
77
78
  | onChange | `function` | `-` | Callback function that is triggered when the input value changes. **(Only to be used if the input has no children)** **(Only to be used if the input has no children)** |
@@ -157,6 +158,7 @@ const MyComponent = () => {
157
158
  helperText="Drag the corner handle to resize"
158
159
  placeholder="Enter your text here..."
159
160
  resizable
161
+ defaultHeight={140}
160
162
  />
161
163
  );
162
164
  };
@@ -1,6 +1,17 @@
1
1
  import type { TextInputProps, ViewProps } from 'react-native';
2
2
 
3
3
  export interface TextareaBaseProps {
4
+ /**
5
+ * Sets the initial height of a resizable textarea in pixels.
6
+ * Has no effect unless `resizable` is enabled.
7
+ *
8
+ * @type number
9
+ * @example
10
+ * ```tsx
11
+ * <Textarea resizable defaultHeight={140} />
12
+ * ```
13
+ */
14
+ defaultHeight?: number;
4
15
  /**
5
16
  * If true, the textarea can be resized vertically using a drag handle.
6
17
  *
@@ -66,6 +66,10 @@ const meta = {
66
66
  description: 'Enables a drag handle to resize the Textarea vertically',
67
67
  defaultValue: false,
68
68
  },
69
+ defaultHeight: {
70
+ control: { type: 'number', min: 64, step: 8 },
71
+ description: 'Sets the initial height of the Textarea in pixels',
72
+ },
69
73
  },
70
74
  args: {
71
75
  placeholder: 'Textarea placeholder',
@@ -90,3 +94,13 @@ export const Resizable: Story = {
90
94
  resizable: true,
91
95
  },
92
96
  };
97
+
98
+ export const DefaultHeight: Story = {
99
+ args: {
100
+ label: 'Notes',
101
+ helperText: 'Starts taller by default',
102
+ placeholder: 'Add more detail here...',
103
+ resizable: true,
104
+ defaultHeight: 140,
105
+ },
106
+ };
@@ -33,6 +33,7 @@ const Textarea = ({
33
33
  validationStatus = 'initial',
34
34
  children,
35
35
  resizable = false,
36
+ defaultHeight,
36
37
  disabled,
37
38
  focused,
38
39
  readonly,
@@ -56,8 +57,9 @@ const Textarea = ({
56
57
  const textareaDisabled = disabled ?? formFieldContext?.disabled;
57
58
  const textareaReadonly = readonly ?? formFieldContext?.readonly;
58
59
  const textareaValidationStatus = formFieldContext?.validationStatus ?? validationStatus;
59
- const textareaHeight = useSharedValue(DEFAULT_TEXTAREA_HEIGHT);
60
- const resizeStartHeight = useSharedValue(DEFAULT_TEXTAREA_HEIGHT);
60
+ const textareaDefaultHeight = defaultHeight ?? DEFAULT_TEXTAREA_HEIGHT;
61
+ const textareaHeight = useSharedValue(textareaDefaultHeight);
62
+ const resizeStartHeight = useSharedValue(textareaDefaultHeight);
61
63
  const theme = useTheme();
62
64
 
63
65
  useEffect(() => {
@@ -66,6 +68,13 @@ const Textarea = ({
66
68
  }
67
69
  }, [formFieldContext]);
68
70
 
71
+ useEffect(() => {
72
+ if (!hasMeasuredHeight.current) {
73
+ textareaHeight.value = textareaDefaultHeight;
74
+ resizeStartHeight.value = textareaDefaultHeight;
75
+ }
76
+ }, [resizeStartHeight, textareaDefaultHeight, textareaHeight]);
77
+
69
78
  const getAccessibilityLabel = () => {
70
79
  let accessibilityLabel = '';
71
80
  if (textareaLabel) {
@@ -48,11 +48,14 @@ export * from './ProgressBar';
48
48
  export * from './ProgressStepper';
49
49
  export * from './Radio';
50
50
  export * from './RadioCard';
51
+ export * from './Rating';
52
+ export * from './Roundel';
51
53
  export * from './SectionHeader';
52
54
  export * from './SegmentedControl';
53
55
  export * from './Select';
54
56
  export * from './Skeleton';
55
57
  export * from './Spinner';
58
+ export * from './StepperInput';
56
59
  export * from './Switch';
57
60
  export * from './Table';
58
61
  export * from './Tabs';
@@ -94,6 +94,7 @@ export const grey = {
94
94
  '700': '#4c4c4c',
95
95
  '800': '#3f3f3f',
96
96
  '900': '#3a3837',
97
+ '925': '#2f2d2d',
97
98
  '950': '#232323',
98
99
  '975': '#191919',
99
100
  '1000': '#101010',
@@ -480,25 +481,25 @@ export const light = {
480
481
 
481
482
  export const dark = {
482
483
  background: {
483
- brand: '#7a42c8',
484
+ brand: '#442484',
484
485
  loading: '#30302c',
485
486
  primary: '#191917',
486
- secondary: '#232323',
487
+ secondary: '#2f2d2d',
487
488
  },
488
489
  border: {
489
- strong: '#ebebeb',
490
- subtle: '#4c473d',
490
+ strong: '#888888',
491
+ subtle: '#5b5b5b',
491
492
  },
492
493
  feedback: {
493
494
  danger: {
494
- border: '#ff7964',
495
+ border: '#f4412a',
495
496
  foreground: {
496
- default: '#101010',
497
+ default: '#ffffff',
497
498
  subtle: '#ff7964',
498
499
  },
499
500
  surface: {
500
- default: '#ff634a',
501
- subtle: '#ffa89d',
501
+ default: '#de2612',
502
+ subtle: '#6b1f1a',
502
503
  },
503
504
  },
504
505
  functional: {
@@ -513,36 +514,36 @@ export const dark = {
513
514
  },
514
515
  },
515
516
  info: {
516
- border: '#6bb0ff',
517
+ border: '#2786f1',
517
518
  foreground: {
518
- default: '#101010',
519
+ default: '#ffffff',
519
520
  subtle: '#6bb0ff',
520
521
  },
521
522
  surface: {
522
- default: '#6bb0ff',
523
- subtle: '#bcddff',
523
+ default: '#1c6cd4',
524
+ subtle: '#0b3375',
524
525
  },
525
526
  },
526
527
  positive: {
527
- border: '#58ca93',
528
+ border: '#19a660',
528
529
  foreground: {
529
- default: '#101010',
530
+ default: '#ffffff',
530
531
  subtle: '#58ca93',
531
532
  },
532
533
  surface: {
533
- default: '#36bf7d',
534
- subtle: '#a2e2c3',
534
+ default: '#0f834a',
535
+ subtle: '#074b2a',
535
536
  },
536
537
  },
537
538
  warning: {
538
- border: '#ff9639',
539
+ border: '#f56e00',
539
540
  foreground: {
540
- default: '#101010',
541
+ default: '#ffffff',
541
542
  subtle: '#ff9639',
542
543
  },
543
544
  surface: {
544
- default: '#ff8010',
545
- subtle: '#ffcca8',
545
+ default: '#cf5d00',
546
+ subtle: '#893900',
546
547
  },
547
548
  },
548
549
  },
@@ -585,9 +586,9 @@ export const dark = {
585
586
  },
586
587
  surface: {
587
588
  strong: {
588
- active: '#c6b5e2',
589
- default: '#996cda',
590
- hover: '#af90de',
589
+ active: '#ddd5eb',
590
+ default: '#af90de',
591
+ hover: '#c6b5e2',
591
592
  },
592
593
  },
593
594
  },
@@ -670,52 +671,52 @@ export const dark = {
670
671
  },
671
672
  },
672
673
  shadow: {
673
- brand: '#7a42c8',
674
- broadband: '#506c21',
675
- cashback: '#8b2bc9',
676
- default: '#f7f7f7',
677
- energy: '#326e7a',
678
- insurance: '#9b4c0e',
679
- mobile: '#a7266d',
680
- pig: '#8f358f',
674
+ brand: '#442484',
675
+ broadband: '#4f6b20',
676
+ cashback: '#7429b5',
677
+ default: '#3f3f3f',
678
+ energy: '#2c6370',
679
+ insurance: '#7f4518',
680
+ mobile: '#8a3260',
681
+ pig: '#7a1f7e',
681
682
  },
682
683
  surface: {
683
684
  brand: {
684
- default: '#996cda',
685
+ default: '#af90de',
685
686
  strong: '#26164f',
686
687
  subtle: '#442484',
687
688
  },
688
689
  broadband: {
689
690
  default: '#506c21',
690
- subtle: '#35421c',
691
+ subtle: '#4f6b20',
691
692
  },
692
693
  cashback: {
693
694
  default: '#8b2bc9',
694
- subtle: '#522270',
695
+ subtle: '#7429b5',
695
696
  },
696
697
  energy: {
697
698
  default: '#326e7a',
698
- subtle: '#254348',
699
+ subtle: '#2c6370',
699
700
  },
700
701
  highlight: {
701
702
  default: '#ffb921',
702
- subtle: '#756230',
703
+ subtle: '#82692b',
703
704
  },
704
705
  insurance: {
705
706
  default: '#9b4c0e',
706
- subtle: '#5a3213',
707
+ subtle: '#7f4518',
707
708
  },
708
709
  mobile: {
709
710
  default: '#a7266d',
710
- subtle: '#601f42',
711
+ subtle: '#8a3260',
711
712
  },
712
713
  neutral: {
713
- strong: '#232323',
714
+ strong: '#2f2d2d',
714
715
  subtle: '#191917',
715
716
  },
716
717
  pig: {
717
718
  default: '#8f358f',
718
- subtle: '#5d2167',
719
+ subtle: '#7a1f7e',
719
720
  },
720
721
  },
721
722
  text: {
@@ -10,15 +10,15 @@ export default {
10
10
  gap: 8,
11
11
  iconButton: {
12
12
  unstyled: {
13
- foregroundColor: '#101010',
14
- foregroundColorActive: '#3f3f3f',
15
- foregroundColorHover: '#3a3837',
13
+ foregroundColor: '#ebebeb',
14
+ foregroundColorActive: '#b2afae',
15
+ foregroundColorHover: '#d3d3d3',
16
16
  },
17
17
  },
18
18
  link: {
19
- color: '#101010',
20
- colorActive: '#3f3f3f',
21
- colorHover: '#3a3837',
19
+ color: '#ebebeb',
20
+ colorActive: '#b2afae',
21
+ colorHover: '#d3d3d3',
22
22
  },
23
23
  padding: 14,
24
24
  } as const;