react-native-terra-ui 0.11.4 → 0.11.5
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/CHANGELOG.md +8 -0
- package/lib/module/components/header/utils.js +20 -0
- package/lib/module/components/header/utils.js.map +1 -0
- package/lib/module/components/header/variants/LargeTitleHeader/index.js +6 -5
- package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
- package/lib/module/components/header/variants/TitleHeader/index.js +4 -3
- package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/input/Input.js +32 -0
- package/lib/module/components/input/Input.js.map +1 -0
- package/lib/module/components/input/index.js +5 -0
- package/lib/module/components/input/index.js.map +1 -0
- package/lib/module/components/input/parts/InputField.js +161 -0
- package/lib/module/components/input/parts/InputField.js.map +1 -0
- package/lib/module/components/input/types.js +4 -0
- package/lib/module/components/input/types.js.map +1 -0
- package/lib/module/components/input/variants/SearchInput/index.js +100 -0
- package/lib/module/components/input/variants/SearchInput/index.js.map +1 -0
- package/lib/module/components/toolbar/Toolbar.js +12 -5
- package/lib/module/components/toolbar/Toolbar.js.map +1 -1
- package/lib/module/theme/defaults/icons.js +21 -0
- package/lib/module/theme/defaults/icons.js.map +1 -1
- package/lib/typescript/src/components/header/utils.d.ts +14 -0
- package/lib/typescript/src/components/header/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +6 -1
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/index.d.ts +1 -0
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input/Input.d.ts +28 -0
- package/lib/typescript/src/components/input/Input.d.ts.map +1 -0
- package/lib/typescript/src/components/input/index.d.ts +4 -0
- package/lib/typescript/src/components/input/index.d.ts.map +1 -0
- package/lib/typescript/src/components/input/parts/InputField.d.ts +8 -0
- package/lib/typescript/src/components/input/parts/InputField.d.ts.map +1 -0
- package/lib/typescript/src/components/input/types.d.ts +29 -0
- package/lib/typescript/src/components/input/types.d.ts.map +1 -0
- package/lib/typescript/src/components/input/variants/SearchInput/index.d.ts +24 -0
- package/lib/typescript/src/components/input/variants/SearchInput/index.d.ts.map +1 -0
- package/lib/typescript/src/components/toolbar/Toolbar.d.ts +8 -3
- package/lib/typescript/src/components/toolbar/Toolbar.d.ts.map +1 -1
- package/lib/typescript/src/theme/defaults/icons.d.ts.map +1 -1
- package/lib/typescript/src/theme/types.d.ts +1 -1
- package/lib/typescript/src/theme/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/header/utils.ts +19 -0
- package/src/components/header/variants/LargeTitleHeader/index.tsx +27 -13
- package/src/components/header/variants/TitleHeader/index.tsx +13 -3
- package/src/components/index.ts +1 -0
- package/src/components/input/Input.tsx +36 -0
- package/src/components/input/index.ts +3 -0
- package/src/components/input/parts/InputField.tsx +138 -0
- package/src/components/input/types.ts +31 -0
- package/src/components/input/variants/SearchInput/index.tsx +118 -0
- package/src/components/toolbar/Toolbar.tsx +17 -7
- package/src/theme/defaults/icons.tsx +13 -0
- package/src/theme/types.ts +1 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { type ComponentRef, forwardRef, useCallback, useState } from "react";
|
|
2
|
+
import { TextInput, View } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
5
|
+
|
|
6
|
+
import type { ColorToken, RadiusKey } from "#theme/types";
|
|
7
|
+
import { resolveThemeColor } from "#utils/resolve-theme-color";
|
|
8
|
+
import { FONT_WEIGHT_VALUE } from "#utils/typography";
|
|
9
|
+
import type { InputBaseProps, InputSize } from "../types";
|
|
10
|
+
|
|
11
|
+
/** Typographic role of the text, per size. */
|
|
12
|
+
const TEXT_VARIANT: Record<InputSize, "body-sm" | "body-md" | "body-lg"> = {
|
|
13
|
+
sm: "body-sm",
|
|
14
|
+
md: "body-md",
|
|
15
|
+
lg: "body-lg",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The field every input in the family renders: a themed container with optional
|
|
20
|
+
* leading and trailing slots around a native `TextInput`. Internal — consumers
|
|
21
|
+
* reach it through `Input` or one of its variants.
|
|
22
|
+
*/
|
|
23
|
+
export const InputField = forwardRef<ComponentRef<typeof TextInput>, InputBaseProps>(function InputField(
|
|
24
|
+
{
|
|
25
|
+
size = "md",
|
|
26
|
+
isDisabled = false,
|
|
27
|
+
isInvalid = false,
|
|
28
|
+
leading,
|
|
29
|
+
trailing,
|
|
30
|
+
background = "surface.sunken",
|
|
31
|
+
radius = "lg",
|
|
32
|
+
style,
|
|
33
|
+
onFocus,
|
|
34
|
+
onBlur,
|
|
35
|
+
...rest
|
|
36
|
+
},
|
|
37
|
+
ref
|
|
38
|
+
) {
|
|
39
|
+
// The focus ring is a style decision, so it stays a variant; only the
|
|
40
|
+
// placeholder colour has to leave the stylesheet, because `TextInput` takes it
|
|
41
|
+
// as a prop rather than as a style.
|
|
42
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
43
|
+
const { theme } = useUnistyles();
|
|
44
|
+
|
|
45
|
+
const handleFocus = useCallback<NonNullable<InputBaseProps["onFocus"]>>(
|
|
46
|
+
(event) => {
|
|
47
|
+
setIsFocused(true);
|
|
48
|
+
onFocus?.(event);
|
|
49
|
+
},
|
|
50
|
+
[onFocus]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const handleBlur = useCallback<NonNullable<InputBaseProps["onBlur"]>>(
|
|
54
|
+
(event) => {
|
|
55
|
+
setIsFocused(false);
|
|
56
|
+
onBlur?.(event);
|
|
57
|
+
},
|
|
58
|
+
[onBlur]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
styles.useVariants({ size, disabled: isDisabled, invalid: isInvalid, focused: isFocused && !isDisabled });
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<View style={[styles.container, styles.fill(background, radius), style]}>
|
|
65
|
+
{leading}
|
|
66
|
+
<TextInput
|
|
67
|
+
ref={ref}
|
|
68
|
+
editable={!isDisabled}
|
|
69
|
+
placeholderTextColor={theme.color["text.tertiary"]}
|
|
70
|
+
style={styles.input}
|
|
71
|
+
onFocus={handleFocus}
|
|
72
|
+
onBlur={handleBlur}
|
|
73
|
+
{...rest}
|
|
74
|
+
/>
|
|
75
|
+
{trailing}
|
|
76
|
+
</View>
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const styles = StyleSheet.create((theme) => ({
|
|
81
|
+
container: {
|
|
82
|
+
flexDirection: "row",
|
|
83
|
+
alignItems: "center",
|
|
84
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
85
|
+
borderColor: theme.color["border.subtle"],
|
|
86
|
+
variants: {
|
|
87
|
+
size: {
|
|
88
|
+
sm: { height: 36, paddingHorizontal: theme.spacing["4"], gap: 6 },
|
|
89
|
+
md: { height: 44, paddingHorizontal: theme.spacing["4"], gap: 6 },
|
|
90
|
+
lg: { height: 52, paddingHorizontal: theme.spacing["4"], gap: 6 },
|
|
91
|
+
},
|
|
92
|
+
focused: {
|
|
93
|
+
true: { borderColor: theme.color["accent.border"] },
|
|
94
|
+
false: {},
|
|
95
|
+
},
|
|
96
|
+
invalid: {
|
|
97
|
+
true: { borderColor: theme.color["danger.text"] },
|
|
98
|
+
false: {},
|
|
99
|
+
},
|
|
100
|
+
disabled: {
|
|
101
|
+
true: { opacity: theme.opacity.disabled },
|
|
102
|
+
false: {},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
// `background` is an open `ColorToken` and `radius` an eight-step scale, so
|
|
107
|
+
// both resolve in a dynamic function rather than enumerating as variants.
|
|
108
|
+
fill: (background: ColorToken, radius: RadiusKey) => ({
|
|
109
|
+
backgroundColor: resolveThemeColor(background, theme),
|
|
110
|
+
borderRadius: theme.radius[radius],
|
|
111
|
+
}),
|
|
112
|
+
input: {
|
|
113
|
+
flex: 1,
|
|
114
|
+
// Android centres text in a taller box by default; zeroing the padding lets
|
|
115
|
+
// the container's own height own the geometry on both platforms.
|
|
116
|
+
paddingVertical: 0,
|
|
117
|
+
color: theme.color["text.primary"],
|
|
118
|
+
variants: {
|
|
119
|
+
size: {
|
|
120
|
+
sm: {
|
|
121
|
+
fontSize: theme.typography.variants[TEXT_VARIANT.sm].fontSize,
|
|
122
|
+
fontWeight: FONT_WEIGHT_VALUE[theme.typography.variants[TEXT_VARIANT.sm].fontWeight],
|
|
123
|
+
},
|
|
124
|
+
md: {
|
|
125
|
+
fontSize: theme.typography.variants[TEXT_VARIANT.md].fontSize,
|
|
126
|
+
fontWeight: FONT_WEIGHT_VALUE[theme.typography.variants[TEXT_VARIANT.md].fontWeight],
|
|
127
|
+
},
|
|
128
|
+
lg: {
|
|
129
|
+
fontSize: theme.typography.variants[TEXT_VARIANT.lg].fontSize,
|
|
130
|
+
fontWeight: FONT_WEIGHT_VALUE[theme.typography.variants[TEXT_VARIANT.lg].fontWeight],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
focused: { true: {}, false: {} },
|
|
134
|
+
invalid: { true: {}, false: {} },
|
|
135
|
+
disabled: { true: {}, false: {} },
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
}));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { StyleProp, TextInputProps, ViewStyle } from "react-native";
|
|
3
|
+
|
|
4
|
+
import type { ColorToken, RadiusKey } from "#theme/types";
|
|
5
|
+
|
|
6
|
+
/** Field height and typography step. */
|
|
7
|
+
export type InputSize = "sm" | "md" | "lg";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Props shared by every input in the family. `style` reaches the field's
|
|
11
|
+
* container rather than the native `TextInput`, matching the rest of the
|
|
12
|
+
* library — the text itself is themed from `size`.
|
|
13
|
+
*/
|
|
14
|
+
export interface InputBaseProps extends Omit<TextInputProps, "style" | "editable"> {
|
|
15
|
+
/** Field height and text step. Defaults to `'md'`. */
|
|
16
|
+
size?: InputSize;
|
|
17
|
+
/** Blocks typing and dims the field. Defaults to `false`. */
|
|
18
|
+
isDisabled?: boolean;
|
|
19
|
+
/** Paints the border in the danger tone. Defaults to `false`. */
|
|
20
|
+
isInvalid?: boolean;
|
|
21
|
+
/** Content pinned before the text, e.g. an `Icon`. */
|
|
22
|
+
leading?: ReactNode;
|
|
23
|
+
/** Content pinned after the text, e.g. a unit or a button. */
|
|
24
|
+
trailing?: ReactNode;
|
|
25
|
+
/** Fill token. Defaults to `'surface.sunken'`. */
|
|
26
|
+
background?: ColorToken;
|
|
27
|
+
/** Corner radius. Defaults to `'md'`. */
|
|
28
|
+
radius?: RadiusKey;
|
|
29
|
+
/** Merged over the container's own style. */
|
|
30
|
+
style?: StyleProp<ViewStyle>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type ComponentRef, forwardRef, useCallback, useState } from "react";
|
|
2
|
+
import { Pressable, type TextInput } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
5
|
+
|
|
6
|
+
import { Icon } from "../../../icon";
|
|
7
|
+
import { InputField } from "../../parts/InputField";
|
|
8
|
+
import type { InputBaseProps } from "../../types";
|
|
9
|
+
|
|
10
|
+
/** Glyph size per field size, in dp. */
|
|
11
|
+
const ICON_SIZE = { sm: 16, md: 18, lg: 20 } as const;
|
|
12
|
+
|
|
13
|
+
/** Touch expansion around the clear button, in dp — it is smaller than a cell. */
|
|
14
|
+
const CLEAR_HIT_SLOP = 8;
|
|
15
|
+
|
|
16
|
+
export interface SearchInputProps extends Omit<InputBaseProps, "leading" | "trailing"> {
|
|
17
|
+
/**
|
|
18
|
+
* Called after the clear button empties the field. `onChangeText` fires with
|
|
19
|
+
* an empty string first, so a controlled field needs no extra wiring.
|
|
20
|
+
*/
|
|
21
|
+
onClear?: () => void;
|
|
22
|
+
/** Show the clear button once there is text. Defaults to `true`. */
|
|
23
|
+
isClearable?: boolean;
|
|
24
|
+
/** Accessible name of the clear button. Defaults to `'Clear search'`. */
|
|
25
|
+
clearAccessibilityLabel?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A search field: a leading magnifier, and a clear button that appears as soon
|
|
30
|
+
* as there is text. Controlled through `value` or left to manage its own, and
|
|
31
|
+
* it defaults to the search return key so the keyboard reads correctly.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <Input.Search value={query} onChangeText={setQuery} onSubmitEditing={run} />
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export const SearchInput = forwardRef<ComponentRef<typeof TextInput>, SearchInputProps>(function SearchInput(
|
|
39
|
+
{
|
|
40
|
+
value,
|
|
41
|
+
defaultValue,
|
|
42
|
+
onChangeText,
|
|
43
|
+
onClear,
|
|
44
|
+
isClearable = true,
|
|
45
|
+
clearAccessibilityLabel = "Clear search",
|
|
46
|
+
placeholder = "Search",
|
|
47
|
+
isDisabled = false,
|
|
48
|
+
size = "md",
|
|
49
|
+
radius = "full",
|
|
50
|
+
returnKeyType = "search",
|
|
51
|
+
clearButtonMode = "never",
|
|
52
|
+
...rest
|
|
53
|
+
},
|
|
54
|
+
ref
|
|
55
|
+
) {
|
|
56
|
+
// The clear button only exists while there is text, so an uncontrolled field
|
|
57
|
+
// still has to be tracked. That state also has to drive the native input:
|
|
58
|
+
// resetting it alone would hide the button while the text stayed on screen,
|
|
59
|
+
// so `TextInput` is always handed a `value`, seeded from `defaultValue` when
|
|
60
|
+
// the caller does not own one.
|
|
61
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? "");
|
|
62
|
+
const isControlled = value !== undefined;
|
|
63
|
+
const text = isControlled ? value : uncontrolledValue;
|
|
64
|
+
|
|
65
|
+
const handleChangeText = useCallback(
|
|
66
|
+
(next: string) => {
|
|
67
|
+
if (!isControlled) setUncontrolledValue(next);
|
|
68
|
+
onChangeText?.(next);
|
|
69
|
+
},
|
|
70
|
+
[isControlled, onChangeText]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const handleClear = useCallback(() => {
|
|
74
|
+
if (!isControlled) setUncontrolledValue("");
|
|
75
|
+
onChangeText?.("");
|
|
76
|
+
onClear?.();
|
|
77
|
+
}, [isControlled, onChangeText, onClear]);
|
|
78
|
+
|
|
79
|
+
const hasText = text.length > 0;
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<InputField
|
|
83
|
+
ref={ref}
|
|
84
|
+
size={size}
|
|
85
|
+
radius={radius}
|
|
86
|
+
isDisabled={isDisabled}
|
|
87
|
+
placeholder={placeholder}
|
|
88
|
+
returnKeyType={returnKeyType}
|
|
89
|
+
// iOS's native clear button would sit beside ours; ours is the
|
|
90
|
+
// cross-platform one, so the native affordance stays off by default.
|
|
91
|
+
clearButtonMode={clearButtonMode}
|
|
92
|
+
value={text}
|
|
93
|
+
onChangeText={handleChangeText}
|
|
94
|
+
leading={<Icon name="terra.search" size={ICON_SIZE[size]} color="text.tertiary" />}
|
|
95
|
+
trailing={
|
|
96
|
+
isClearable && hasText && !isDisabled ? (
|
|
97
|
+
<Pressable
|
|
98
|
+
accessibilityLabel={clearAccessibilityLabel}
|
|
99
|
+
accessibilityRole="button"
|
|
100
|
+
hitSlop={CLEAR_HIT_SLOP}
|
|
101
|
+
onPress={handleClear}
|
|
102
|
+
style={styles.clear}
|
|
103
|
+
>
|
|
104
|
+
<Icon name="terra.x" size={ICON_SIZE[size]} color="text.tertiary" />
|
|
105
|
+
</Pressable>
|
|
106
|
+
) : undefined
|
|
107
|
+
}
|
|
108
|
+
{...rest}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const styles = StyleSheet.create(() => ({
|
|
114
|
+
clear: {
|
|
115
|
+
alignItems: "center",
|
|
116
|
+
justifyContent: "center",
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
@@ -87,16 +87,21 @@ const LABEL_VARIANT = "label-md";
|
|
|
87
87
|
|
|
88
88
|
// ─── Button ───────────────────────────────────────────────────────────────────
|
|
89
89
|
|
|
90
|
-
interface ToolbarButtonBaseProps extends Omit<PressableProps, "children" | "style"> {
|
|
90
|
+
interface ToolbarButtonBaseProps extends Omit<PressableProps, "children" | "style" | "disabled"> {
|
|
91
91
|
/** Icon to render, before the label when both are given. */
|
|
92
92
|
icon?: TerraIconName;
|
|
93
93
|
/** Icon size in density-independent pixels. Defaults to `20`. */
|
|
94
94
|
iconSize?: number;
|
|
95
95
|
/**
|
|
96
96
|
* Foreground token for the icon and label. Defaults to the group's `tint`,
|
|
97
|
-
* or `text.primary` outside a group.
|
|
97
|
+
* or `text.primary` outside a group. Ignored while `isDisabled`.
|
|
98
98
|
*/
|
|
99
99
|
tint?: ColorToken;
|
|
100
|
+
/**
|
|
101
|
+
* Blocks presses and dims the cell, repainting the icon and label in
|
|
102
|
+
* `text.tertiary`. Defaults to `false`.
|
|
103
|
+
*/
|
|
104
|
+
isDisabled?: boolean;
|
|
100
105
|
/**
|
|
101
106
|
* Fill. Defaults to `surface.default` standalone, and to `transparent`
|
|
102
107
|
* inside a {@link ToolbarGroup}, which paints the shared capsule instead.
|
|
@@ -146,6 +151,7 @@ export function ToolbarButton({
|
|
|
146
151
|
children,
|
|
147
152
|
accessibilityLabel,
|
|
148
153
|
onPress,
|
|
154
|
+
isDisabled = false,
|
|
149
155
|
hitSlop = HIT_SLOP,
|
|
150
156
|
style,
|
|
151
157
|
...rest
|
|
@@ -154,7 +160,9 @@ export function ToolbarButton({
|
|
|
154
160
|
|
|
155
161
|
// Tokens, not resolved values: `Icon` and `Text` each resolve against the
|
|
156
162
|
// live theme, so they keep their own subscriptions.
|
|
157
|
-
|
|
163
|
+
// A disabled cell drops to the muted foreground, so an explicit `tint` — or a
|
|
164
|
+
// group's — cannot paint it as though it were still actionable.
|
|
165
|
+
const fg = isDisabled ? "text.tertiary" : (tint ?? groupTint);
|
|
158
166
|
const bg = background ?? groupBackground;
|
|
159
167
|
const shape = Children.count(children) > 0 ? "label" : "icon";
|
|
160
168
|
// A grouped cell normally shrinks to its glyph, which is invisible while it is
|
|
@@ -177,12 +185,14 @@ export function ToolbarButton({
|
|
|
177
185
|
<Pressable
|
|
178
186
|
accessibilityLabel={accessibilityLabel}
|
|
179
187
|
accessibilityRole="button"
|
|
188
|
+
accessibilityState={{ disabled: isDisabled }}
|
|
189
|
+
disabled={isDisabled}
|
|
180
190
|
hitSlop={hitSlop}
|
|
181
|
-
onPress={onPress}
|
|
191
|
+
onPress={isDisabled ? undefined : onPress}
|
|
182
192
|
{...rest}
|
|
183
193
|
style={(state) => [
|
|
184
194
|
cell,
|
|
185
|
-
styles.fill(bg, state.pressed),
|
|
195
|
+
styles.fill(bg, state.pressed, isDisabled),
|
|
186
196
|
typeof style === "function" ? style(state) : style,
|
|
187
197
|
]}
|
|
188
198
|
>
|
|
@@ -393,8 +403,8 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
393
403
|
},
|
|
394
404
|
// Split from `button` so the open-ended fill and the press state stay in the
|
|
395
405
|
// stylesheet without turning the geometry into a dynamic function too.
|
|
396
|
-
fill: (background: ColorToken, pressed: boolean) => ({
|
|
406
|
+
fill: (background: ColorToken, pressed: boolean, isDisabled: boolean) => ({
|
|
397
407
|
backgroundColor: resolveThemeColor(background, theme),
|
|
398
|
-
opacity: pressed ? theme.opacity.pressed : 1,
|
|
408
|
+
opacity: isDisabled ? theme.opacity.disabled : pressed ? theme.opacity.pressed : 1,
|
|
399
409
|
}),
|
|
400
410
|
}));
|
|
@@ -126,12 +126,25 @@ export const PersonIcon: TerraIconComponent = (props) => {
|
|
|
126
126
|
);
|
|
127
127
|
};
|
|
128
128
|
|
|
129
|
+
const SearchIcon: TerraIconComponent = (props) => {
|
|
130
|
+
const { color, size, strokeWidth } = resolveIconProps(props);
|
|
131
|
+
const stroke = strokeProps(color, strokeWidth);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<SvgIcon size={size}>
|
|
135
|
+
<Circle cx={11} cy={11} r={7} {...stroke} />
|
|
136
|
+
<Path d="m20 20-3.9-3.9" {...stroke} />
|
|
137
|
+
</SvgIcon>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
129
141
|
export const defaultIcons: Record<TerraSemanticIconName, TerraIconComponent> = {
|
|
130
142
|
"terra.chevron-left": ChevronLeftIcon,
|
|
131
143
|
"terra.chevron-right": ChevronRightIcon,
|
|
132
144
|
"terra.x": XIcon,
|
|
133
145
|
"terra.check": CheckIcon,
|
|
134
146
|
"terra.person": PersonIcon,
|
|
147
|
+
"terra.search": SearchIcon,
|
|
135
148
|
"status.info": InfoIcon,
|
|
136
149
|
"status.success": SuccessIcon,
|
|
137
150
|
"status.warning": WarningIcon,
|