fluent-styles 1.62.13 → 1.62.15
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/lib/commonjs/button/index.js +12 -4
- package/lib/commonjs/button/index.js.map +1 -1
- package/lib/commonjs/datePicker/index.js +7 -2
- package/lib/commonjs/datePicker/index.js.map +1 -1
- package/lib/commonjs/dropdown/index.js +49 -16
- package/lib/commonjs/dropdown/index.js.map +1 -1
- package/lib/commonjs/input/index.js +87 -44
- package/lib/commonjs/input/index.js.map +1 -1
- package/lib/commonjs/page/index.js +24 -3
- package/lib/commonjs/page/index.js.map +1 -1
- package/lib/commonjs/pressable/index.js +6 -0
- package/lib/commonjs/pressable/index.js.map +1 -1
- package/lib/commonjs/safeAreaView/index.js +10 -0
- package/lib/commonjs/safeAreaView/index.js.map +1 -1
- package/lib/commonjs/utiles/styled.js +12 -2
- package/lib/commonjs/utiles/styled.js.map +1 -1
- package/lib/module/button/index.js +12 -4
- package/lib/module/button/index.js.map +1 -1
- package/lib/module/datePicker/index.js +7 -2
- package/lib/module/datePicker/index.js.map +1 -1
- package/lib/module/dropdown/index.js +49 -16
- package/lib/module/dropdown/index.js.map +1 -1
- package/lib/module/input/index.js +87 -44
- package/lib/module/input/index.js.map +1 -1
- package/lib/module/page/index.js +25 -4
- package/lib/module/page/index.js.map +1 -1
- package/lib/module/pressable/index.js +7 -1
- package/lib/module/pressable/index.js.map +1 -1
- package/lib/module/safeAreaView/index.js +11 -1
- package/lib/module/safeAreaView/index.js.map +1 -1
- package/lib/module/utiles/styled.js +12 -2
- package/lib/module/utiles/styled.js.map +1 -1
- package/lib/typescript/badge/index.d.ts +1 -6
- package/lib/typescript/badge/index.d.ts.map +1 -1
- package/lib/typescript/button/index.d.ts +3 -8
- package/lib/typescript/button/index.d.ts.map +1 -1
- package/lib/typescript/card/index.d.ts +1 -6
- package/lib/typescript/card/index.d.ts.map +1 -1
- package/lib/typescript/checkBox/index.d.ts +1 -6
- package/lib/typescript/checkBox/index.d.ts.map +1 -1
- package/lib/typescript/datePicker/index.d.ts.map +1 -1
- package/lib/typescript/dialog/index.d.ts +1 -6
- package/lib/typescript/dialog/index.d.ts.map +1 -1
- package/lib/typescript/divider/index.d.ts +1 -6
- package/lib/typescript/divider/index.d.ts.map +1 -1
- package/lib/typescript/dropdown/index.d.ts +27 -2
- package/lib/typescript/dropdown/index.d.ts.map +1 -1
- package/lib/typescript/image/index.d.ts +1 -6
- package/lib/typescript/image/index.d.ts.map +1 -1
- package/lib/typescript/input/index.d.ts +21 -8
- package/lib/typescript/input/index.d.ts.map +1 -1
- package/lib/typescript/page/index.d.ts.map +1 -1
- package/lib/typescript/pressable/index.d.ts +1 -6
- package/lib/typescript/pressable/index.d.ts.map +1 -1
- package/lib/typescript/safeAreaProvider/index.d.ts +1 -6
- package/lib/typescript/safeAreaProvider/index.d.ts.map +1 -1
- package/lib/typescript/safeAreaView/index.d.ts +1 -6
- package/lib/typescript/safeAreaView/index.d.ts.map +1 -1
- package/lib/typescript/scrollView/index.d.ts +1 -6
- package/lib/typescript/scrollView/index.d.ts.map +1 -1
- package/lib/typescript/shape/cycle.d.ts +1 -6
- package/lib/typescript/shape/cycle.d.ts.map +1 -1
- package/lib/typescript/shape/index.d.ts +1 -6
- package/lib/typescript/shape/index.d.ts.map +1 -1
- package/lib/typescript/spacer/index.d.ts +1 -6
- package/lib/typescript/spacer/index.d.ts.map +1 -1
- package/lib/typescript/stack/index.d.ts +3 -18
- package/lib/typescript/stack/index.d.ts.map +1 -1
- package/lib/typescript/text/index.d.ts +1 -6
- package/lib/typescript/text/index.d.ts.map +1 -1
- package/lib/typescript/utiles/styled.d.ts +1 -6
- package/lib/typescript/utiles/styled.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/button/index.tsx +14 -2
- package/src/datePicker/index.tsx +5 -1
- package/src/dropdown/index.tsx +72 -11
- package/src/input/index.tsx +111 -48
- package/src/page/index.tsx +36 -3
- package/src/pressable/index.tsx +8 -2
- package/src/safeAreaView/index.tsx +11 -1
- package/src/utiles/styled.tsx +12 -3
- package/lib/commonjs/portal/portal.test.js +0 -154
- package/lib/commonjs/portal/portal.test.js.map +0 -1
- package/lib/module/portal/portal.test.js +0 -155
- package/lib/module/portal/portal.test.js.map +0 -1
- package/lib/typescript/portal/portal.test.d.ts +0 -11
- package/lib/typescript/portal/portal.test.d.ts.map +0 -1
- package/src/portal/portal.test.ts +0 -157
package/src/dropdown/index.tsx
CHANGED
|
@@ -81,6 +81,32 @@ interface DropdownBaseProps<TItem extends DropdownOptionItem>
|
|
|
81
81
|
emptyText?: string;
|
|
82
82
|
focusColor?: string;
|
|
83
83
|
groupBy?: (item: TItem) => string;
|
|
84
|
+
/**
|
|
85
|
+
* Resting-surface + option-panel colour overrides — lets a consumer theme
|
|
86
|
+
* the dropdown (e.g. for dark mode) without the trigger's own focus/error
|
|
87
|
+
* border logic needing to change. A nested `colors` object (rather than
|
|
88
|
+
* a flat `backgroundColor` prop) avoids colliding with the `ViewStyle`
|
|
89
|
+
* props this interface already extends, which style the outer wrapper,
|
|
90
|
+
* not the trigger box.
|
|
91
|
+
*/
|
|
92
|
+
colors?: StyledDropdownColors;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface StyledDropdownColors {
|
|
96
|
+
/** Trigger box resting background. @default '#fff' */
|
|
97
|
+
background?: string;
|
|
98
|
+
/** Trigger box resting border (overridden by focus/error). @default gray[200] */
|
|
99
|
+
border?: string;
|
|
100
|
+
/** Selected value text colour. @default gray[800] */
|
|
101
|
+
text?: string;
|
|
102
|
+
/** Open option-panel background. @default '#fff' */
|
|
103
|
+
panelBackground?: string;
|
|
104
|
+
/** Open option-panel border. @default gray[200] */
|
|
105
|
+
panelBorder?: string;
|
|
106
|
+
/** Option row label text colour. @default gray[800] */
|
|
107
|
+
panelText?: string;
|
|
108
|
+
/** Dividers / section-header strip background inside the panel. @default gray[100] */
|
|
109
|
+
panelDivider?: string;
|
|
84
110
|
}
|
|
85
111
|
|
|
86
112
|
export interface StyledDropdownProps<
|
|
@@ -221,11 +247,12 @@ const OptionRow: React.FC<{
|
|
|
221
247
|
multiSelect: boolean;
|
|
222
248
|
size: DropdownSize;
|
|
223
249
|
focusColor: string;
|
|
224
|
-
|
|
250
|
+
textColor: string;
|
|
251
|
+
}> = ({ item, selected, onPress, multiSelect, size, focusColor, textColor: baseTextColor }) => {
|
|
225
252
|
const sz = SZ[size];
|
|
226
253
|
const textColor = item.disabled
|
|
227
254
|
? theme.colors.gray[300]
|
|
228
|
-
:
|
|
255
|
+
: baseTextColor;
|
|
229
256
|
|
|
230
257
|
return (
|
|
231
258
|
<StyledButton
|
|
@@ -309,6 +336,7 @@ interface ListPanelProps {
|
|
|
309
336
|
allSelected?: boolean;
|
|
310
337
|
onSelectAll?: () => void;
|
|
311
338
|
onPanelLayout?: (height: number) => void;
|
|
339
|
+
colors: Required<StyledDropdownColors>;
|
|
312
340
|
}
|
|
313
341
|
|
|
314
342
|
type ListEntry = DropdownOptionItem | { _header: string };
|
|
@@ -333,6 +361,7 @@ const ListPanel: React.FC<ListPanelProps> = ({
|
|
|
333
361
|
allSelected,
|
|
334
362
|
onSelectAll,
|
|
335
363
|
onPanelLayout,
|
|
364
|
+
colors,
|
|
336
365
|
}) => {
|
|
337
366
|
const [query, setQuery] = useState("");
|
|
338
367
|
const sz = SZ[size];
|
|
@@ -376,7 +405,7 @@ const ListPanel: React.FC<ListPanelProps> = ({
|
|
|
376
405
|
paddingHorizontal={sz.px}
|
|
377
406
|
paddingTop={10}
|
|
378
407
|
paddingBottom={4}
|
|
379
|
-
backgroundColor={
|
|
408
|
+
backgroundColor={colors.panelDivider}
|
|
380
409
|
>
|
|
381
410
|
<StyledText
|
|
382
411
|
fontSize={11}
|
|
@@ -398,10 +427,11 @@ const ListPanel: React.FC<ListPanelProps> = ({
|
|
|
398
427
|
multiSelect={multiSelect}
|
|
399
428
|
size={size}
|
|
400
429
|
focusColor={focusColor}
|
|
430
|
+
textColor={colors.panelText}
|
|
401
431
|
/>
|
|
402
432
|
);
|
|
403
433
|
},
|
|
404
|
-
[selectedKeys, onSelect, multiSelect, size, focusColor, sz.px],
|
|
434
|
+
[selectedKeys, onSelect, multiSelect, size, focusColor, sz.px, colors.panelDivider, colors.panelText],
|
|
405
435
|
);
|
|
406
436
|
|
|
407
437
|
const keyExtractor = useCallback(
|
|
@@ -427,10 +457,10 @@ const ListPanel: React.FC<ListPanelProps> = ({
|
|
|
427
457
|
left={position.left}
|
|
428
458
|
width={position.width}
|
|
429
459
|
maxHeight={maxHeight}
|
|
430
|
-
backgroundColor={
|
|
460
|
+
backgroundColor={colors.panelBackground}
|
|
431
461
|
borderRadius={10}
|
|
432
462
|
borderWidth={1}
|
|
433
|
-
borderColor={
|
|
463
|
+
borderColor={colors.panelBorder}
|
|
434
464
|
shadowColor={theme.colors.gray[900]}
|
|
435
465
|
shadowOffset={{
|
|
436
466
|
width: 0,
|
|
@@ -545,7 +575,7 @@ const ListPanel: React.FC<ListPanelProps> = ({
|
|
|
545
575
|
renderItem={renderItem}
|
|
546
576
|
ItemSeparatorComponent={() => (
|
|
547
577
|
<StyledDivider
|
|
548
|
-
backgroundColor={
|
|
578
|
+
backgroundColor={colors.panelDivider}
|
|
549
579
|
height={1}
|
|
550
580
|
/>
|
|
551
581
|
)}
|
|
@@ -634,6 +664,7 @@ const Trigger: React.FC<{
|
|
|
634
664
|
hasError: boolean;
|
|
635
665
|
focusColor: string;
|
|
636
666
|
leftIcon?: React.ReactNode;
|
|
667
|
+
colors: Required<StyledDropdownColors>;
|
|
637
668
|
}> = ({
|
|
638
669
|
triggerRef,
|
|
639
670
|
onPress,
|
|
@@ -648,24 +679,25 @@ const Trigger: React.FC<{
|
|
|
648
679
|
hasError,
|
|
649
680
|
focusColor,
|
|
650
681
|
leftIcon,
|
|
682
|
+
colors,
|
|
651
683
|
}) => {
|
|
652
684
|
const sz = SZ[size];
|
|
653
685
|
const opacity = disabled ? 0.5 : 1;
|
|
654
|
-
const textColor = isPlaceholder ? placeholderColor :
|
|
686
|
+
const textColor = isPlaceholder ? placeholderColor : colors.text;
|
|
655
687
|
|
|
656
688
|
// Border colour logic
|
|
657
689
|
const borderColor = hasError
|
|
658
690
|
? theme.colors.red[500]
|
|
659
691
|
: open
|
|
660
692
|
? focusColor
|
|
661
|
-
:
|
|
693
|
+
: colors.border;
|
|
662
694
|
|
|
663
695
|
// Variant-specific styles
|
|
664
696
|
const variantStyle: ViewStyle = (() => {
|
|
665
697
|
switch (variant) {
|
|
666
698
|
case "filled":
|
|
667
699
|
return {
|
|
668
|
-
backgroundColor:
|
|
700
|
+
backgroundColor: colors.background,
|
|
669
701
|
borderWidth: 0,
|
|
670
702
|
borderBottomWidth: open ? 2 : 0,
|
|
671
703
|
borderBottomColor: borderColor,
|
|
@@ -686,7 +718,7 @@ const Trigger: React.FC<{
|
|
|
686
718
|
};
|
|
687
719
|
default: // outline
|
|
688
720
|
return {
|
|
689
|
-
backgroundColor:
|
|
721
|
+
backgroundColor: colors.background,
|
|
690
722
|
borderWidth: open ? 1.5 : 1,
|
|
691
723
|
borderColor: borderColor,
|
|
692
724
|
borderRadius: 8,
|
|
@@ -810,6 +842,7 @@ export const StyledDropdown = (
|
|
|
810
842
|
emptyText = "No options found",
|
|
811
843
|
focusColor = theme.colors.indigo?.[500] ?? "#6366f1",
|
|
812
844
|
groupBy,
|
|
845
|
+
colors: colorsProp,
|
|
813
846
|
ref,
|
|
814
847
|
...rest
|
|
815
848
|
}: StyledDropdownProps & { ref?: React.Ref<any> },
|
|
@@ -819,6 +852,18 @@ export const StyledDropdown = (
|
|
|
819
852
|
const { triggerRef, pos, measure, updatePanelHeight } =
|
|
820
853
|
useMeasure(maxHeight);
|
|
821
854
|
|
|
855
|
+
// PATCHED: resolved once here so both the closed Trigger and the open
|
|
856
|
+
// ListPanel share one themed palette instead of each hard-coding white.
|
|
857
|
+
const RC: Required<StyledDropdownColors> = {
|
|
858
|
+
background: colorsProp?.background ?? theme.colors.white,
|
|
859
|
+
border: colorsProp?.border ?? theme.colors.gray[200],
|
|
860
|
+
text: colorsProp?.text ?? theme.colors.gray[800],
|
|
861
|
+
panelBackground: colorsProp?.panelBackground ?? theme.colors.white,
|
|
862
|
+
panelBorder: colorsProp?.panelBorder ?? theme.colors.gray[200],
|
|
863
|
+
panelText: colorsProp?.panelText ?? theme.colors.gray[800],
|
|
864
|
+
panelDivider: colorsProp?.panelDivider ?? theme.colors.gray[100],
|
|
865
|
+
};
|
|
866
|
+
|
|
822
867
|
const selected = value ?? internalValue;
|
|
823
868
|
const hasError = error || !!errorMessage;
|
|
824
869
|
const selectedItem = useMemo(
|
|
@@ -885,6 +930,7 @@ export const StyledDropdown = (
|
|
|
885
930
|
hasError={hasError}
|
|
886
931
|
focusColor={focusColor}
|
|
887
932
|
leftIcon={leftIcon}
|
|
933
|
+
colors={RC}
|
|
888
934
|
/>
|
|
889
935
|
{open && (
|
|
890
936
|
<ListPanel
|
|
@@ -897,6 +943,7 @@ export const StyledDropdown = (
|
|
|
897
943
|
multiSelect={false}
|
|
898
944
|
searchable={searchable}
|
|
899
945
|
searchPlaceholder={searchPlaceholder}
|
|
946
|
+
colors={RC}
|
|
900
947
|
clearable={clearable}
|
|
901
948
|
onClear={handleClear}
|
|
902
949
|
emptyText={emptyText}
|
|
@@ -943,6 +990,7 @@ export const StyledMultiSelectDropdown = (
|
|
|
943
990
|
separator = ", ",
|
|
944
991
|
maxDisplay = 3,
|
|
945
992
|
selectAll = false,
|
|
993
|
+
colors: colorsProp,
|
|
946
994
|
ref,
|
|
947
995
|
...rest
|
|
948
996
|
}: StyledMultiSelectDropdownProps & { ref?: React.Ref<any> },
|
|
@@ -951,6 +999,17 @@ export const StyledMultiSelectDropdown = (
|
|
|
951
999
|
const [internalKeys, setInternalKeys] = useState<string[]>(
|
|
952
1000
|
defaultValue ?? [],
|
|
953
1001
|
);
|
|
1002
|
+
|
|
1003
|
+
// PATCHED: see StyledDropdown above — same shared-palette rationale.
|
|
1004
|
+
const RC: Required<StyledDropdownColors> = {
|
|
1005
|
+
background: colorsProp?.background ?? theme.colors.white,
|
|
1006
|
+
border: colorsProp?.border ?? theme.colors.gray[200],
|
|
1007
|
+
text: colorsProp?.text ?? theme.colors.gray[800],
|
|
1008
|
+
panelBackground: colorsProp?.panelBackground ?? theme.colors.white,
|
|
1009
|
+
panelBorder: colorsProp?.panelBorder ?? theme.colors.gray[200],
|
|
1010
|
+
panelText: colorsProp?.panelText ?? theme.colors.gray[800],
|
|
1011
|
+
panelDivider: colorsProp?.panelDivider ?? theme.colors.gray[100],
|
|
1012
|
+
};
|
|
954
1013
|
const { triggerRef, pos, measure, updatePanelHeight } = useMeasure(maxHeight);
|
|
955
1014
|
|
|
956
1015
|
const selectedKeys = useMemo(
|
|
@@ -1042,9 +1101,11 @@ export const StyledMultiSelectDropdown = (
|
|
|
1042
1101
|
hasError={hasError}
|
|
1043
1102
|
focusColor={focusColor}
|
|
1044
1103
|
leftIcon={leftIcon}
|
|
1104
|
+
colors={RC}
|
|
1045
1105
|
/>
|
|
1046
1106
|
{open && (
|
|
1047
1107
|
<ListPanel
|
|
1108
|
+
colors={RC}
|
|
1048
1109
|
data={data}
|
|
1049
1110
|
selectedKeys={selectedKeys}
|
|
1050
1111
|
onSelect={handleSelect}
|
package/src/input/index.tsx
CHANGED
|
@@ -15,13 +15,10 @@ import {
|
|
|
15
15
|
View,
|
|
16
16
|
} from 'react-native'
|
|
17
17
|
import type {
|
|
18
|
-
NativeSyntheticEvent,
|
|
19
|
-
TextInputFocusEventData,
|
|
20
18
|
TextStyle,
|
|
21
19
|
ViewStyle,
|
|
22
20
|
} from 'react-native'
|
|
23
21
|
|
|
24
|
-
import { styled } from '../utiles/styled'
|
|
25
22
|
import { theme } from '../utiles/theme'
|
|
26
23
|
import { viewStyleVariants, viewStyleStringVariants } from '../utiles/viewStyleVariants'
|
|
27
24
|
import { StyledText } from '../text'
|
|
@@ -129,41 +126,83 @@ export interface StyledTextInputProps
|
|
|
129
126
|
// ── Pass-through style props (from original) ───────────────────────────
|
|
130
127
|
fontSize?: number
|
|
131
128
|
fontWeight?: TextStyle['fontWeight']
|
|
132
|
-
|
|
129
|
+
|
|
130
|
+
// ── Theme override ──────────────────────────────────────────────────────
|
|
131
|
+
/**
|
|
132
|
+
* Resting-state surface colours. Lets a consumer theme this field (e.g.
|
|
133
|
+
* for dark mode) without fighting the focus/error border logic below —
|
|
134
|
+
* those stay computed internally and only fall back to `colors.border`
|
|
135
|
+
* when neither focused nor in an error state.
|
|
136
|
+
*/
|
|
137
|
+
colors?: StyledFieldColors
|
|
138
|
+
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
...viewStyleVariants,
|
|
148
|
-
...viewStyleStringVariants,
|
|
149
|
-
|
|
150
|
-
fontSize: (selected: string) => {
|
|
151
|
-
const size = selected || theme.fontSize.normal;
|
|
152
|
-
if (isNaN(Number(size))) {
|
|
153
|
-
// throw new Error('Invalid fontSize value');
|
|
154
|
-
}
|
|
155
|
-
return { fontSize: Number(size) };
|
|
156
|
-
},
|
|
141
|
+
export interface StyledFieldColors {
|
|
142
|
+
/** Resting background of the input box. @default '#fff' (outline/filled) */
|
|
143
|
+
background?: string
|
|
144
|
+
/** Resting border colour (overridden by focus/error). @default gray[200] */
|
|
145
|
+
border?: string
|
|
146
|
+
/** Typed text colour. @default gray[800] */
|
|
147
|
+
text?: string
|
|
148
|
+
/** Placeholder text colour. @default gray[300] */
|
|
149
|
+
placeholder?: string
|
|
150
|
+
/** Label text colour — `labelProps.color` still wins if also set. @default gray[700] */
|
|
151
|
+
label?: string
|
|
152
|
+
}
|
|
157
153
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
154
|
+
// ─── Flat style-prop resolution ────────────────────────────────────────────────
|
|
155
|
+
// Replaces the generic `styled()` wrapper that used to sit around TextInput.
|
|
156
|
+
// Preserves its behaviour exactly: any flat ViewStyle/TextStyle prop (e.g.
|
|
157
|
+
// `opacity`, `padding`, `borderColor`) passed straight to StyledTextInput is
|
|
158
|
+
// resolved into a real style on the inner TextInput and stripped out of the
|
|
159
|
+
// props spread onto it — same variant maps `styled()` used, resolved locally
|
|
160
|
+
// instead of through the shared HOC.
|
|
161
|
+
const BASE_INPUT_STYLE: TextStyle = {
|
|
162
|
+
borderColor: theme.colors.gray[200],
|
|
163
|
+
// PATCHED: was theme.colors.gray[1] (white) — this style lands on the
|
|
164
|
+
// *inner* native TextInput, layered on top of the wrap View that
|
|
165
|
+
// `variantWrapStyle`/`FC.background` already colour. Nothing downstream
|
|
166
|
+
// ever overrode it, so every input rendered an opaque white rectangle
|
|
167
|
+
// regardless of the wrap's themed colour. Transparent lets the wrap's
|
|
168
|
+
// colour actually show through.
|
|
169
|
+
backgroundColor: 'transparent',
|
|
170
|
+
flex: 1,
|
|
171
|
+
color: theme.colors.gray[800],
|
|
172
|
+
paddingHorizontal: 16,
|
|
173
|
+
paddingVertical: 12,
|
|
174
|
+
fontSize: theme.fontSize.normal,
|
|
175
|
+
minHeight: 48,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const FLAT_STYLE_VARIANTS: Record<string, any> = {
|
|
179
|
+
...viewStyleVariants,
|
|
180
|
+
...viewStyleStringVariants,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const resolveFlatStyle = (
|
|
184
|
+
props: Record<string, any>,
|
|
185
|
+
): { style: TextStyle; clean: Record<string, any> } => {
|
|
186
|
+
const style: TextStyle = { ...BASE_INPUT_STYLE }
|
|
187
|
+
const clean: Record<string, any> = { ...props }
|
|
188
|
+
|
|
189
|
+
Object.keys(FLAT_STYLE_VARIANTS).forEach((key) => {
|
|
190
|
+
if (!(key in clean)) return
|
|
191
|
+
const selected = clean[key]
|
|
192
|
+
delete clean[key]
|
|
193
|
+
|
|
194
|
+
const variant = FLAT_STYLE_VARIANTS[key]
|
|
195
|
+
if (typeof variant === 'function') {
|
|
196
|
+
const resolved = variant(selected, props)
|
|
197
|
+
if (resolved) Object.assign(style, resolved)
|
|
198
|
+
} else if (variant?.[selected]) {
|
|
199
|
+
const value = variant[selected]
|
|
200
|
+
Object.assign(style, typeof value === 'function' ? value(selected, props) : value)
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
return { style, clean }
|
|
205
|
+
}
|
|
167
206
|
|
|
168
207
|
export interface StyledTextInputHandle extends StyledTextInputProps {
|
|
169
208
|
focus: () => void
|
|
@@ -201,7 +240,9 @@ function variantWrapStyle(
|
|
|
201
240
|
switch (variant) {
|
|
202
241
|
case 'filled':
|
|
203
242
|
return {
|
|
204
|
-
|
|
243
|
+
// PATCHED: used to hard-jump to '#fff' on focus, which fought any
|
|
244
|
+
// themed `bg` (e.g. a dark surface would flash white on tap).
|
|
245
|
+
backgroundColor: bg,
|
|
205
246
|
borderWidth: focused ? 1.5 : 0,
|
|
206
247
|
borderColor: bc,
|
|
207
248
|
}
|
|
@@ -218,8 +259,11 @@ function variantWrapStyle(
|
|
|
218
259
|
borderWidth: 0,
|
|
219
260
|
}
|
|
220
261
|
default: // outline
|
|
262
|
+
// PATCHED: used to hard-code '#fff', ignoring the `bg` param entirely
|
|
263
|
+
// — the one line responsible for every "white input on a dark
|
|
264
|
+
// screen" bug. `bg` now carries the caller's themed surface colour.
|
|
221
265
|
return {
|
|
222
|
-
backgroundColor:
|
|
266
|
+
backgroundColor: bg,
|
|
223
267
|
borderWidth: focused ? 1.5 : 1,
|
|
224
268
|
borderColor: bc,
|
|
225
269
|
}
|
|
@@ -368,6 +412,7 @@ export const StyledTextInput = (
|
|
|
368
412
|
borderColor: borderColorProp,
|
|
369
413
|
focusColor = theme.colors.indigo?.[500] ?? '#6366f1',
|
|
370
414
|
floatLabel = false,
|
|
415
|
+
colors: colorsProp,
|
|
371
416
|
|
|
372
417
|
// Styles
|
|
373
418
|
containerStyle,
|
|
@@ -396,7 +441,7 @@ export const StyledTextInput = (
|
|
|
396
441
|
onBlur: onBlurProp,
|
|
397
442
|
ref,
|
|
398
443
|
...rest
|
|
399
|
-
}: StyledTextInputProps & { ref?: React.Ref<
|
|
444
|
+
}: StyledTextInputProps & { ref?: React.Ref<StyledTextInputHandle> },
|
|
400
445
|
) => {
|
|
401
446
|
const [focused, setFocused] = useState(false)
|
|
402
447
|
const [localValue, setLocalValue] = useState(defaultValue ?? '')
|
|
@@ -418,12 +463,12 @@ export const StyledTextInput = (
|
|
|
418
463
|
onChangeText?.(text)
|
|
419
464
|
}, [value, onChangeText])
|
|
420
465
|
|
|
421
|
-
const handleFocus = useCallback((e:
|
|
466
|
+
const handleFocus = useCallback((e: Parameters<NonNullable<TextInputProps['onFocus']>>[0]) => {
|
|
422
467
|
setFocused(true)
|
|
423
468
|
onFocusProp?.(e)
|
|
424
469
|
}, [onFocusProp])
|
|
425
470
|
|
|
426
|
-
const handleBlur = useCallback((e:
|
|
471
|
+
const handleBlur = useCallback((e: Parameters<NonNullable<TextInputProps['onBlur']>>[0]) => {
|
|
427
472
|
setFocused(false)
|
|
428
473
|
onBlurProp?.(e)
|
|
429
474
|
}, [onBlurProp])
|
|
@@ -432,8 +477,20 @@ export const StyledTextInput = (
|
|
|
432
477
|
const hasError = error || !!errorMessage
|
|
433
478
|
const sz = SIZE[size]
|
|
434
479
|
|
|
480
|
+
// PATCHED: `colors` lets a consumer theme the resting surface (e.g. for
|
|
481
|
+
// dark mode) without touching the focus/error logic below — those stay
|
|
482
|
+
// computed here and only fall back to `FC.border` when neither focused
|
|
483
|
+
// nor erroring, so a themed border never masks a validation error.
|
|
484
|
+
const FC = {
|
|
485
|
+
background: colorsProp?.background ?? (theme.colors.gray[50] ?? '#fafafa'),
|
|
486
|
+
border: colorsProp?.border ?? theme.colors.gray[200],
|
|
487
|
+
text: colorsProp?.text ?? theme.colors.gray[800],
|
|
488
|
+
placeholder: colorsProp?.placeholder ?? theme.colors.gray[300],
|
|
489
|
+
label: colorsProp?.label ?? theme.colors.gray[700],
|
|
490
|
+
}
|
|
491
|
+
|
|
435
492
|
const resolvedBorderColor = borderColorProp
|
|
436
|
-
?? (hasError ? theme.colors.red[500] :
|
|
493
|
+
?? (hasError ? theme.colors.red[500] : FC.border)
|
|
437
494
|
|
|
438
495
|
const resolvedFocusColor = hasError
|
|
439
496
|
? theme.colors.red[500]
|
|
@@ -442,7 +499,7 @@ export const StyledTextInput = (
|
|
|
442
499
|
const wrapStyle = variantWrapStyle(
|
|
443
500
|
variant,
|
|
444
501
|
resolvedBorderColor,
|
|
445
|
-
|
|
502
|
+
FC.background,
|
|
446
503
|
focused,
|
|
447
504
|
resolvedFocusColor,
|
|
448
505
|
)
|
|
@@ -451,6 +508,11 @@ export const StyledTextInput = (
|
|
|
451
508
|
const hasLeftAddon = !!leftAddon
|
|
452
509
|
const hasRightAddon = !!rightAddon
|
|
453
510
|
|
|
511
|
+
// Resolves any flat ViewStyle/TextStyle prop passed straight to
|
|
512
|
+
// StyledTextInput (e.g. `opacity`, `padding`) into a real style, and
|
|
513
|
+
// strips those keys out of what gets spread onto the native TextInput.
|
|
514
|
+
const { style: flatStyle, clean: cleanRest } = resolveFlatStyle(rest)
|
|
515
|
+
|
|
454
516
|
// ── Right-side icons (priority: loading > clear > rightIcon) ─────────
|
|
455
517
|
const resolvedRightNode = loading ? (
|
|
456
518
|
<ActivityIndicator size="small" color={theme.colors.gray[400]} />
|
|
@@ -476,8 +538,8 @@ export const StyledTextInput = (
|
|
|
476
538
|
<StyledText
|
|
477
539
|
fontSize={theme.fontSize.small}
|
|
478
540
|
fontWeight={theme.fontWeight.semiBold}
|
|
479
|
-
color={
|
|
480
|
-
|
|
541
|
+
color={FC.label}
|
|
542
|
+
|
|
481
543
|
marginBottom={6}
|
|
482
544
|
{...labelProps}
|
|
483
545
|
>
|
|
@@ -535,7 +597,7 @@ export const StyledTextInput = (
|
|
|
535
597
|
) : null}
|
|
536
598
|
|
|
537
599
|
{/* Core TextInput */}
|
|
538
|
-
<
|
|
600
|
+
<TextInput
|
|
539
601
|
ref={inputRef}
|
|
540
602
|
value={value}
|
|
541
603
|
defaultValue={defaultValue}
|
|
@@ -543,12 +605,13 @@ export const StyledTextInput = (
|
|
|
543
605
|
onFocus={handleFocus}
|
|
544
606
|
onBlur={handleBlur}
|
|
545
607
|
placeholder={floatLabel ? undefined : placeholder}
|
|
546
|
-
placeholderTextColor={
|
|
608
|
+
placeholderTextColor={FC.placeholder}
|
|
547
609
|
editable={editable && !loading}
|
|
548
610
|
multiline={multiline}
|
|
549
611
|
numberOfLines={numberOfLines}
|
|
550
612
|
maxLength={maxLength}
|
|
551
613
|
style={[
|
|
614
|
+
flatStyle,
|
|
552
615
|
S.input,
|
|
553
616
|
{
|
|
554
617
|
fontSize: fontSizeProp ?? sz.fontSize,
|
|
@@ -557,12 +620,12 @@ export const StyledTextInput = (
|
|
|
557
620
|
paddingVertical: sz.paddingV,
|
|
558
621
|
minHeight: multiline ? sz.minHeight * 2 : sz.minHeight,
|
|
559
622
|
opacity: editable && !loading ? 1 : 0.55,
|
|
560
|
-
color:
|
|
623
|
+
color: FC.text,
|
|
561
624
|
textAlignVertical: multiline ? 'top' : 'center',
|
|
562
625
|
},
|
|
563
626
|
inputStyle,
|
|
564
627
|
]}
|
|
565
|
-
{...
|
|
628
|
+
{...cleanRest}
|
|
566
629
|
/>
|
|
567
630
|
|
|
568
631
|
{/* Right icon / clear / spinner */}
|
package/src/page/index.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { StyledSafeAreaView, StyledSafeAreaViewProps } from '../safeAreaView';
|
|
3
|
-
import { StatusBar, StatusBarProps } from 'react-native';
|
|
3
|
+
import { StatusBar, StatusBarProps, Platform, useColorScheme } from 'react-native';
|
|
4
4
|
import { StyledHeader } from '../header';
|
|
5
5
|
|
|
6
6
|
interface StyledPageProps extends StyledSafeAreaViewProps {
|
|
@@ -19,10 +19,43 @@ interface StyledPageType {
|
|
|
19
19
|
displayName?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// statusBarStyle used to default to a hardcoded 'dark-content' regardless of
|
|
23
|
+
// the active theme — any screen rendered in dark mode that forgot to pass
|
|
24
|
+
// its own statusBarStyle got dark (near-invisible) status bar icons on a
|
|
25
|
+
// dark background. Falls back to the OS color scheme via useColorScheme()
|
|
26
|
+
// instead, the same signal a theme-aware caller would use for its own
|
|
27
|
+
// explicit statusBarStyle.
|
|
28
|
+
//
|
|
29
|
+
// translucentStatusBar now also defaults to true on Android specifically
|
|
30
|
+
// (via Platform.select): with edge-to-edge unavoidable from targetSdk 35+,
|
|
31
|
+
// StatusBar's `backgroundColor` prop is deprecated and silently ignored on
|
|
32
|
+
// Android there, so a non-translucent bar left the *native* window
|
|
33
|
+
// background (white by default) showing behind the status bar instead of
|
|
34
|
+
// the page's own backgroundColor. iOS's status bar is always an overlay, so
|
|
35
|
+
// it's left unaffected (translucentStatusBar stays undefined there unless
|
|
36
|
+
// the caller sets it).
|
|
37
|
+
const Page = ({
|
|
38
|
+
children, statusBarProps, showStatusBar = true,
|
|
39
|
+
statusBarStyle, statusBarBackgroundColor,
|
|
40
|
+
hideStatusBarOnAndroid, hideStatusBarOnIOS,
|
|
41
|
+
translucentStatusBar,
|
|
42
|
+
...props
|
|
43
|
+
}: React.PropsWithChildren<StyledPageProps>) => {
|
|
44
|
+
const colorScheme = useColorScheme();
|
|
45
|
+
const resolvedStatusBarStyle = statusBarStyle ?? (colorScheme === 'dark' ? 'light-content' : 'dark-content');
|
|
46
|
+
const resolvedTranslucent = translucentStatusBar ?? Platform.select({ android: true, default: undefined });
|
|
47
|
+
|
|
23
48
|
return (
|
|
24
49
|
<StyledSafeAreaView {...props}>
|
|
25
|
-
{showStatusBar &&
|
|
50
|
+
{showStatusBar && (
|
|
51
|
+
<StatusBar
|
|
52
|
+
hidden={hideStatusBarOnAndroid || hideStatusBarOnIOS}
|
|
53
|
+
barStyle={resolvedStatusBarStyle}
|
|
54
|
+
backgroundColor={statusBarBackgroundColor}
|
|
55
|
+
translucent={resolvedTranslucent}
|
|
56
|
+
{...statusBarProps}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
26
59
|
{children}
|
|
27
60
|
</StyledSafeAreaView>
|
|
28
61
|
)
|
package/src/pressable/index.tsx
CHANGED
|
@@ -2,16 +2,22 @@
|
|
|
2
2
|
import { Pressable, PressableProps } from 'react-native';
|
|
3
3
|
import { styled } from '../utiles/styled';
|
|
4
4
|
import { ViewStyle } from '../utiles/viewStyleProps';
|
|
5
|
-
import { viewStyleStringVariants } from '../utiles/viewStyleVariants';
|
|
5
|
+
import { viewStyleVariants, viewStyleStringVariants } from '../utiles/viewStyleVariants';
|
|
6
6
|
|
|
7
7
|
type StyledPressableProps = PressableProps & ViewStyle;
|
|
8
8
|
|
|
9
|
+
// Same missing-viewStyleVariants gap as StyledSafeAreaView (see that file
|
|
10
|
+
// for the full explanation): width/height/borderRadius/backgroundColor
|
|
11
|
+
// passed to StyledPressable were silently dropped instead of styled —
|
|
12
|
+
// e.g. an icon button built as <StyledPressable width={44} height={44}
|
|
13
|
+
// borderRadius={22} backgroundColor={...}> rendered with none of that.
|
|
9
14
|
const StyledPressable = styled<StyledPressableProps>(Pressable, {
|
|
10
15
|
base: {
|
|
11
16
|
position: 'relative',
|
|
12
17
|
} as ViewStyle,
|
|
13
18
|
variants: {
|
|
14
|
-
...
|
|
19
|
+
...viewStyleVariants,
|
|
20
|
+
...viewStyleStringVariants,
|
|
15
21
|
} as any
|
|
16
22
|
});
|
|
17
23
|
|
|
@@ -4,15 +4,25 @@ import {
|
|
|
4
4
|
} from 'react-native-safe-area-context';
|
|
5
5
|
import { styled } from '../utiles/styled';
|
|
6
6
|
import { ViewStyleProps, ViewStyle } from '../utiles/viewStyleProps';
|
|
7
|
-
import { viewStyleStringVariants } from '../utiles/viewStyleVariants';
|
|
7
|
+
import { viewStyleVariants, viewStyleStringVariants } from '../utiles/viewStyleVariants';
|
|
8
8
|
|
|
9
9
|
type StyledSafeAreaViewProps = SafeAreaViewProps & ViewStyleProps;
|
|
10
10
|
|
|
11
|
+
// This only spread viewStyleStringVariants, so backgroundColor/padding/
|
|
12
|
+
// margin/width/height/borderRadius/etc — everything defined in
|
|
13
|
+
// viewStyleVariants instead — were never registered as variants here. They
|
|
14
|
+
// fell through as raw unhandled props on SafeAreaView instead of landing in
|
|
15
|
+
// `style`, so e.g. <StyledPage backgroundColor="#111"> silently did nothing.
|
|
16
|
+
// Stack already spreads both and works correctly, which is what exposed the
|
|
17
|
+
// gap: on Android specifically, a screen that relied on StyledPage's
|
|
18
|
+
// backgroundColor to paint the safe area showed the native window's default
|
|
19
|
+
// white background through instead.
|
|
11
20
|
const StyledSafeAreaView = styled<StyledSafeAreaViewProps>(SafeAreaView, {
|
|
12
21
|
base: {
|
|
13
22
|
flex: 1,
|
|
14
23
|
} as ViewStyle,
|
|
15
24
|
variants: {
|
|
25
|
+
...viewStyleVariants,
|
|
16
26
|
...viewStyleStringVariants,
|
|
17
27
|
}
|
|
18
28
|
});
|
package/src/utiles/styled.tsx
CHANGED
|
@@ -17,7 +17,16 @@ const styled = <P extends object>(
|
|
|
17
17
|
Component: ComponentType<P>,
|
|
18
18
|
{ base, variants }: StyledOptions = {}
|
|
19
19
|
) => {
|
|
20
|
-
|
|
20
|
+
// `ref` used to be handled via a plain function reading it off props —
|
|
21
|
+
// that only works under React 19's "ref as prop" model. Consumers still
|
|
22
|
+
// on React 18 (the current baseline for RN/Expo) get a plain function
|
|
23
|
+
// component that can never receive a ref at all (React strips it before
|
|
24
|
+
// the function runs), so any ref attached to a styled() component —
|
|
25
|
+
// including this library's own internals, e.g. StyledDropdown's Trigger
|
|
26
|
+
// measuring its own on-screen position — silently no-ops with a
|
|
27
|
+
// "Function components cannot be given refs" warning. forwardRef is the
|
|
28
|
+
// version that works on both React 18 and 19.
|
|
29
|
+
const StyledComponent = React.forwardRef<any, P>((props, ref) => {
|
|
21
30
|
const styles: Style = { ...(base || {}) };
|
|
22
31
|
const options = props as Record<string, any>;
|
|
23
32
|
const incomingStyle = options.style;
|
|
@@ -39,8 +48,8 @@ const styled = <P extends object>(
|
|
|
39
48
|
});
|
|
40
49
|
}
|
|
41
50
|
|
|
42
|
-
return <Component {...(cleanProps as P)} style={[styles, incomingStyle]} />;
|
|
43
|
-
};
|
|
51
|
+
return <Component {...(cleanProps as P)} ref={ref} style={[styles, incomingStyle]} />;
|
|
52
|
+
});
|
|
44
53
|
|
|
45
54
|
StyledComponent.displayName = `Styled(${
|
|
46
55
|
typeof Component === "string" ? Component : Component.displayName ?? Component.name ?? "Component"
|