@jobber/components-native 0.85.1-JOB-124062-569fcb4.27 → 0.85.1-JOB-136074-0fe613a.28

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.
@@ -1,9 +1,9 @@
1
- import type { DeepPartial, FieldValues } from "react-hook-form";
1
+ import type { FieldValues } from "react-hook-form";
2
2
  interface FormCacheProps<T extends FieldValues> {
3
3
  readonly localCacheId?: string | string[];
4
4
  readonly localCacheKey?: string;
5
5
  readonly localCacheExclude?: string[];
6
- readonly setLocalCache: (data: DeepPartial<T>) => void;
6
+ readonly setLocalCache: (data: T) => void;
7
7
  }
8
8
  export declare function FormCache<T extends FieldValues>({ localCacheExclude, localCacheKey, setLocalCache, }: FormCacheProps<T>): JSX.Element;
9
9
  export {};
@@ -1,5 +1,5 @@
1
1
  import type { MutableRefObject } from "react";
2
- import type { DeepPartial, FieldValues, UseFormReturn } from "react-hook-form";
2
+ import type { FieldValues, UseFormReturn } from "react-hook-form";
3
3
  export interface UseConfirmBeforeBackProps {
4
4
  alwaysPreventBack: boolean;
5
5
  onAcceptEvent?: () => void;
@@ -18,7 +18,7 @@ interface LocalCacheOptions {
18
18
  export interface AtlantisFormContextProps {
19
19
  useConfirmBeforeBack: (props: UseConfirmBeforeBackProps) => MutableRefObject<() => void>;
20
20
  useInternalFormLocalCache: <TData extends FieldValues>(formMethods: UseFormReturn<TData>, cacheKey?: string, options?: LocalCacheOptions) => {
21
- setLocalCache: (data: DeepPartial<TData>) => void;
21
+ setLocalCache: (data: TData) => void;
22
22
  removeLocalCache: () => void;
23
23
  };
24
24
  }
@@ -1,4 +1,4 @@
1
- import type { DeepPartial, FieldValues, UseFormHandleSubmit, UseFormReturn } from "react-hook-form";
1
+ import type { FieldValues, UseFormHandleSubmit, UseFormReturn } from "react-hook-form";
2
2
  import type { MutableRefObject, RefObject } from "react";
3
3
  import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
4
4
  import type { InternalFormProps } from "../types";
@@ -13,7 +13,7 @@ interface UseInternalForm<T extends FieldValues> {
13
13
  readonly isSubmitting: boolean;
14
14
  readonly isDirty: boolean;
15
15
  readonly removeListenerRef: MutableRefObject<() => void>;
16
- readonly setLocalCache: (data: DeepPartial<T>) => void;
16
+ readonly setLocalCache: (data: T) => void;
17
17
  }
18
18
  export declare function useInternalForm<T extends FieldValues, SubmitResponseType>({ mode, reValidateMode, initialValues, formRef, localCacheKey, localCacheId, scrollViewRef, saveButtonHeight, messageBannerHeight, }: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T>;
19
19
  export {};
@@ -1,8 +1,8 @@
1
1
  import type { MutableRefObject, RefObject } from "react";
2
- import type { ControllerProps, DefaultValues, FieldPath, FieldValues, Mode, UseFormReturn } from "react-hook-form";
2
+ import type { ControllerProps, DeepPartial, FieldPath, FieldValues, Mode, UnpackNestedValue, UseFormReturn } from "react-hook-form";
3
3
  import type { IconNames } from "@jobber/design";
4
4
  import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
5
- export type FormValues<T> = T;
5
+ export type FormValues<T> = UnpackNestedValue<T>;
6
6
  export type FormErrors = FormNetworkErrors | FormUserErrors;
7
7
  export type FormBannerMessage = FormWarningMessage | FormNoticeMessage;
8
8
  export declare enum FormSubmitErrorType {
@@ -81,7 +81,7 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
81
81
  * The initial values of the form inputs
82
82
  * This should be available as soon as initialLoading is set to false
83
83
  */
84
- initialValues?: DefaultValues<T>;
84
+ initialValues?: FormValues<DeepPartial<T>>;
85
85
  /**
86
86
  * When the validation should happen.
87
87
  * Possible values are "onBlur", "onChange", "onSubmit", "onTouched", and "all".
@@ -0,0 +1 @@
1
+ export declare function isEdgeToEdgeEnabled(): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.85.1-JOB-124062-569fcb4.27+569fcb4b6",
3
+ "version": "0.85.1-JOB-136074-0fe613a.28+0fe613a84",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  "autolinker": "^4.0.0",
44
44
  "deepmerge": "^4.2.2",
45
45
  "lodash": "^4.17.21",
46
- "react-hook-form": "^7.52.0",
46
+ "react-hook-form": "^7.30.0",
47
47
  "react-intl": "^6.4.2",
48
48
  "react-native-keyboard-aware-scroll-view": "^0.9.5",
49
49
  "react-native-modalize": "^2.0.13",
@@ -86,6 +86,7 @@
86
86
  "react": "^18.2.0",
87
87
  "react-intl": "^6.4.2",
88
88
  "react-native": ">=0.76.0",
89
+ "react-native-build-config": "^0.3.2",
89
90
  "react-native-gesture-handler": ">=2.10.0",
90
91
  "react-native-keyboard-aware-scroll-view": "^0.9.5",
91
92
  "react-native-modal-datetime-picker": " >=13.0.0",
@@ -95,5 +96,10 @@
95
96
  "react-native-safe-area-context": "^5.4.0",
96
97
  "react-native-svg": ">=12.0.0"
97
98
  },
98
- "gitHead": "569fcb4b6d2ef0068470ad901519b1a01de00ba5"
99
+ "peerDependenciesMeta": {
100
+ "react-native-build-config": {
101
+ "optional": true
102
+ }
103
+ },
104
+ "gitHead": "0fe613a84c9d804a249c128d7cd35ea5a6cbda38"
99
105
  }
package/src/Form/Form.tsx CHANGED
@@ -26,6 +26,7 @@ import { useScrollToError } from "./hooks/useScrollToError";
26
26
  import { FormSaveButton } from "./components/FormSaveButton";
27
27
  import { useSaveButtonPosition } from "./hooks/useSaveButtonPosition";
28
28
  import { FormCache } from "./components/FormCache/FormCache";
29
+ import { isEdgeToEdgeEnabled } from "../utils/buildConfig/isEdgeToEdgeEnabled";
29
30
  import { InputAccessoriesProvider } from "../InputText";
30
31
  import { tokens } from "../utils/design";
31
32
  import { ErrorMessageProvider } from "../ErrorMessageWrapper";
@@ -134,6 +135,9 @@ function InternalForm<T extends FieldValues, S>({
134
135
 
135
136
  const styles = useStyles();
136
137
 
138
+ // Check if edge-to-edge is enabled using utility function
139
+ const edgeToEdgeEnabled = isEdgeToEdgeEnabled();
140
+
137
141
  return (
138
142
  <FormProvider {...formMethods}>
139
143
  <>
@@ -161,6 +165,7 @@ function InternalForm<T extends FieldValues, S>({
161
165
  <KeyboardAwareScrollView
162
166
  enableResetScrollToCoords={false}
163
167
  enableAutomaticScroll={true}
168
+ enableOnAndroid={edgeToEdgeEnabled}
164
169
  keyboardOpeningTime={
165
170
  Platform.OS === "ios" ? tokens["timing-slowest"] : 0
166
171
  }
@@ -168,6 +173,7 @@ function InternalForm<T extends FieldValues, S>({
168
173
  ref={scrollViewRef}
169
174
  {...keyboardProps}
170
175
  extraHeight={headerHeight}
176
+ extraScrollHeight={edgeToEdgeEnabled ? 20 : 0}
171
177
  contentContainerStyle={
172
178
  !keyboardHeight && styles.scrollContentContainer
173
179
  }
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo } from "react";
2
- import type { DeepPartial, FieldValues } from "react-hook-form";
2
+ import type { FieldValues } from "react-hook-form";
3
3
  import { useFormContext, useWatch } from "react-hook-form";
4
4
  import omit from "lodash/omit";
5
5
 
@@ -7,7 +7,7 @@ interface FormCacheProps<T extends FieldValues> {
7
7
  readonly localCacheId?: string | string[];
8
8
  readonly localCacheKey?: string;
9
9
  readonly localCacheExclude?: string[];
10
- readonly setLocalCache: (data: DeepPartial<T>) => void;
10
+ readonly setLocalCache: (data: T) => void;
11
11
  }
12
12
 
13
13
  export function FormCache<T extends FieldValues>({
@@ -18,7 +18,7 @@ export function FormCache<T extends FieldValues>({
18
18
  const { control, formState } = useFormContext<T>();
19
19
  const { isDirty } = formState;
20
20
 
21
- const formData = useWatch({ control });
21
+ const formData = useWatch<T>({ control });
22
22
  const shouldExclude = useMemo(() => {
23
23
  return Array.isArray(localCacheExclude) && localCacheExclude.length > 0;
24
24
  }, [localCacheExclude]);
@@ -46,6 +46,5 @@ export function FormCache<T extends FieldValues>({
46
46
  }
47
47
  }, [formData, isDirty, localCacheExclude, setLocalCache, shouldExclude]);
48
48
 
49
- // eslint-disable-next-line react/jsx-no-useless-fragment
50
49
  return <></>;
51
50
  }
@@ -1,5 +1,5 @@
1
1
  import type { MutableRefObject } from "react";
2
- import type { DeepPartial, FieldValues, UseFormReturn } from "react-hook-form";
2
+ import type { FieldValues, UseFormReturn } from "react-hook-form";
3
3
 
4
4
  export interface UseConfirmBeforeBackProps {
5
5
  alwaysPreventBack: boolean;
@@ -27,7 +27,7 @@ export interface AtlantisFormContextProps {
27
27
  cacheKey?: string,
28
28
  options?: LocalCacheOptions,
29
29
  ) => {
30
- setLocalCache: (data: DeepPartial<TData>) => void;
30
+ setLocalCache: (data: TData) => void;
31
31
  removeLocalCache: () => void;
32
32
  };
33
33
  }
@@ -1,5 +1,4 @@
1
1
  import type {
2
- DeepPartial,
3
2
  FieldValues,
4
3
  UseFormHandleSubmit,
5
4
  UseFormReturn,
@@ -32,7 +31,7 @@ interface UseInternalForm<T extends FieldValues> {
32
31
  readonly isSubmitting: boolean;
33
32
  readonly isDirty: boolean;
34
33
  readonly removeListenerRef: MutableRefObject<() => void>;
35
- readonly setLocalCache: (data: DeepPartial<T>) => void;
34
+ readonly setLocalCache: (data: T) => void;
36
35
  }
37
36
 
38
37
  export function useInternalForm<T extends FieldValues, SubmitResponseType>({
package/src/Form/types.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import type { MutableRefObject, RefObject } from "react";
2
2
  import type {
3
3
  ControllerProps,
4
- DefaultValues,
4
+ DeepPartial,
5
5
  FieldPath,
6
6
  FieldValues,
7
7
  Mode,
8
+ UnpackNestedValue,
8
9
  UseFormReturn,
9
10
  } from "react-hook-form";
10
11
  import type { IconNames } from "@jobber/design";
11
12
  import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
12
13
 
13
- export type FormValues<T> = T;
14
+ export type FormValues<T> = UnpackNestedValue<T>;
14
15
  export type FormErrors = FormNetworkErrors | FormUserErrors;
15
16
  export type FormBannerMessage = FormWarningMessage | FormNoticeMessage;
16
17
 
@@ -108,7 +109,7 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
108
109
  * The initial values of the form inputs
109
110
  * This should be available as soon as initialLoading is set to false
110
111
  */
111
- initialValues?: DefaultValues<T>;
112
+ initialValues?: FormValues<DeepPartial<T>>;
112
113
 
113
114
  /**
114
115
  * When the validation should happen.
@@ -80,56 +80,6 @@ const getKeyboard = (props: InputCurrencyProps) => {
80
80
  }
81
81
  };
82
82
 
83
- const computeDisplayFromNumericInput = (
84
- numberedValue: number,
85
- decimalNumbers: string,
86
- decimalCount: number,
87
- maxLength: number,
88
- maxDecimalPlaces: number,
89
- decimalPlaces: number,
90
- formatNumber: (
91
- value: number,
92
- opts?: FormatNumberOptions | undefined,
93
- ) => string,
94
- ): {
95
- onChangeValue: number | string;
96
- displayValue: string;
97
- } => {
98
- const transformedValue = limitInputWholeDigits(numberedValue, maxLength);
99
- const stringValue =
100
- decimalNumbers !== ""
101
- ? transformedValue.toString() + "." + decimalNumbers.slice(1)
102
- : transformedValue.toString();
103
-
104
- if (checkLastChar(stringValue)) {
105
- const roundedDecimal = configureDecimal(
106
- decimalCount,
107
- maxDecimalPlaces,
108
- stringValue,
109
- decimalPlaces,
110
- );
111
- const internationalizedValueToDisplay = formatNumber(roundedDecimal, {
112
- maximumFractionDigits: maxDecimalPlaces,
113
- });
114
-
115
- return {
116
- onChangeValue: roundedDecimal,
117
- displayValue: internationalizedValueToDisplay,
118
- };
119
- } else {
120
- const internationalizedValueToDisplay =
121
- formatNumber(transformedValue, {
122
- maximumFractionDigits: maxDecimalPlaces,
123
- }) + decimalNumbers;
124
-
125
- return {
126
- onChangeValue:
127
- transformedValue.toString() + "." + decimalNumbers.slice(1),
128
- displayValue: internationalizedValueToDisplay,
129
- };
130
- }
131
- };
132
-
133
83
  export function InputCurrency(props: InputCurrencyProps): JSX.Element {
134
84
  const {
135
85
  showCurrencySymbol = true,
@@ -149,6 +99,54 @@ export function InputCurrency(props: InputCurrencyProps): JSX.Element {
149
99
  internalValue,
150
100
  );
151
101
 
102
+ const setOnChangeAndDisplayValues = (
103
+ onChangeValue: number | string | undefined,
104
+ valueToDisplay: string | undefined,
105
+ ) => {
106
+ props.onChange?.(onChangeValue);
107
+ setDisplayValue(valueToDisplay);
108
+ };
109
+
110
+ const checkDecimalAndI18nOfDisplayValue = (
111
+ numberedValue: number,
112
+ decimalNumbers: string,
113
+ decimalCount: number,
114
+ ) => {
115
+ const transformedValue = limitInputWholeDigits(numberedValue, maxLength);
116
+ const stringValue =
117
+ decimalNumbers !== ""
118
+ ? transformedValue.toString() + "." + decimalNumbers.slice(1)
119
+ : transformedValue.toString();
120
+
121
+ if (checkLastChar(stringValue)) {
122
+ const roundedDecimal = configureDecimal(
123
+ decimalCount,
124
+ maxDecimalPlaces,
125
+ stringValue,
126
+ decimalPlaces,
127
+ );
128
+ const internationalizedValueToDisplay = intl.formatNumber(
129
+ roundedDecimal,
130
+ {
131
+ maximumFractionDigits: maxDecimalPlaces,
132
+ },
133
+ );
134
+ setOnChangeAndDisplayValues(
135
+ roundedDecimal,
136
+ internationalizedValueToDisplay,
137
+ );
138
+ } else {
139
+ const internationalizedValueToDisplay =
140
+ intl.formatNumber(transformedValue, {
141
+ maximumFractionDigits: maxDecimalPlaces,
142
+ }) + decimalNumbers;
143
+ setOnChangeAndDisplayValues(
144
+ transformedValue.toString() + "." + decimalNumbers.slice(1),
145
+ internationalizedValueToDisplay,
146
+ );
147
+ }
148
+ };
149
+
152
150
  const handleChange = (newValue: string | undefined) => {
153
151
  const [decimalCount, wholeIntegerValue, decimalNumbers] = parseGivenInput(
154
152
  newValue,
@@ -160,37 +158,19 @@ export function InputCurrency(props: InputCurrencyProps): JSX.Element {
160
158
  : wholeIntegerValue;
161
159
 
162
160
  if (isValidNumber(numberedValue) && typeof numberedValue === "number") {
163
- const result = computeDisplayFromNumericInput(
161
+ checkDecimalAndI18nOfDisplayValue(
164
162
  numberedValue,
165
163
  decimalNumbers,
166
164
  decimalCount,
167
- maxLength,
168
- maxDecimalPlaces,
169
- decimalPlaces,
170
- intl.formatNumber,
171
165
  );
172
- const { onChangeValue, displayValue: valueToDisplay } = result;
173
- props.onChange?.(onChangeValue);
174
- setDisplayValue(valueToDisplay);
175
166
  } else {
176
167
  const value = numberedValue?.toString() + decimalNumbers;
177
- props.onChange?.(value);
178
- setDisplayValue(value);
168
+ setOnChangeAndDisplayValues(value, value);
179
169
  }
180
170
  };
181
171
 
182
172
  const { t } = useAtlantisI18n();
183
173
 
184
- const defaultValidations = {
185
- pattern: {
186
- value: NUMBER_VALIDATION_REGEX,
187
- message: t("errors.notANumber"),
188
- },
189
- } as const;
190
- const mergedValidations = props.validations
191
- ? Object.assign({}, defaultValidations, props.validations)
192
- : defaultValidations;
193
-
194
174
  return (
195
175
  <InputText
196
176
  {...props}
@@ -207,7 +187,13 @@ export function InputCurrency(props: InputCurrencyProps): JSX.Element {
207
187
  .replace(floatSeparators.decimal, ".");
208
188
  },
209
189
  }}
210
- validations={mergedValidations}
190
+ validations={{
191
+ pattern: {
192
+ value: NUMBER_VALIDATION_REGEX,
193
+ message: t("errors.notANumber"),
194
+ },
195
+ ...props.validations,
196
+ }}
211
197
  onBlur={() => {
212
198
  props.onBlur?.();
213
199
 
@@ -7,17 +7,6 @@ export const InputEmail = forwardRef(InputEmailInternal);
7
7
  type InputEmailProps = Omit<InputTextProps, "keyboard">;
8
8
 
9
9
  function InputEmailInternal(props: InputEmailProps, ref: Ref<InputTextRef>) {
10
- const defaultValidations = {
11
- pattern: {
12
- value:
13
- /[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?/,
14
- message: "Enter a valid email address (email@example.com)",
15
- },
16
- } as const;
17
- const mergedValidations = props.validations
18
- ? Object.assign({}, defaultValidations, props.validations)
19
- : defaultValidations;
20
-
21
10
  return (
22
11
  <InputText
23
12
  {...props}
@@ -25,7 +14,14 @@ function InputEmailInternal(props: InputEmailProps, ref: Ref<InputTextRef>) {
25
14
  autoCapitalize="none"
26
15
  autoCorrect={false}
27
16
  keyboard={"email-address"}
28
- validations={mergedValidations}
17
+ validations={{
18
+ pattern: {
19
+ value:
20
+ /[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?/,
21
+ message: "Enter a valid email address (email@example.com)",
22
+ },
23
+ ...props.validations,
24
+ }}
29
25
  />
30
26
  );
31
27
  }
@@ -46,16 +46,6 @@ function InputNumberInternal(props: InputNumberProps, ref: Ref<InputTextRef>) {
46
46
  const { inputTransform: convertToString, outputTransform: convertToNumber } =
47
47
  useNumberTransform(props.value);
48
48
 
49
- const defaultValidations = {
50
- pattern: {
51
- value: NUMBER_VALIDATION_REGEX,
52
- message: t("errors.notANumber"),
53
- },
54
- } as const;
55
- const mergedValidations = props.validations
56
- ? Object.assign({}, defaultValidations, props.validations)
57
- : defaultValidations;
58
-
59
49
  return (
60
50
  <InputText
61
51
  {...props}
@@ -68,7 +58,13 @@ function InputNumberInternal(props: InputNumberProps, ref: Ref<InputTextRef>) {
68
58
  value={props.value?.toString()}
69
59
  defaultValue={props.defaultValue?.toString()}
70
60
  onChangeText={handleChange}
71
- validations={mergedValidations}
61
+ validations={{
62
+ pattern: {
63
+ value: NUMBER_VALIDATION_REGEX,
64
+ message: t("errors.notANumber"),
65
+ },
66
+ ...props.validations,
67
+ }}
72
68
  />
73
69
  );
74
70
  }
@@ -4,7 +4,7 @@ import type {
4
4
  RegisterOptions,
5
5
  UseControllerReturn,
6
6
  } from "react-hook-form";
7
- import { get, useController, useForm, useFormContext } from "react-hook-form";
7
+ import { useController, useForm, useFormContext } from "react-hook-form";
8
8
  import { useState } from "react";
9
9
 
10
10
  interface UseFormControllerProps<T> {
@@ -44,11 +44,18 @@ export function useFormController<T>({
44
44
  });
45
45
 
46
46
  // The naming convention established by react-hook-form for arrays of fields is, for example, "emails.0.description".
47
- // Preserve original behavior: only treat three-part names as nested paths.
48
- // For anything else, perform a flat lookup to avoid behavioral changes.
49
- const identifiers = fieldName.split(".");
50
- const error =
51
- identifiers.length === 3 ? get(errors, fieldName) : errors[fieldName];
47
+ // This corresponds to the structure of the error object, e.g. { emails: { 0: { description: "foobar" } } }
48
+ // We assume here that fields will either follow this period-delimited three-part convention, or else that they are simple and indivisible (e.g. "city").
49
+ // TODO: Add support for two-part identifiers (e.g. "property.province")
50
+ const fieldIdentifiers = fieldName.split(".");
51
+ let error: FieldError | undefined;
52
+
53
+ if (fieldIdentifiers.length === 3) {
54
+ const [section, item, identifier] = fieldIdentifiers;
55
+ error = errors[section]?.[item]?.[identifier];
56
+ } else {
57
+ error = errors[fieldName];
58
+ }
52
59
 
53
60
  return { error, field };
54
61
  }
@@ -0,0 +1,7 @@
1
+ declare module "react-native-build-config" {
2
+ const BuildConfig: {
3
+ IS_EDGE_TO_EDGE_ENABLED?: boolean;
4
+ [k: string]: unknown;
5
+ };
6
+ export = BuildConfig;
7
+ }
@@ -0,0 +1,21 @@
1
+ import { Platform } from "react-native";
2
+
3
+ type BuildCfg = { IS_EDGE_TO_EDGE_ENABLED?: boolean } | undefined | null;
4
+
5
+ function loadBuildConfig(): BuildCfg {
6
+ try {
7
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
8
+ const mod = require("react-native-build-config");
9
+
10
+ return (mod?.default ?? mod) as BuildCfg;
11
+ } catch {
12
+ return null; // module not installed or not linked
13
+ }
14
+ }
15
+
16
+ export function isEdgeToEdgeEnabled(): boolean {
17
+ if (Platform.OS !== "android") return false;
18
+ const cfg = loadBuildConfig();
19
+
20
+ return !!cfg?.IS_EDGE_TO_EDGE_ENABLED;
21
+ }