@colixsystems/widget-sdk 0.210.0 → 0.212.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -2
- package/dist/checkbox-view.js +150 -0
- package/dist/checkbox.js +11 -0
- package/dist/checkbox.native.js +7 -0
- package/dist/contract.cjs +42 -3
- package/dist/contract.js +42 -3
- package/dist/form-control-logic.js +55 -0
- package/dist/form-control-tokens.js +121 -0
- package/dist/form-control-view.js +98 -0
- package/dist/index.d.ts +136 -6
- package/dist/index.js +9 -1
- package/dist/index.native.js +9 -1
- package/dist/search-field-view.js +155 -0
- package/dist/search-field.js +13 -0
- package/dist/search-field.native.js +9 -0
- package/dist/select-view.js +312 -0
- package/dist/select.js +15 -0
- package/dist/select.native.js +9 -0
- package/dist/switch-view.js +149 -0
- package/dist/switch.js +80 -0
- package/dist/switch.native.js +7 -0
- package/dist/text-field-view.js +54 -8
- package/dist/text-field.js +6 -1
- package/dist/text-field.native.js +3 -3
- package/package.json +1 -1
- package/src/contract.js +42 -3
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// What every form control shares on screen: the label above it and the helper
|
|
2
|
+
// or error below it. One frame, so `<TextField>`, `<Checkbox>`, `<Switch>`,
|
|
3
|
+
// `<Select>` and `<SearchField>` cannot drift apart. Its rules, which need no
|
|
4
|
+
// renderer, live in ./form-control-logic.js.
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { errorMessage, isShown, isText } from "./form-control-logic.js";
|
|
8
|
+
|
|
9
|
+
const h = React.createElement;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Bind the shared frame to one platform's primitives.
|
|
13
|
+
*
|
|
14
|
+
* @param {{View: any, Text: any, Pressable: any}} rn
|
|
15
|
+
*/
|
|
16
|
+
export function makeFieldFrame(rn) {
|
|
17
|
+
const { View, Text, Pressable } = rn;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {object} props
|
|
21
|
+
* @param {object} props.t — resolved form-control tokens.
|
|
22
|
+
* @param {any} [props.label] — text, or an element rendered as given.
|
|
23
|
+
* @param {any} [props.helper]
|
|
24
|
+
* @param {any} [props.error] — a message replaces the helper; `true` shows none.
|
|
25
|
+
* @param {string} [props.noteId] — the id the control's `aria-describedby` names.
|
|
26
|
+
* @param {() => void} [props.onLabelPress] — a text label then acts for the control.
|
|
27
|
+
* @param {boolean} [props.noteDisabled] — dim the note alone, when the
|
|
28
|
+
* control dims itself and `disabled` would dim it twice.
|
|
29
|
+
*/
|
|
30
|
+
function FieldFrame({
|
|
31
|
+
t,
|
|
32
|
+
label,
|
|
33
|
+
helper,
|
|
34
|
+
error,
|
|
35
|
+
disabled,
|
|
36
|
+
noteId,
|
|
37
|
+
onLabelPress,
|
|
38
|
+
noteDisabled,
|
|
39
|
+
style,
|
|
40
|
+
children,
|
|
41
|
+
}) {
|
|
42
|
+
const message = errorMessage(error);
|
|
43
|
+
const note = message || (isShown(helper) ? helper : null);
|
|
44
|
+
const labelText = isText(label)
|
|
45
|
+
? h(
|
|
46
|
+
Text,
|
|
47
|
+
{
|
|
48
|
+
style: {
|
|
49
|
+
color: t.labelColor,
|
|
50
|
+
fontFamily: t.fontFamily,
|
|
51
|
+
fontSize: t.labelSize,
|
|
52
|
+
fontWeight: "500",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
label,
|
|
56
|
+
)
|
|
57
|
+
: null;
|
|
58
|
+
return h(
|
|
59
|
+
View,
|
|
60
|
+
{ style: [{ gap: t.gap }, disabled ? { opacity: t.disabledOpacity } : null, style] },
|
|
61
|
+
labelText && typeof onLabelPress === "function"
|
|
62
|
+
? h(
|
|
63
|
+
Pressable,
|
|
64
|
+
{
|
|
65
|
+
onPress: onLabelPress,
|
|
66
|
+
disabled,
|
|
67
|
+
// The control is the one stop for a keyboard and a reader.
|
|
68
|
+
tabIndex: -1,
|
|
69
|
+
accessible: false,
|
|
70
|
+
style: { alignSelf: "flex-start" },
|
|
71
|
+
},
|
|
72
|
+
labelText,
|
|
73
|
+
)
|
|
74
|
+
: labelText || (isShown(label) ? label : null),
|
|
75
|
+
children,
|
|
76
|
+
isText(note)
|
|
77
|
+
? h(
|
|
78
|
+
Text,
|
|
79
|
+
{
|
|
80
|
+
nativeID: noteId,
|
|
81
|
+
// Announced where the host can: the web and Android.
|
|
82
|
+
...(message ? { accessibilityLiveRegion: "polite" } : null),
|
|
83
|
+
style: {
|
|
84
|
+
color: message ? t.errorColor : t.helperColor,
|
|
85
|
+
fontFamily: t.fontFamily,
|
|
86
|
+
fontSize: t.helperSize,
|
|
87
|
+
...(noteDisabled ? { opacity: t.disabledOpacity } : null),
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
note,
|
|
91
|
+
)
|
|
92
|
+
: note,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
FieldFrame.displayName = "FieldFrame";
|
|
97
|
+
return FieldFrame;
|
|
98
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3167,7 +3167,6 @@ export const TextInput: any;
|
|
|
3167
3167
|
export const FlatList: any;
|
|
3168
3168
|
export const SectionList: any;
|
|
3169
3169
|
export const ActivityIndicator: any;
|
|
3170
|
-
export const Switch: any;
|
|
3171
3170
|
export const StyleSheet: any;
|
|
3172
3171
|
|
|
3173
3172
|
/**
|
|
@@ -3620,19 +3619,150 @@ export const Button: (props: {
|
|
|
3620
3619
|
}) => any;
|
|
3621
3620
|
|
|
3622
3621
|
/**
|
|
3623
|
-
*
|
|
3624
|
-
*
|
|
3625
|
-
*
|
|
3626
|
-
* <TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />
|
|
3622
|
+
* What every form control shares: the label drawn with it (and read by a
|
|
3623
|
+
* screen reader), a hint below it, and an error that replaces the hint and
|
|
3624
|
+
* marks the control. `error: true` marks it without a message.
|
|
3627
3625
|
*/
|
|
3628
|
-
export
|
|
3626
|
+
export interface FormControlProps {
|
|
3627
|
+
label?: ReactNode;
|
|
3628
|
+
helper?: ReactNode;
|
|
3629
|
+
error?: ReactNode | boolean;
|
|
3630
|
+
disabled?: boolean;
|
|
3631
|
+
/** Required when there is no text `label`. */
|
|
3632
|
+
accessibilityLabel?: string;
|
|
3633
|
+
testID?: string;
|
|
3634
|
+
}
|
|
3635
|
+
|
|
3636
|
+
/** Per-control overrides of the theme's `muted` scope. */
|
|
3637
|
+
export interface FormFieldOverrides {
|
|
3629
3638
|
background?: string;
|
|
3630
3639
|
textColor?: string;
|
|
3631
3640
|
borderColor?: string;
|
|
3632
3641
|
radius?: number;
|
|
3633
3642
|
height?: number;
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
/**
|
|
3646
|
+
* Themed text-entry field (Design > Components > Muted). Every TextInput prop
|
|
3647
|
+
* works.
|
|
3648
|
+
*
|
|
3649
|
+
* @example
|
|
3650
|
+
* <TextField label="Name" value={name} onChangeText={setName} placeholder="Your name" />
|
|
3651
|
+
*/
|
|
3652
|
+
export const TextField: (props: FormControlProps & FormFieldOverrides & {
|
|
3634
3653
|
multiline?: boolean;
|
|
3635
3654
|
style?: any;
|
|
3655
|
+
/** The box around a labelled field. */
|
|
3656
|
+
containerStyle?: any;
|
|
3657
|
+
[prop: string]: any;
|
|
3658
|
+
}) => any;
|
|
3659
|
+
|
|
3660
|
+
/**
|
|
3661
|
+
* Themed tick box: the input surface, filled with the primary colour when
|
|
3662
|
+
* checked. One 44px target with the checkbox role and state on both hosts.
|
|
3663
|
+
*
|
|
3664
|
+
* @example
|
|
3665
|
+
* <Checkbox label="Remember me" value={remember} onValueChange={setRemember} />
|
|
3666
|
+
*/
|
|
3667
|
+
export const Checkbox: (props: FormControlProps & {
|
|
3668
|
+
value?: boolean;
|
|
3669
|
+
onValueChange?: (next: boolean) => void;
|
|
3670
|
+
/** The row. */
|
|
3671
|
+
style?: any;
|
|
3672
|
+
/** The box around a row that shows a helper or an error. */
|
|
3673
|
+
containerStyle?: any;
|
|
3674
|
+
[prop: string]: any;
|
|
3675
|
+
}) => any;
|
|
3676
|
+
|
|
3677
|
+
/**
|
|
3678
|
+
* Themed on/off toggle: the platform's own switch with the theme's track
|
|
3679
|
+
* colours. Every React Native `Switch` prop works: a `trackColor` overrides
|
|
3680
|
+
* the theme for the states it names, and the thumb is the platform's unless
|
|
3681
|
+
* `thumbColor` is passed. Without a label, helper or error it renders the
|
|
3682
|
+
* bare switch.
|
|
3683
|
+
*
|
|
3684
|
+
* @example
|
|
3685
|
+
* <Switch label="Email me updates" value={on} onValueChange={setOn} />
|
|
3686
|
+
*/
|
|
3687
|
+
export const Switch: (props: FormControlProps & {
|
|
3688
|
+
value?: boolean;
|
|
3689
|
+
onValueChange?: (next: boolean) => void;
|
|
3690
|
+
trackColor?: { false?: string; true?: string };
|
|
3691
|
+
thumbColor?: string;
|
|
3692
|
+
style?: any;
|
|
3693
|
+
/** The box around a labelled switch. */
|
|
3694
|
+
containerStyle?: any;
|
|
3695
|
+
[prop: string]: any;
|
|
3696
|
+
}) => any;
|
|
3697
|
+
|
|
3698
|
+
export interface SelectOption<V extends string | number = string | number> {
|
|
3699
|
+
value: V;
|
|
3700
|
+
label: string;
|
|
3701
|
+
disabled?: boolean;
|
|
3702
|
+
testID?: string;
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
/**
|
|
3706
|
+
* Themed single-choice picker. The trigger is the input field, a button that
|
|
3707
|
+
* opens a dialog; its options open at screen level (through `Overlay`) in a
|
|
3708
|
+
* sheet painted from the `popover` scope, so no container clips them and a
|
|
3709
|
+
* long list scrolls. Each option has the option role; the list is a listbox
|
|
3710
|
+
* on the web only. Other props land on the trigger.
|
|
3711
|
+
*
|
|
3712
|
+
* @example
|
|
3713
|
+
* <Select label="Status" value={status} onValueChange={setStatus}
|
|
3714
|
+
* options={[{ value: "open", label: "Open" }, { value: "done", label: "Done" }]} />
|
|
3715
|
+
*/
|
|
3716
|
+
export const Select: <V extends string | number = string | number>(
|
|
3717
|
+
props: FormControlProps & FormFieldOverrides & {
|
|
3718
|
+
options: Array<SelectOption<V>>;
|
|
3719
|
+
value?: V | null;
|
|
3720
|
+
onValueChange?: (value: V, option: SelectOption<V>) => void;
|
|
3721
|
+
/** Shown while nothing is chosen. Default "Select". */
|
|
3722
|
+
placeholder?: string;
|
|
3723
|
+
/** The sheet's heading. Defaults to a text `label`. */
|
|
3724
|
+
title?: string;
|
|
3725
|
+
/** Shown in the sheet when `options` is empty. Default "No options". */
|
|
3726
|
+
emptyText?: string;
|
|
3727
|
+
closeAccessibilityLabel?: string;
|
|
3728
|
+
/** The trigger. */
|
|
3729
|
+
style?: any;
|
|
3730
|
+
containerStyle?: any;
|
|
3731
|
+
[prop: string]: any;
|
|
3732
|
+
},
|
|
3733
|
+
) => any;
|
|
3734
|
+
|
|
3735
|
+
/**
|
|
3736
|
+
* Space-key activation for a control whose role is not a button: on the web a
|
|
3737
|
+
* `Pressable` answers Space only for a button. Native ignores `onKeyDown`.
|
|
3738
|
+
*
|
|
3739
|
+
* @example
|
|
3740
|
+
* <Pressable accessibilityRole="radio" onPress={pick} onKeyDown={spaceActivates(pick)} />
|
|
3741
|
+
*/
|
|
3742
|
+
export function spaceActivates(
|
|
3743
|
+
onPress: () => void,
|
|
3744
|
+
): (event: { key: string; repeat?: boolean; preventDefault: () => void }) => void;
|
|
3745
|
+
|
|
3746
|
+
/**
|
|
3747
|
+
* Themed search box: a `TextField` with a search glyph, the search key and a
|
|
3748
|
+
* clear control. Every `TextField` prop works.
|
|
3749
|
+
*
|
|
3750
|
+
* @example
|
|
3751
|
+
* <SearchField value={query} onChangeText={setQuery} placeholder="Search orders" />
|
|
3752
|
+
*/
|
|
3753
|
+
export const SearchField: (props: FormControlProps & {
|
|
3754
|
+
value?: string;
|
|
3755
|
+
onChangeText?: (text: string) => void;
|
|
3756
|
+
/** Called after the clear control empties the field. */
|
|
3757
|
+
onClear?: () => void;
|
|
3758
|
+
/** Default "Search". */
|
|
3759
|
+
placeholder?: string;
|
|
3760
|
+
/** Default "Clear search". */
|
|
3761
|
+
clearAccessibilityLabel?: string;
|
|
3762
|
+
/** The input. */
|
|
3763
|
+
style?: any;
|
|
3764
|
+
/** The box around it; use it for `flex: 1` in a toolbar. */
|
|
3765
|
+
containerStyle?: any;
|
|
3636
3766
|
[prop: string]: any;
|
|
3637
3767
|
}) => any;
|
|
3638
3768
|
|
package/dist/index.js
CHANGED
|
@@ -167,7 +167,6 @@ export {
|
|
|
167
167
|
FlatList,
|
|
168
168
|
SectionList,
|
|
169
169
|
ActivityIndicator,
|
|
170
|
-
Switch,
|
|
171
170
|
StyleSheet,
|
|
172
171
|
Linking,
|
|
173
172
|
Icon,
|
|
@@ -266,6 +265,15 @@ export {
|
|
|
266
265
|
export { TextField } from "./text-field.js";
|
|
267
266
|
// Themed collapsible sections, drawn from the `card` and `list` scopes.
|
|
268
267
|
export { Accordion, AccordionItem } from "./accordion.js";
|
|
268
|
+
// The themed form controls; they follow the theme's `muted` scope as
|
|
269
|
+
// `<TextField>` does, and `popover` for the Select's options. `<Switch>` is
|
|
270
|
+
// the platform's own.
|
|
271
|
+
export { Checkbox } from "./checkbox.js";
|
|
272
|
+
export { Switch } from "./switch.js";
|
|
273
|
+
export { Select } from "./select.js";
|
|
274
|
+
export { SearchField } from "./search-field.js";
|
|
275
|
+
// Space for a control whose role is not a button; a no-op on native.
|
|
276
|
+
export { spaceActivates } from "./form-control-logic.js";
|
|
269
277
|
// A themed tab strip; follows the theme's `tabs` scope.
|
|
270
278
|
export { Tabs } from "./tabs.js";
|
|
271
279
|
// Themed loading states; follow the theme's `loading` scope.
|
package/dist/index.native.js
CHANGED
|
@@ -160,7 +160,6 @@ export {
|
|
|
160
160
|
FlatList,
|
|
161
161
|
SectionList,
|
|
162
162
|
ActivityIndicator,
|
|
163
|
-
Switch,
|
|
164
163
|
StyleSheet,
|
|
165
164
|
Linking,
|
|
166
165
|
Icon,
|
|
@@ -259,6 +258,15 @@ export {
|
|
|
259
258
|
export { TextField } from "./text-field.native.js";
|
|
260
259
|
// Themed collapsible sections, drawn from the `card` and `list` scopes.
|
|
261
260
|
export { Accordion, AccordionItem } from "./accordion.native.js";
|
|
261
|
+
// The themed form controls; they follow the theme's `muted` scope as
|
|
262
|
+
// `<TextField>` does, and `popover` for the Select's options. `<Switch>` is
|
|
263
|
+
// the platform's own.
|
|
264
|
+
export { Checkbox } from "./checkbox.native.js";
|
|
265
|
+
export { Switch } from "./switch.native.js";
|
|
266
|
+
export { Select } from "./select.native.js";
|
|
267
|
+
export { SearchField } from "./search-field.native.js";
|
|
268
|
+
// Space for a control whose role is not a button; a no-op on native.
|
|
269
|
+
export { spaceActivates } from "./form-control-logic.js";
|
|
262
270
|
// A themed tab strip; follows the theme's `tabs` scope.
|
|
263
271
|
export { Tabs } from "./tabs.native.js";
|
|
264
272
|
// Themed loading states; follow the theme's `loading` scope.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// The ONE implementation behind the `<SearchField>` SDK primitive. It is
|
|
2
|
+
// `<TextField>` with a search glyph and a clear control, so it wears the same
|
|
3
|
+
// field and takes the same props; the platform bindings (`search-field.js` /
|
|
4
|
+
// `search-field.native.js`) differ only in where the primitives come from.
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { useHostTheme } from "./hooks.js";
|
|
8
|
+
import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
|
|
9
|
+
import { Icon } from "./icon.js";
|
|
10
|
+
import { resolveFormControlTokens } from "./form-control-tokens.js";
|
|
11
|
+
import {
|
|
12
|
+
errorMessage,
|
|
13
|
+
hasNote,
|
|
14
|
+
isShown,
|
|
15
|
+
shareRef,
|
|
16
|
+
spokenLabel,
|
|
17
|
+
} from "./form-control-logic.js";
|
|
18
|
+
import { makeFieldFrame } from "./form-control-view.js";
|
|
19
|
+
|
|
20
|
+
const h = React.createElement;
|
|
21
|
+
const GLYPH_SIZE = 18;
|
|
22
|
+
// The glyph and the clear control each own a strip this wide inside the field.
|
|
23
|
+
const ADORNMENT_WIDTH = 40;
|
|
24
|
+
// A real target: the web host ignores hitSlop, so size carries it.
|
|
25
|
+
const CLEAR_MIN_SIZE = 36;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Bind the shared search field to one platform's primitives.
|
|
29
|
+
*
|
|
30
|
+
* @param {{View: any, Text: any, Pressable: any, TextField: any}} rn
|
|
31
|
+
*/
|
|
32
|
+
export function makeSearchField(rn) {
|
|
33
|
+
const { View, Pressable, TextField } = rn;
|
|
34
|
+
const FieldFrame = makeFieldFrame(rn);
|
|
35
|
+
|
|
36
|
+
const SearchField = React.forwardRef(function SearchField(props, ref) {
|
|
37
|
+
const {
|
|
38
|
+
value,
|
|
39
|
+
onChangeText,
|
|
40
|
+
onClear,
|
|
41
|
+
placeholder = "Search",
|
|
42
|
+
clearAccessibilityLabel = "Clear search",
|
|
43
|
+
label,
|
|
44
|
+
helper,
|
|
45
|
+
error,
|
|
46
|
+
disabled = false,
|
|
47
|
+
accessibilityLabel,
|
|
48
|
+
style,
|
|
49
|
+
containerStyle,
|
|
50
|
+
testID,
|
|
51
|
+
...rest
|
|
52
|
+
} = props;
|
|
53
|
+
const theme = useHostTheme() || DEFAULT_THEME_TOKENS;
|
|
54
|
+
const t = resolveFormControlTokens(theme);
|
|
55
|
+
const inputRef = React.useRef(null);
|
|
56
|
+
const setRef = React.useMemo(() => shareRef(ref, inputRef), [ref]);
|
|
57
|
+
const noteId = React.useId();
|
|
58
|
+
const focusInput = () => {
|
|
59
|
+
const input = inputRef.current;
|
|
60
|
+
if (input && typeof input.focus === "function") input.focus();
|
|
61
|
+
};
|
|
62
|
+
const hasText = typeof value === "string" && value.length > 0;
|
|
63
|
+
const clear = () => {
|
|
64
|
+
if (typeof onChangeText === "function") onChangeText("");
|
|
65
|
+
if (typeof onClear === "function") onClear();
|
|
66
|
+
focusInput();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return h(
|
|
70
|
+
FieldFrame,
|
|
71
|
+
{
|
|
72
|
+
t,
|
|
73
|
+
label,
|
|
74
|
+
helper,
|
|
75
|
+
error: errorMessage(error),
|
|
76
|
+
disabled,
|
|
77
|
+
noteId,
|
|
78
|
+
onLabelPress: focusInput,
|
|
79
|
+
style: containerStyle,
|
|
80
|
+
},
|
|
81
|
+
h(
|
|
82
|
+
View,
|
|
83
|
+
{ style: { justifyContent: "center" } },
|
|
84
|
+
h(TextField, {
|
|
85
|
+
ref: setRef,
|
|
86
|
+
value,
|
|
87
|
+
onChangeText,
|
|
88
|
+
placeholder,
|
|
89
|
+
// The frame dims the whole control, glyph included.
|
|
90
|
+
...(disabled ? { editable: false, "aria-disabled": true } : null),
|
|
91
|
+
error: isShown(error),
|
|
92
|
+
...(hasNote({ helper, error }) ? { "aria-describedby": noteId } : null),
|
|
93
|
+
// `role` names it on the web; `accessibilityRole` on a device.
|
|
94
|
+
role: "searchbox",
|
|
95
|
+
accessibilityRole: "search",
|
|
96
|
+
accessibilityLabel: spokenLabel(accessibilityLabel, label) || placeholder,
|
|
97
|
+
enterKeyHint: "search",
|
|
98
|
+
autoCapitalize: "none",
|
|
99
|
+
autoCorrect: false,
|
|
100
|
+
testID,
|
|
101
|
+
...rest,
|
|
102
|
+
style: [
|
|
103
|
+
{ paddingLeft: ADORNMENT_WIDTH },
|
|
104
|
+
hasText && !disabled ? { paddingRight: ADORNMENT_WIDTH } : null,
|
|
105
|
+
style,
|
|
106
|
+
],
|
|
107
|
+
}),
|
|
108
|
+
h(
|
|
109
|
+
View,
|
|
110
|
+
{
|
|
111
|
+
style: {
|
|
112
|
+
position: "absolute",
|
|
113
|
+
left: 0,
|
|
114
|
+
top: 0,
|
|
115
|
+
bottom: 0,
|
|
116
|
+
width: ADORNMENT_WIDTH,
|
|
117
|
+
alignItems: "center",
|
|
118
|
+
justifyContent: "center",
|
|
119
|
+
pointerEvents: "none",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
h(Icon, { name: "search", size: GLYPH_SIZE, color: t.placeholderColor }),
|
|
123
|
+
),
|
|
124
|
+
hasText && !disabled
|
|
125
|
+
? h(
|
|
126
|
+
Pressable,
|
|
127
|
+
{
|
|
128
|
+
onPress: clear,
|
|
129
|
+
accessibilityRole: "button",
|
|
130
|
+
accessibilityLabel: clearAccessibilityLabel,
|
|
131
|
+
testID: testID ? `${testID}-clear` : undefined,
|
|
132
|
+
style: ({ pressed }) => [
|
|
133
|
+
{
|
|
134
|
+
position: "absolute",
|
|
135
|
+
right: 2,
|
|
136
|
+
top: 0,
|
|
137
|
+
bottom: 0,
|
|
138
|
+
minWidth: CLEAR_MIN_SIZE,
|
|
139
|
+
minHeight: CLEAR_MIN_SIZE,
|
|
140
|
+
alignItems: "center",
|
|
141
|
+
justifyContent: "center",
|
|
142
|
+
},
|
|
143
|
+
pressed ? { opacity: 0.6 } : null,
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
h(Icon, { name: "x", size: GLYPH_SIZE, color: t.placeholderColor }),
|
|
147
|
+
)
|
|
148
|
+
: null,
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
SearchField.displayName = "SearchField";
|
|
154
|
+
return SearchField;
|
|
155
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// `<SearchField>` (web). Binds ./search-field-view.js to react-native-web and
|
|
2
|
+
// the web `<TextField>`; see ./primitives.js for why not the bare specifier.
|
|
3
|
+
|
|
4
|
+
import * as ReactNative from "react-native-web";
|
|
5
|
+
import { makeSearchField } from "./search-field-view.js";
|
|
6
|
+
import { TextField } from "./text-field.js";
|
|
7
|
+
|
|
8
|
+
export const SearchField = makeSearchField({
|
|
9
|
+
View: ReactNative.View,
|
|
10
|
+
Text: ReactNative.Text,
|
|
11
|
+
Pressable: ReactNative.Pressable,
|
|
12
|
+
TextField,
|
|
13
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// `<SearchField>` (native). Binds ./search-field-view.js to React Native's own
|
|
2
|
+
// primitives and the native `<TextField>`; only the imports differ from
|
|
3
|
+
// ./search-field.js.
|
|
4
|
+
|
|
5
|
+
import { View, Text, Pressable } from "react-native";
|
|
6
|
+
import { makeSearchField } from "./search-field-view.js";
|
|
7
|
+
import { TextField } from "./text-field.native.js";
|
|
8
|
+
|
|
9
|
+
export const SearchField = makeSearchField({ View, Text, Pressable, TextField });
|