@oxy.so/bloom 2.11.0 → 2.12.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.
Files changed (180) hide show
  1. package/docs/app-shell.mdx +90 -4
  2. package/docs/content-panel.mdx +65 -19
  3. package/docs/date-picker.mdx +80 -2
  4. package/docs/listing-card.mdx +54 -0
  5. package/docs/rating.mdx +52 -2
  6. package/docs/stay-search.mdx +38 -0
  7. package/lib/commonjs/app-shell/AppShell.js +121 -5
  8. package/lib/commonjs/app-shell/AppShell.js.map +1 -1
  9. package/lib/commonjs/app-shell/layout.js +10 -5
  10. package/lib/commonjs/app-shell/layout.js.map +1 -1
  11. package/lib/commonjs/content-panel/ContentPanel.js +8 -2
  12. package/lib/commonjs/content-panel/ContentPanel.js.map +1 -1
  13. package/lib/commonjs/content-panel/ContentPanel.web.js +38 -25
  14. package/lib/commonjs/content-panel/ContentPanel.web.js.map +1 -1
  15. package/lib/commonjs/date-picker/TimeField.js +175 -0
  16. package/lib/commonjs/date-picker/TimeField.js.map +1 -0
  17. package/lib/commonjs/date-picker/index.js +7 -0
  18. package/lib/commonjs/date-picker/index.js.map +1 -1
  19. package/lib/commonjs/date-picker/time.js +160 -0
  20. package/lib/commonjs/date-picker/time.js.map +1 -0
  21. package/lib/commonjs/index.js +7 -0
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/commonjs/index.web.js +7 -0
  24. package/lib/commonjs/index.web.js.map +1 -1
  25. package/lib/commonjs/listing-card/ListingCard.js +34 -1
  26. package/lib/commonjs/listing-card/ListingCard.js.map +1 -1
  27. package/lib/commonjs/listing-card/WishlistCard.js +50 -8
  28. package/lib/commonjs/listing-card/WishlistCard.js.map +1 -1
  29. package/lib/commonjs/listing-card/shared.js +23 -1
  30. package/lib/commonjs/listing-card/shared.js.map +1 -1
  31. package/lib/commonjs/rating/RatingInput.js +218 -0
  32. package/lib/commonjs/rating/RatingInput.js.map +1 -0
  33. package/lib/commonjs/rating/index.js +7 -0
  34. package/lib/commonjs/rating/index.js.map +1 -1
  35. package/lib/commonjs/stay-search/DestinationSuggestions.js +46 -9
  36. package/lib/commonjs/stay-search/DestinationSuggestions.js.map +1 -1
  37. package/lib/commonjs/stay-search/GuestPicker.js +9 -0
  38. package/lib/commonjs/stay-search/GuestPicker.js.map +1 -1
  39. package/lib/commonjs/track-list/parts.js +0 -13
  40. package/lib/commonjs/track-list/parts.js.map +1 -1
  41. package/lib/module/app-shell/AppShell.js +121 -5
  42. package/lib/module/app-shell/AppShell.js.map +1 -1
  43. package/lib/module/app-shell/layout.js +10 -5
  44. package/lib/module/app-shell/layout.js.map +1 -1
  45. package/lib/module/content-panel/ContentPanel.js +8 -2
  46. package/lib/module/content-panel/ContentPanel.js.map +1 -1
  47. package/lib/module/content-panel/ContentPanel.web.js +38 -25
  48. package/lib/module/content-panel/ContentPanel.web.js.map +1 -1
  49. package/lib/module/date-picker/TimeField.js +170 -0
  50. package/lib/module/date-picker/TimeField.js.map +1 -0
  51. package/lib/module/date-picker/index.js +3 -0
  52. package/lib/module/date-picker/index.js.map +1 -1
  53. package/lib/module/date-picker/time.js +151 -0
  54. package/lib/module/date-picker/time.js.map +1 -0
  55. package/lib/module/index.js +1 -1
  56. package/lib/module/index.js.map +1 -1
  57. package/lib/module/index.web.js +1 -1
  58. package/lib/module/index.web.js.map +1 -1
  59. package/lib/module/listing-card/ListingCard.js +34 -1
  60. package/lib/module/listing-card/ListingCard.js.map +1 -1
  61. package/lib/module/listing-card/WishlistCard.js +50 -8
  62. package/lib/module/listing-card/WishlistCard.js.map +1 -1
  63. package/lib/module/listing-card/shared.js +22 -0
  64. package/lib/module/listing-card/shared.js.map +1 -1
  65. package/lib/module/rating/RatingInput.js +212 -0
  66. package/lib/module/rating/RatingInput.js.map +1 -0
  67. package/lib/module/rating/index.js +1 -0
  68. package/lib/module/rating/index.js.map +1 -1
  69. package/lib/module/stay-search/DestinationSuggestions.js +45 -9
  70. package/lib/module/stay-search/DestinationSuggestions.js.map +1 -1
  71. package/lib/module/stay-search/GuestPicker.js +9 -0
  72. package/lib/module/stay-search/GuestPicker.js.map +1 -1
  73. package/lib/module/track-list/parts.js +1 -14
  74. package/lib/module/track-list/parts.js.map +1 -1
  75. package/lib/typescript/commonjs/app-shell/AppShell.d.ts.map +1 -1
  76. package/lib/typescript/commonjs/app-shell/layout.d.ts +5 -4
  77. package/lib/typescript/commonjs/app-shell/layout.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/app-shell/types.d.ts +12 -2
  79. package/lib/typescript/commonjs/app-shell/types.d.ts.map +1 -1
  80. package/lib/typescript/commonjs/content-panel/ContentPanel.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/content-panel/ContentPanel.web.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/content-panel/types.d.ts +53 -42
  83. package/lib/typescript/commonjs/content-panel/types.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/date-picker/TimeField.d.ts +6 -0
  85. package/lib/typescript/commonjs/date-picker/TimeField.d.ts.map +1 -0
  86. package/lib/typescript/commonjs/date-picker/index.d.ts +2 -1
  87. package/lib/typescript/commonjs/date-picker/index.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/date-picker/time.d.ts +56 -0
  89. package/lib/typescript/commonjs/date-picker/time.d.ts.map +1 -0
  90. package/lib/typescript/commonjs/date-picker/types.d.ts +49 -3
  91. package/lib/typescript/commonjs/date-picker/types.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/index.d.ts +2 -2
  93. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  94. package/lib/typescript/commonjs/index.web.d.ts +2 -2
  95. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/listing-card/ListingCard.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/listing-card/WishlistCard.d.ts +1 -1
  98. package/lib/typescript/commonjs/listing-card/WishlistCard.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/listing-card/shared.d.ts +8 -1
  100. package/lib/typescript/commonjs/listing-card/shared.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/listing-card/types.d.ts +37 -0
  102. package/lib/typescript/commonjs/listing-card/types.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/rating/RatingInput.d.ts +8 -0
  104. package/lib/typescript/commonjs/rating/RatingInput.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/rating/index.d.ts +2 -1
  106. package/lib/typescript/commonjs/rating/index.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/rating/types.d.ts +31 -0
  108. package/lib/typescript/commonjs/rating/types.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/stay-search/DestinationSuggestions.d.ts +8 -0
  110. package/lib/typescript/commonjs/stay-search/DestinationSuggestions.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/stay-search/GuestPicker.d.ts +6 -1
  112. package/lib/typescript/commonjs/stay-search/GuestPicker.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/stay-search/types.d.ts +20 -0
  114. package/lib/typescript/commonjs/stay-search/types.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/track-list/parts.d.ts.map +1 -1
  116. package/lib/typescript/module/app-shell/AppShell.d.ts.map +1 -1
  117. package/lib/typescript/module/app-shell/layout.d.ts +5 -4
  118. package/lib/typescript/module/app-shell/layout.d.ts.map +1 -1
  119. package/lib/typescript/module/app-shell/types.d.ts +12 -2
  120. package/lib/typescript/module/app-shell/types.d.ts.map +1 -1
  121. package/lib/typescript/module/content-panel/ContentPanel.d.ts.map +1 -1
  122. package/lib/typescript/module/content-panel/ContentPanel.web.d.ts.map +1 -1
  123. package/lib/typescript/module/content-panel/types.d.ts +53 -42
  124. package/lib/typescript/module/content-panel/types.d.ts.map +1 -1
  125. package/lib/typescript/module/date-picker/TimeField.d.ts +6 -0
  126. package/lib/typescript/module/date-picker/TimeField.d.ts.map +1 -0
  127. package/lib/typescript/module/date-picker/index.d.ts +2 -1
  128. package/lib/typescript/module/date-picker/index.d.ts.map +1 -1
  129. package/lib/typescript/module/date-picker/time.d.ts +56 -0
  130. package/lib/typescript/module/date-picker/time.d.ts.map +1 -0
  131. package/lib/typescript/module/date-picker/types.d.ts +49 -3
  132. package/lib/typescript/module/date-picker/types.d.ts.map +1 -1
  133. package/lib/typescript/module/index.d.ts +2 -2
  134. package/lib/typescript/module/index.d.ts.map +1 -1
  135. package/lib/typescript/module/index.web.d.ts +2 -2
  136. package/lib/typescript/module/index.web.d.ts.map +1 -1
  137. package/lib/typescript/module/listing-card/ListingCard.d.ts.map +1 -1
  138. package/lib/typescript/module/listing-card/WishlistCard.d.ts +1 -1
  139. package/lib/typescript/module/listing-card/WishlistCard.d.ts.map +1 -1
  140. package/lib/typescript/module/listing-card/shared.d.ts +8 -1
  141. package/lib/typescript/module/listing-card/shared.d.ts.map +1 -1
  142. package/lib/typescript/module/listing-card/types.d.ts +37 -0
  143. package/lib/typescript/module/listing-card/types.d.ts.map +1 -1
  144. package/lib/typescript/module/rating/RatingInput.d.ts +8 -0
  145. package/lib/typescript/module/rating/RatingInput.d.ts.map +1 -0
  146. package/lib/typescript/module/rating/index.d.ts +2 -1
  147. package/lib/typescript/module/rating/index.d.ts.map +1 -1
  148. package/lib/typescript/module/rating/types.d.ts +31 -0
  149. package/lib/typescript/module/rating/types.d.ts.map +1 -1
  150. package/lib/typescript/module/stay-search/DestinationSuggestions.d.ts +8 -0
  151. package/lib/typescript/module/stay-search/DestinationSuggestions.d.ts.map +1 -1
  152. package/lib/typescript/module/stay-search/GuestPicker.d.ts +6 -1
  153. package/lib/typescript/module/stay-search/GuestPicker.d.ts.map +1 -1
  154. package/lib/typescript/module/stay-search/types.d.ts +20 -0
  155. package/lib/typescript/module/stay-search/types.d.ts.map +1 -1
  156. package/lib/typescript/module/track-list/parts.d.ts.map +1 -1
  157. package/package.json +1 -1
  158. package/src/app-shell/AppShell.tsx +91 -4
  159. package/src/app-shell/layout.ts +10 -5
  160. package/src/app-shell/types.ts +12 -2
  161. package/src/content-panel/ContentPanel.tsx +8 -2
  162. package/src/content-panel/ContentPanel.web.tsx +39 -25
  163. package/src/content-panel/types.ts +50 -42
  164. package/src/date-picker/TimeField.tsx +162 -0
  165. package/src/date-picker/index.ts +6 -0
  166. package/src/date-picker/time.ts +158 -0
  167. package/src/date-picker/types.ts +52 -3
  168. package/src/index.ts +9 -2
  169. package/src/index.web.ts +9 -2
  170. package/src/listing-card/ListingCard.tsx +34 -1
  171. package/src/listing-card/WishlistCard.tsx +42 -6
  172. package/src/listing-card/shared.ts +22 -0
  173. package/src/listing-card/types.ts +37 -0
  174. package/src/rating/RatingInput.tsx +214 -0
  175. package/src/rating/index.ts +9 -1
  176. package/src/rating/types.ts +33 -0
  177. package/src/stay-search/DestinationSuggestions.tsx +48 -9
  178. package/src/stay-search/GuestPicker.tsx +9 -0
  179. package/src/stay-search/types.ts +20 -0
  180. package/src/track-list/parts.tsx +1 -20
@@ -4,6 +4,7 @@ import { Platform, Pressable, View } from 'react-native';
4
4
  import { resolveButtonRamps } from '../button/shared';
5
5
  import { RiMapPinLine } from '../icons/remix/RiMapPinLine';
6
6
  import { adoptStyleSheet } from '../styles/adopt-style-sheet';
7
+ import { DISABLED_OPACITY } from '../styles/tokens';
7
8
  import type { WebCssStyle } from '../styles/web-view-style';
8
9
  import { webDataSet } from '../styles/web-data';
9
10
  import { useTheme } from '../theme/use-theme';
@@ -20,6 +21,13 @@ import type { DestinationSuggestionsProps } from './types';
20
21
  *
21
22
  * A `listbox` of `option`s. The list takes focus (web) and owns the arrow
22
23
  * keys; the rows stay out of the tab order.
24
+ *
25
+ * A row can be `disabled` — "Use my location" while location permission is
26
+ * off. It stays in the list rather than disappearing, because a row that
27
+ * vanishes teaches nothing: it dims to `DISABLED_OPACITY`, carries
28
+ * `aria-disabled`, answers no press or Enter, is never highlighted by hover, and
29
+ * the arrow keys step OVER it. `disabledReason` replaces its description and is
30
+ * read after the title, so the row says why.
23
31
  */
24
32
 
25
33
  const IS_WEB = Platform.OS === 'web';
@@ -36,6 +44,28 @@ const CSS = `
36
44
  }
37
45
  `;
38
46
 
47
+ /**
48
+ * The next row the arrow keys land on, stepping OVER every disabled one and
49
+ * wrapping once. `-1` when every row is disabled — the highlight then stays
50
+ * where it is rather than landing on a row that answers nothing. Pure.
51
+ */
52
+ export function nextSelectable(
53
+ items: readonly { disabled?: boolean }[],
54
+ from: number,
55
+ step: 1 | -1,
56
+ ): number {
57
+ const count = items.length;
58
+ if (count === 0) return -1;
59
+ // From "nothing highlighted", ArrowDown starts at the top and ArrowUp at the
60
+ // bottom, which is what the list did before any row could be disabled.
61
+ const start = from < 0 ? (step === 1 ? 0 : count - 1) : (from + step + count) % count;
62
+ for (let i = 0; i < count; i++) {
63
+ const index = (start + step * i + count * count) % count;
64
+ if (!items[index]?.disabled) return index;
65
+ }
66
+ return -1;
67
+ }
68
+
39
69
  function DestinationSuggestionsComponent({
40
70
  items,
41
71
  onSelect,
@@ -68,13 +98,16 @@ function DestinationSuggestionsComponent({
68
98
  if (items.length === 0) return;
69
99
  if (e.key === 'ArrowDown') {
70
100
  e.preventDefault();
71
- highlight(current < 0 ? 0 : (current + 1) % items.length);
101
+ const next = nextSelectable(items, current, 1);
102
+ if (next >= 0) highlight(next);
72
103
  } else if (e.key === 'ArrowUp') {
73
104
  e.preventDefault();
74
- highlight(current <= 0 ? items.length - 1 : current - 1);
105
+ const next = nextSelectable(items, current, -1);
106
+ if (next >= 0) highlight(next);
75
107
  } else if (e.key === 'Enter' && current >= 0 && current < items.length) {
76
108
  e.preventDefault();
77
- onSelect(items[current]!);
109
+ const item = items[current]!;
110
+ if (!item.disabled) onSelect(item);
78
111
  }
79
112
  };
80
113
 
@@ -103,17 +136,22 @@ function DestinationSuggestionsComponent({
103
136
  >
104
137
  {items.map((item, index) => {
105
138
  const Icon = item.icon ?? RiMapPinLine;
106
- const selected = index === current;
139
+ const disabled = item.disabled === true;
140
+ const selected = index === current && !disabled;
141
+ const secondLine = disabled ? (item.disabledReason ?? item.description) : item.description;
107
142
  return (
108
143
  <Pressable
109
144
  key={item.id}
110
145
  role="option"
111
- accessibilityLabel={item.description ? `${item.title}, ${item.description}` : item.title}
146
+ accessibilityLabel={secondLine ? `${item.title}, ${secondLine}` : item.title}
112
147
  aria-selected={selected}
113
- accessibilityState={{ selected }}
148
+ accessibilityState={{ selected, disabled }}
149
+ disabled={disabled}
114
150
  focusable={false}
115
151
  onPress={() => onSelect(item)}
116
- onHoverIn={() => highlight(index)}
152
+ onHoverIn={() => {
153
+ if (!disabled) highlight(index);
154
+ }}
117
155
  testID={testID ? `${testID}-${index}` : undefined}
118
156
  style={{
119
157
  flexDirection: 'row',
@@ -124,6 +162,7 @@ function DestinationSuggestionsComponent({
124
162
  paddingLeft: 12,
125
163
  paddingRight: 12,
126
164
  borderRadius: 16,
165
+ opacity: disabled ? DISABLED_OPACITY : 1,
127
166
  backgroundColor: selected ? palette.rowHighlight : 'transparent',
128
167
  }}
129
168
  >
@@ -143,9 +182,9 @@ function DestinationSuggestionsComponent({
143
182
  <Text variant="body-medium" numberOfLines={1} style={{ color: palette.text }}>
144
183
  {item.title}
145
184
  </Text>
146
- {item.description ? (
185
+ {secondLine ? (
147
186
  <Text variant="body-2-regular" numberOfLines={1} style={{ color: palette.textSecondary }}>
148
- {item.description}
187
+ {secondLine}
149
188
  </Text>
150
189
  ) : null}
151
190
  </View>
@@ -19,6 +19,11 @@ const COUNTED: readonly GuestKind[] = ['adults', 'children'];
19
19
  * them — the one guests picker, used by the search bar's "Who" panel and the
20
20
  * booking card's guests popover alike. Then an optional note and "Close".
21
21
  *
22
+ * The stepper button names travel too: `decrementLabel` / `incrementLabel`
23
+ * reach every row, so the two words a screen reader reads on the eight buttons
24
+ * are translatable like everything else the family draws. Their default is
25
+ * `Stepper`'s own English pair.
26
+ *
22
27
  * Two rules:
23
28
  * - while any child, infant or pet is counted, adults cannot go below 1 (the
24
29
  * adults `−` disables there), and adding one of them with no adults sets
@@ -35,6 +40,8 @@ function GuestPickerComponent({
35
40
  kinds = GUEST_KINDS,
36
41
  labels,
37
42
  descriptions,
43
+ decrementLabel,
44
+ incrementLabel,
38
45
  note,
39
46
  onClose,
40
47
  closeLabel = 'Close',
@@ -71,6 +78,8 @@ function GuestPickerComponent({
71
78
  min={kind === 'adults' ? minimumAdults(value) : 0}
72
79
  max={maxFor(kind)}
73
80
  size={size}
81
+ decrementLabel={decrementLabel}
82
+ incrementLabel={incrementLabel}
74
83
  onValueChange={(n) => onChange(applyGuestCount(value, kind, n))}
75
84
  divider={index < kinds.length - 1}
76
85
  testID={testID ? `${testID}-${kind}` : undefined}
@@ -97,6 +97,18 @@ export interface DestinationSuggestion {
97
97
  description?: string;
98
98
  /** A Remix icon component (`RiMapPinLine`, `RiTimeLine`…). Default `RiMapPinLine`. */
99
99
  icon?: ButtonIconComponent;
100
+ /**
101
+ * The row is offered but cannot be chosen — "Use my location" while location
102
+ * is off. It dims, answers no press, and the arrow keys step over it.
103
+ */
104
+ disabled?: boolean;
105
+ /**
106
+ * Why it cannot be chosen ("Location is off"). Drawn in place of
107
+ * `description` while `disabled`, and read after the title, so the row says
108
+ * what is wrong rather than only looking wrong. Ignored when the row is not
109
+ * disabled.
110
+ */
111
+ disabledReason?: string;
100
112
  }
101
113
 
102
114
  export interface DestinationSuggestionsProps {
@@ -135,6 +147,14 @@ export interface GuestPickerProps {
135
147
  labels?: Partial<Record<GuestKind, string>>;
136
148
  /** Override descriptions (`null` hides one). */
137
149
  descriptions?: Partial<Record<GuestKind, string | null>>;
150
+ /**
151
+ * The name of every row's `−` button, which is English by default
152
+ * (`"Decrease"`). Same spelling as `Stepper`'s, and passed to every row — the
153
+ * row's own title is what says WHICH counter it belongs to.
154
+ */
155
+ decrementLabel?: string;
156
+ /** The name of every row's `+` button. Default `"Increase"`. */
157
+ incrementLabel?: string;
138
158
  /**
139
159
  * The most guests the place takes, counting adults and children (infants and
140
160
  * pets do not count). Their `+` buttons disable once the sum reaches it.
@@ -1,4 +1,4 @@
1
- import React, { Fragment, memo, useMemo, useState } from 'react';
1
+ import React, { Fragment, memo, useMemo } from 'react';
2
2
  import { Image, View, type GestureResponderEvent } from 'react-native';
3
3
 
4
4
  import { GlyphButton } from '../button';
@@ -13,8 +13,6 @@ import { RiMore2Fill } from '../icons/remix/RiMore2Fill';
13
13
  import { RiMusic2Line } from '../icons/remix/RiMusic2Line';
14
14
  import { resolvePhoto } from '../listing-card/shared';
15
15
  import { useImageResolver } from '../image-resolver/context';
16
- import { DISABLED_OPACITY, borderRadius } from '../styles/tokens';
17
- import type { WebCssStyle } from '../styles/web-view-style';
18
16
  import type { WebAriaProps } from '../styles/styled-primitives';
19
17
  import { useTheme } from '../theme/use-theme';
20
18
  import { IS_WEB, resolveTrackListPaint } from './shared';
@@ -58,23 +56,6 @@ export function TrackIconButton({
58
56
  }: TrackIconButtonProps) {
59
57
  const theme = useTheme();
60
58
  const paint = useMemo(() => resolveTrackListPaint(theme), [theme]);
61
- const [hovered, setHovered] = useState(false);
62
- const color =
63
- pressed && activeColor
64
- ? activeColor
65
- : hovered && !disabled
66
- ? paint.text
67
- : paint.textMuted;
68
- const rootStyle: WebCssStyle = {
69
- width: 32,
70
- height: 32,
71
- borderRadius: borderRadius.full,
72
- alignItems: 'center',
73
- justifyContent: 'center',
74
- opacity: disabled ? DISABLED_OPACITY : 1,
75
- '--bloom-track-ring': paint.ring,
76
- };
77
- const toggle = pressed !== undefined;
78
59
  return (
79
60
  <GlyphButton
80
61
  {...aria}