@scripso-homepad/ui 0.4.12 → 0.4.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scripso-homepad/ui",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "type": "module",
5
5
  "description": "Cross-platform UI components for Homepad (React Web + React Native)",
6
6
  "license": "MIT",
@@ -221,11 +221,12 @@ const styles = StyleSheet.create({
221
221
  },
222
222
  flag: {
223
223
  fontSize: 18,
224
- lineHeight: 22,
224
+ lineHeight: 24,
225
225
  },
226
226
  dialCode: {
227
227
  fontSize: fontSize.md,
228
228
  fontWeight: fontWeight.medium,
229
+ lineHeight: 19,
229
230
  },
230
231
  overlay: {
231
232
  flex: 1,
@@ -389,12 +389,12 @@ const styles = StyleSheet.create({
389
389
  },
390
390
  error: {
391
391
  fontSize: 12,
392
- lineHeight: 12,
392
+ lineHeight: 16,
393
393
  color: colors.inputError,
394
394
  },
395
395
  hint: {
396
396
  fontSize: 12,
397
- lineHeight: 12,
397
+ lineHeight: 16,
398
398
  color: colors.stormGray300,
399
399
  },
400
400
  });
@@ -68,6 +68,8 @@ export interface InputProps extends TextInputProps {
68
68
  /** Styles merged into the bordered field container. */
69
69
  fieldStyle?: StyleProp<ViewStyle>;
70
70
  labelClassName?: string;
71
+ /** Styles merged into the field label. */
72
+ labelStyles?: StyleProp<TextStyle>;
71
73
  inputClassName?: string;
72
74
  errorClassName?: string;
73
75
  hintClassName?: string;
@@ -88,6 +90,7 @@ export function Input({
88
90
  fieldClassName,
89
91
  fieldStyle,
90
92
  labelClassName,
93
+ labelStyles,
91
94
  inputClassName,
92
95
  errorClassName,
93
96
  hintClassName,
@@ -164,7 +167,11 @@ export function Input({
164
167
  return (
165
168
  <View ref={wrapperRef} style={[styles.wrapper, containerStyle]}>
166
169
  {label ? (
167
- <Label disabled={isDisabled} className={labelClassName}>
170
+ <Label
171
+ disabled={isDisabled}
172
+ className={labelClassName}
173
+ style={labelStyles}
174
+ >
168
175
  {label}
169
176
  </Label>
170
177
  ) : null}
@@ -32,6 +32,8 @@ export interface PhoneInputProps extends Omit<TextInputProps, "style"> {
32
32
  style?: StyleProp<TextStyle>;
33
33
  className?: string;
34
34
  labelClassName?: string;
35
+ /** Styles merged into the field label. */
36
+ labelStyles?: StyleProp<TextStyle>;
35
37
  inputClassName?: string;
36
38
  errorClassName?: string;
37
39
  hintClassName?: string;
@@ -47,6 +49,7 @@ export function PhoneInput({
47
49
  style,
48
50
  className,
49
51
  labelClassName,
52
+ labelStyles,
50
53
  inputClassName,
51
54
  errorClassName,
52
55
  hintClassName,
@@ -87,7 +90,11 @@ export function PhoneInput({
87
90
  return (
88
91
  <View ref={wrapperRef} style={[styles.wrapper, containerStyle]}>
89
92
  {label ? (
90
- <Label disabled={isDisabled} className={labelClassName}>
93
+ <Label
94
+ disabled={isDisabled}
95
+ className={labelClassName}
96
+ style={labelStyles}
97
+ >
91
98
  {label}
92
99
  </Label>
93
100
  ) : null}
@@ -98,39 +105,39 @@ export function PhoneInput({
98
105
  disabled={isDisabled}
99
106
  style={styles.countrySelector}
100
107
  />
101
- <View style={styles.phoneColumn}>
102
- <View style={[phoneFieldStyles.outline, styles.phoneOutline]}>
103
- <View
108
+ <View style={[phoneFieldStyles.outline, styles.phoneOutline]}>
109
+ <View
110
+ style={[
111
+ inputFieldMetrics.container,
112
+ phoneFieldStyles.container,
113
+ styles.phoneField,
114
+ ]}
115
+ >
116
+ <TextInput
117
+ ref={inputRef}
104
118
  style={[
105
- inputFieldMetrics.container,
106
- phoneFieldStyles.container,
107
- styles.phoneField,
119
+ inputFieldMetrics.input,
120
+ phoneFieldStyles.text,
121
+ style,
108
122
  ]}
109
- >
110
- <TextInput
111
- ref={inputRef}
112
- style={[
113
- inputFieldMetrics.input,
114
- phoneFieldStyles.text,
115
- style,
116
- ]}
117
- keyboardType="phone-pad"
118
- placeholderTextColor={phoneFieldStyles.placeholder}
119
- editable={editable}
120
- onFocus={handleFocus}
121
- onBlur={handleBlur}
122
- accessibilityState={{ disabled: isDisabled }}
123
- {...props}
124
- />
125
- </View>
123
+ keyboardType="phone-pad"
124
+ placeholderTextColor={
125
+ error ? colors.inputError : phoneFieldStyles.placeholder
126
+ }
127
+ editable={editable}
128
+ onFocus={handleFocus}
129
+ onBlur={handleBlur}
130
+ accessibilityState={{ disabled: isDisabled }}
131
+ {...props}
132
+ />
126
133
  </View>
127
- {helperMessage ? (
128
- <Text ref={helperRef} style={error ? styles.error : styles.hint}>
129
- {helperMessage}
130
- </Text>
131
- ) : null}
132
134
  </View>
133
135
  </View>
136
+ {helperMessage ? (
137
+ <Text ref={helperRef} style={error ? styles.error : styles.hint}>
138
+ {helperMessage}
139
+ </Text>
140
+ ) : null}
134
141
  </View>
135
142
  );
136
143
  }
@@ -149,12 +156,8 @@ const styles = StyleSheet.create({
149
156
  flexShrink: 0,
150
157
  alignSelf: "flex-start",
151
158
  },
152
- phoneColumn: {
153
- flex: 1,
154
- gap: spacing.sm,
155
- },
156
159
  phoneOutline: {
157
- width: "100%",
160
+ flex: 1,
158
161
  },
159
162
  phoneField: {
160
163
  flex: 1,
@@ -579,12 +579,12 @@ const styles = StyleSheet.create({
579
579
  },
580
580
  error: {
581
581
  fontSize: 12,
582
- lineHeight: 12,
582
+ lineHeight: 16,
583
583
  color: colors.inputError,
584
584
  },
585
585
  hint: {
586
586
  fontSize: 12,
587
- lineHeight: 12,
587
+ lineHeight: 16,
588
588
  color: colors.stormGray300,
589
589
  },
590
590
  });
package/src/index.ts CHANGED
@@ -82,6 +82,7 @@ export {
82
82
  radii,
83
83
  shadows,
84
84
  spacing,
85
+ typographyScale,
85
86
  } from "./theme/tokens";
86
87
  export type {
87
88
  EmeraldGreenStep,
@@ -54,7 +54,7 @@ export const calendarLabelTypography = {
54
54
  fontFamily: fonts.sans,
55
55
  fontSize: fontSize.sm,
56
56
  fontWeight: fontWeight.medium,
57
- lineHeight: fontSize.sm,
57
+ lineHeight: 16,
58
58
  letterSpacing: 0,
59
59
  color: colors.slateBlue,
60
60
  } as const;
@@ -63,7 +63,7 @@ export const calendarFieldTypography = {
63
63
  fontFamily: fonts.sans,
64
64
  fontSize: fontSize.md,
65
65
  fontWeight: fontWeight.regular,
66
- lineHeight: fontSize.md,
66
+ lineHeight: 19,
67
67
  letterSpacing: 0,
68
68
  textAlign: "left",
69
69
  } as const;
@@ -105,7 +105,7 @@ export const calendarDropdownMetrics = StyleSheet.create({
105
105
  fontFamily: fonts.sans,
106
106
  fontSize: fontSize.md,
107
107
  fontWeight: fontWeight.bold,
108
- lineHeight: 18,
108
+ lineHeight: 19,
109
109
  letterSpacing: 0,
110
110
  color: colors.stormGray850,
111
111
  },
@@ -148,7 +148,7 @@ export const calendarDropdownMetrics = StyleSheet.create({
148
148
  fontFamily: fonts.sans,
149
149
  fontSize: fontSize.sm,
150
150
  fontWeight: fontWeight.regular,
151
- lineHeight: fontSize.sm,
151
+ lineHeight: 16,
152
152
  letterSpacing: 0,
153
153
  textAlign: "center",
154
154
  color: colors.stormGray300,
@@ -192,7 +192,7 @@ export const calendarDropdownMetrics = StyleSheet.create({
192
192
  fontFamily: fonts.sans,
193
193
  fontSize: fontSize.md,
194
194
  fontWeight: fontWeight.regular,
195
- lineHeight: 18,
195
+ lineHeight: 19,
196
196
  letterSpacing: 0,
197
197
  textAlign: "center",
198
198
  color: colors.navy,
@@ -265,7 +265,7 @@ export const calendarDropdownMetrics = StyleSheet.create({
265
265
  fontFamily: fonts.sans,
266
266
  fontSize: fontSize.md,
267
267
  fontWeight: fontWeight.regular,
268
- lineHeight: 18,
268
+ lineHeight: 19,
269
269
  letterSpacing: 0,
270
270
  textAlign: "center",
271
271
  color: colors.navy,
@@ -216,6 +216,20 @@ export const fontSize = {
216
216
  xxl: 32,
217
217
  } as const;
218
218
 
219
+ /**
220
+ * Canonical Homepad type scale (matches `.typography-*` in `src/web/styles/typography.css`).
221
+ * font-size / line-height pairs:
222
+ * 12/16 · 14/19 · 18/24 · 20/27 · 24/25 · 32/34
223
+ */
224
+ export const typographyScale = {
225
+ 12: { fontSize: 12, lineHeight: 16 },
226
+ 14: { fontSize: 14, lineHeight: 19 },
227
+ 18: { fontSize: 18, lineHeight: 24 },
228
+ 20: { fontSize: 20, lineHeight: 27 },
229
+ 24: { fontSize: 24, lineHeight: 25 },
230
+ 32: { fontSize: 32, lineHeight: 34 },
231
+ } as const;
232
+
219
233
  export const fontWeight = {
220
234
  regular: "400" as const,
221
235
  medium: "500" as const,
@@ -227,12 +241,12 @@ export const fonts = {
227
241
  sans: "Noto Sans Armenian",
228
242
  } as const;
229
243
 
230
- /** Input label typography — matches Figma (Medium, 100% line-height). */
244
+ /** Input label typography — Medium, typography-12 scale. */
231
245
  export const labelTypography = {
232
246
  fontFamily: fonts.sans,
233
- fontSize: fontSize.sm,
247
+ fontSize: typographyScale[12].fontSize,
234
248
  fontWeight: fontWeight.medium,
235
- lineHeight: fontSize.sm,
249
+ lineHeight: typographyScale[12].lineHeight,
236
250
  letterSpacing: 0,
237
251
  } as const;
238
252
 
@@ -240,16 +254,16 @@ export const labelTypography = {
240
254
  export const buttonTypography = {
241
255
  lg: {
242
256
  fontFamily: fonts.sans,
243
- fontSize: 14,
257
+ fontSize: typographyScale[14].fontSize,
244
258
  fontWeight: fontWeight.semibold,
245
- lineHeight: 19,
259
+ lineHeight: typographyScale[14].lineHeight,
246
260
  letterSpacing: 0,
247
261
  },
248
262
  sm: {
249
263
  fontFamily: fonts.sans,
250
- fontSize: 12,
264
+ fontSize: typographyScale[12].fontSize,
251
265
  fontWeight: fontWeight.semibold,
252
- lineHeight: 16,
266
+ lineHeight: typographyScale[12].lineHeight,
253
267
  letterSpacing: 0,
254
268
  },
255
269
  } as const;
@@ -47,7 +47,7 @@ function DetailField({ label, value }: { label: string; value: string }) {
47
47
  <span className="min-w-0 flex-1 typography-14 font-medium tracking-normal text-storm-gray-500">
48
48
  {label}
49
49
  </span>
50
- <span className="shrink-0 text-right typography-14 font-medium leading-[14px] tracking-normal text-storm-gray-900">
50
+ <span className="shrink-0 text-right typography-14 font-medium tracking-normal text-storm-gray-900">
51
51
  {value}
52
52
  </span>
53
53
  </div>
@@ -31,6 +31,10 @@ export type TableActionsMenuProps = {
31
31
  triggerAriaLabel: string;
32
32
  className?: string;
33
33
  menuClassName?: string;
34
+ /** Dropdown menu width in pixels. Defaults to 231. Ignored when menuFitContent is true. */
35
+ menuWidth?: number;
36
+ /** Size the menu to its content width instead of a fixed pixel width. */
37
+ menuFitContent?: boolean;
34
38
  };
35
39
 
36
40
  type MenuPosition = {
@@ -38,13 +42,13 @@ type MenuPosition = {
38
42
  left: number;
39
43
  };
40
44
 
41
- function clampMenuLeft(left: number): number {
45
+ function clampMenuLeft(left: number, menuWidth: number): number {
42
46
  if (typeof window === 'undefined') {
43
47
  return left;
44
48
  }
45
49
 
46
50
  const minLeft = MENU_VIEWPORT_MARGIN_PX;
47
- const maxLeft = Math.max(minLeft, window.innerWidth - MENU_WIDTH_PX - MENU_VIEWPORT_MARGIN_PX);
51
+ const maxLeft = Math.max(minLeft, window.innerWidth - menuWidth - MENU_VIEWPORT_MARGIN_PX);
48
52
 
49
53
  return Math.min(Math.max(left, minLeft), maxLeft);
50
54
  }
@@ -69,6 +73,8 @@ export function TableActionsMenu({
69
73
  triggerAriaLabel,
70
74
  className,
71
75
  menuClassName,
76
+ menuWidth = MENU_WIDTH_PX,
77
+ menuFitContent = false,
72
78
  }: TableActionsMenuProps) {
73
79
  const [open, setOpen] = useState(false);
74
80
  const [menuPosition, setMenuPosition] = useState<MenuPosition | null>(null);
@@ -88,11 +94,15 @@ export function TableActionsMenu({
88
94
  }
89
95
 
90
96
  const rect = trigger.getBoundingClientRect();
97
+ const resolvedMenuWidth = menuFitContent
98
+ ? (menuRef.current?.getBoundingClientRect().width ?? menuWidth)
99
+ : menuWidth;
100
+
91
101
  setMenuPosition({
92
102
  top: rect.bottom + MENU_OFFSET_PX,
93
- left: clampMenuLeft(rect.right - MENU_WIDTH_PX),
103
+ left: clampMenuLeft(rect.right - resolvedMenuWidth, resolvedMenuWidth),
94
104
  });
95
- }, []);
105
+ }, [menuFitContent, menuWidth]);
96
106
 
97
107
  useOnClickOutside([rootRef, menuRef], closeMenu, open);
98
108
 
@@ -105,6 +115,14 @@ export function TableActionsMenu({
105
115
  updateMenuPosition();
106
116
  }, [open, updateMenuPosition]);
107
117
 
118
+ useIsomorphicLayoutEffect(() => {
119
+ if (!open || !menuFitContent) {
120
+ return;
121
+ }
122
+
123
+ updateMenuPosition();
124
+ }, [items, menuFitContent, open, updateMenuPosition]);
125
+
108
126
  useEffect(() => {
109
127
  if (!open) {
110
128
  return;
@@ -147,7 +165,7 @@ export function TableActionsMenu({
147
165
  position: 'fixed',
148
166
  top: menuPosition.top,
149
167
  left: menuPosition.left,
150
- width: MENU_WIDTH_PX,
168
+ ...(menuFitContent ? { width: 'max-content' } : { width: menuWidth }),
151
169
  }}
152
170
  className={cn(
153
171
  'z-50 overflow-hidden rounded-2xl border border-storm-gray-50 bg-white shadow-[0_4px_20px_0_rgba(0,0,0,0.05)]',
@@ -172,7 +190,8 @@ export function TableActionsMenu({
172
190
  item.onSelect();
173
191
  }}
174
192
  className={cn(
175
- 'flex h-[51px] w-full cursor-pointer items-center gap-2.5 px-3 text-left typography-14 font-medium text-storm-gray-900 transition-colors hover:bg-storm-gray-0',
193
+ 'flex h-[51px] cursor-pointer items-center gap-2.5 px-3 text-left typography-14 font-medium text-storm-gray-900 transition-colors hover:bg-storm-gray-0',
194
+ menuFitContent ? 'w-auto' : 'w-full',
176
195
  index > 0 && 'border-t border-storm-gray-50',
177
196
  item.disabled && 'cursor-not-allowed opacity-50',
178
197
  )}
@@ -47,7 +47,7 @@
47
47
  font-family: var(--font-sans, 'Noto Sans Armenian', ui-sans-serif, system-ui, sans-serif);
48
48
  font-size: 14px;
49
49
  font-weight: 700;
50
- line-height: 22px;
50
+ line-height: 19px;
51
51
  letter-spacing: 0;
52
52
  color: var(--color-slate-blue, #182e3c);
53
53
  }
@@ -56,7 +56,7 @@
56
56
  font-family: var(--font-sans, 'Noto Sans Armenian', ui-sans-serif, system-ui, sans-serif);
57
57
  font-size: 12px;
58
58
  font-weight: 400;
59
- line-height: 1.4;
59
+ line-height: 16px;
60
60
  letter-spacing: 0;
61
61
  color: var(--color-slate-blue, #182e3c);
62
62
  }
@@ -174,13 +174,14 @@ export function TableCell({
174
174
  ...props
175
175
  }: TableCellProps) {
176
176
  const disabled = useTableRowDisabled();
177
+ const resolvedVariant = disabled ? 'muted' : variant;
177
178
 
178
179
  return (
179
180
  <td
180
181
  className={cn(
181
182
  tableCellPaddingClassName,
182
183
  tableBodyClassName,
183
- tableCellVariantClasses[variant],
184
+ tableCellVariantClasses[resolvedVariant],
184
185
  disabled && tableRowDisabledCellClassName,
185
186
  tableAlignClasses[align],
186
187
  className,
@@ -197,6 +198,8 @@ export type TableActionsCellProps = TdHTMLAttributes<HTMLTableCellElement> & {
197
198
  };
198
199
 
199
200
  export function TableActionsCell({ className, children, ...props }: TableActionsCellProps) {
201
+ const disabled = useTableRowDisabled();
202
+
200
203
  return (
201
204
  <td
202
205
  className={cn(
@@ -204,6 +207,7 @@ export function TableActionsCell({ className, children, ...props }: TableActions
204
207
  tableCellPaddingClassName,
205
208
  tableBodyClassName,
206
209
  tableStickyRightShadowClassName,
210
+ disabled && tableRowDisabledCellClassName,
207
211
  className,
208
212
  )}
209
213
  {...props}
@@ -16,9 +16,9 @@ export function TableMenuActivateIcon({ className }: TableMenuActivateIconProps)
16
16
  className={cn('shrink-0', className)}
17
17
  >
18
18
  <path
19
- d="M14.1667 9.16699V6.66699C14.1667 4.3658 12.3012 2.50033 10 2.50033C7.69882 2.50033 5.83333 4.3658 5.83333 6.66699V9.16699M5.83333 9.16699H4.16667C3.24619 9.16699 2.5 9.91318 2.5 10.8337V16.667C2.5 17.5875 3.24619 18.3337 4.16667 18.3337H15.8333C16.7538 18.3337 17.5 17.5875 17.5 16.667V10.8337C17.5 9.91318 16.7538 9.16699 15.8333 9.16699H5.83333Z"
19
+ d="M5.83333 9.16631V5.83297C5.83229 4.79968 6.21523 3.80286 6.90781 3.03603C7.60039 2.2692 8.55319 1.78706 9.58125 1.68321C10.6093 1.57937 11.6393 1.86123 12.4712 2.47407C13.3032 3.08691 13.8777 3.98701 14.0833 4.99964M4.16667 9.16631H15.8333C16.7538 9.16631 17.5 9.9125 17.5 10.833V16.6663C17.5 17.5868 16.7538 18.333 15.8333 18.333H4.16667C3.24619 18.333 2.5 17.5868 2.5 16.6663V10.833C2.5 9.9125 3.24619 9.16631 4.16667 9.16631Z"
20
20
  stroke="#279D54"
21
- strokeWidth="1.75"
21
+ strokeWidth="2"
22
22
  strokeLinecap="round"
23
23
  strokeLinejoin="round"
24
24
  />
@@ -16,9 +16,9 @@ export function TableMenuEditIcon({ className }: TableMenuEditIconProps) {
16
16
  className={cn('shrink-0', className)}
17
17
  >
18
18
  <path
19
- d="M11.6667 3.33366L16.6667 8.33366M2.5 17.5003H5.83333L15.275 8.05866C15.5813 7.75244 15.7533 7.33612 15.7533 6.90233C15.7533 6.46854 15.5813 6.05222 15.275 5.746L14.2542 4.72533C13.9479 4.41911 13.5316 4.24707 13.0978 4.24707C12.664 4.24707 12.2477 4.41911 11.9415 4.72533L2.5 14.167V17.5003Z"
19
+ d="M10 2.50019H4.16667C3.72464 2.50019 3.30072 2.67578 2.98816 2.98834C2.67559 3.3009 2.5 3.72483 2.5 4.16686V15.8335C2.5 16.2755 2.67559 16.6995 2.98816 17.012C3.30072 17.3246 3.72464 17.5002 4.16667 17.5002H15.8333C16.2754 17.5002 16.6993 17.3246 17.0118 17.012C17.3244 16.6995 17.5 16.2755 17.5 15.8335V10.0002M15.3125 2.18769C15.644 1.85617 16.0937 1.66992 16.5625 1.66992C17.0313 1.66992 17.481 1.85617 17.8125 2.18769C18.144 2.51921 18.3303 2.96885 18.3303 3.43769C18.3303 3.90653 18.144 4.35617 17.8125 4.68769L10.3017 12.1994C10.1038 12.3971 9.85934 12.5418 9.59083 12.6202L7.19667 13.3202C7.12496 13.3411 7.04895 13.3424 6.97659 13.3238C6.90423 13.3053 6.83819 13.2676 6.78537 13.2148C6.73255 13.162 6.69491 13.096 6.67637 13.0236C6.65783 12.9512 6.65909 12.8752 6.68 12.8035L7.38 10.4094C7.45877 10.1411 7.60378 9.8969 7.80167 9.69936L15.3125 2.18769Z"
20
20
  stroke="#B5BCC1"
21
- strokeWidth="1.75"
21
+ strokeWidth="2"
22
22
  strokeLinecap="round"
23
23
  strokeLinejoin="round"
24
24
  />
@@ -16,16 +16,9 @@ export function TableMenuSuspendIcon({ className }: TableMenuSuspendIconProps) {
16
16
  className={cn('shrink-0', className)}
17
17
  >
18
18
  <path
19
- d="M10 18.3337C14.6024 18.3337 18.3333 14.6027 18.3333 10.0003C18.3333 5.39795 14.6024 1.66699 10 1.66699C5.39763 1.66699 1.66667 5.39795 1.66667 10.0003C1.66667 14.6027 5.39763 18.3337 10 18.3337Z"
19
+ d="M4.08464 4.08366L15.918 15.917M18.3346 10.0003C18.3346 14.6027 14.6037 18.3337 10.0013 18.3337C5.39893 18.3337 1.66797 14.6027 1.66797 10.0003C1.66797 5.39795 5.39893 1.66699 10.0013 1.66699C14.6037 1.66699 18.3346 5.39795 18.3346 10.0003Z"
20
20
  stroke="#AE0011"
21
- strokeWidth="1.75"
22
- strokeLinecap="round"
23
- strokeLinejoin="round"
24
- />
25
- <path
26
- d="M6.66667 10H13.3333"
27
- stroke="#AE0011"
28
- strokeWidth="1.75"
21
+ strokeWidth="2"
29
22
  strokeLinecap="round"
30
23
  strokeLinejoin="round"
31
24
  />
@@ -12,6 +12,7 @@ export type AppHeaderProps = {
12
12
  title: string;
13
13
  labels: AdminHeaderLabels;
14
14
  hasBuildingSelect?: boolean;
15
+ showSearch?: boolean;
15
16
  buildingOptions?: AdminBuildingOption[];
16
17
  selectedBuildingId?: string;
17
18
  className?: string;
@@ -46,6 +47,7 @@ export function AppHeader({
46
47
  title,
47
48
  labels,
48
49
  hasBuildingSelect = false,
50
+ showSearch = true,
49
51
  buildingOptions = [],
50
52
  selectedBuildingId = '',
51
53
  className,
@@ -88,17 +90,19 @@ export function AppHeader({
88
90
  />
89
91
  ) : null}
90
92
 
91
- <Input
92
- leftIcon={searchIcon ?? <Search strokeWidth={1.75} />}
93
- placeholder={labels.searchPlaceholder}
94
- value={searchValue}
95
- onChangeText={(value) => onSearchChange?.(value)}
96
- editable={!searchDisabled}
97
- className={cn('w-65!', searchClassName)}
98
- containerStyle={{ width: 260, maxWidth: 260, flexShrink: 0 }}
99
- fieldStyle={searchFieldStyle ?? defaultSearchFieldStyle}
100
- accessibilityLabel={labels.searchPlaceholder}
101
- />
93
+ {showSearch ? (
94
+ <Input
95
+ leftIcon={searchIcon ?? <Search strokeWidth={1.75} />}
96
+ placeholder={labels.searchPlaceholder}
97
+ value={searchValue}
98
+ onChangeText={(value) => onSearchChange?.(value)}
99
+ editable={!searchDisabled}
100
+ className={cn('w-65!', searchClassName)}
101
+ containerStyle={{ width: 260, maxWidth: 260, flexShrink: 0 }}
102
+ fieldStyle={searchFieldStyle ?? defaultSearchFieldStyle}
103
+ accessibilityLabel={labels.searchPlaceholder}
104
+ />
105
+ ) : null}
102
106
 
103
107
  <button
104
108
  type="button"
@@ -3,6 +3,8 @@ import { toast } from './toast';
3
3
  export type MutationToastMessages = {
4
4
  success: string;
5
5
  error: string;
6
+ successDescription?: string;
7
+ errorDescription?: string;
6
8
  };
7
9
 
8
10
  export type MutationToastResult = {
@@ -21,12 +23,22 @@ export async function runMutationWithToast(
21
23
  ): Promise<MutationToastResult> {
22
24
  try {
23
25
  await action();
24
- toast.success(messages.success);
26
+
27
+ if (messages.successDescription) {
28
+ toast.success(messages.success, { description: messages.successDescription });
29
+ } else {
30
+ toast.success(messages.success);
31
+ }
25
32
 
26
33
  return { success: true };
27
34
  } catch (error) {
28
35
  const errorMessage = options?.getErrorMessage?.(error) ?? messages.error;
29
- toast.error(errorMessage);
36
+
37
+ if (messages.errorDescription) {
38
+ toast.error(errorMessage, { description: messages.errorDescription });
39
+ } else {
40
+ toast.error(errorMessage);
41
+ }
30
42
 
31
43
  return { success: false, errorMessage };
32
44
  }