@12-apps/ui 6.23.0 → 6.24.1

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 (208) hide show
  1. package/NATIVE.md +2 -1
  2. package/dist/_internal/components/data-display/Avatar/Avatar.js +3 -3
  3. package/dist/_internal/components/data-display/Avatar/Avatar.view.js +2 -2
  4. package/dist/_internal/components/data-display/Badge/Badge.js +2 -2
  5. package/dist/_internal/components/data-display/Chart/Chart.js +3 -3
  6. package/dist/_internal/components/data-display/Chart/SpecChart.js +6 -6
  7. package/dist/_internal/components/data-display/Chart/chart-renderers.js +2 -2
  8. package/dist/_internal/components/data-display/Chart/chart-spec.js +2 -2
  9. package/dist/_internal/components/data-display/Chip/Chip.js +3 -3
  10. package/dist/_internal/components/data-display/Chip/Chip.styles.js +2 -2
  11. package/dist/_internal/components/data-display/DataGrid/DataGrid.js +6 -4
  12. package/dist/_internal/components/data-display/DataViews/DataViewsGrid.js +24 -24
  13. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +30 -30
  14. package/dist/_internal/components/data-display/DataViews/base-list-card-geometry.js +4 -4
  15. package/dist/_internal/components/data-display/DataViews/base-list-card-slots.js +3 -3
  16. package/dist/_internal/components/data-display/DataViews/base-list-card.js +6 -6
  17. package/dist/_internal/components/data-display/DataViews/data-views-display-panel.js +3 -3
  18. package/dist/_internal/components/data-display/DataViews/data-views-filter-panel.js +4 -4
  19. package/dist/_internal/components/data-display/DataViews/data-views-grid-bodies.js +6 -6
  20. package/dist/_internal/components/data-display/DataViews/data-views-grid-parts.js +21 -21
  21. package/dist/_internal/components/data-display/DataViews/data-views-inline-bar.js +6 -6
  22. package/dist/_internal/components/data-display/DataViews/data-views-more-fields.js +4 -4
  23. package/dist/_internal/components/data-display/DataViews/data-views-more-filters.js +5 -5
  24. package/dist/_internal/components/data-display/DataViews/data-views-range-pill.js +3 -3
  25. package/dist/_internal/components/data-display/DataViews/data-views-scope-tabs.js +2 -2
  26. package/dist/_internal/components/data-display/DataViews/data-views-shell-toolbar.js +6 -6
  27. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +2 -2
  28. package/dist/_internal/components/data-display/DataViews/data-views-toolbar.js +5 -5
  29. package/dist/_internal/components/data-display/DataViews/list-card-rails.js +2 -2
  30. package/dist/_internal/components/data-display/DataViews/use-data-views-state.js +2 -2
  31. package/dist/_internal/components/data-display/FleetMap/FleetBody.js +4 -3
  32. package/dist/_internal/components/data-display/FleetMap/FleetMap.base.js +1 -0
  33. package/dist/_internal/components/data-display/FleetMap/FleetMap.base.js.map +1 -0
  34. package/dist/_internal/components/data-display/FleetMap/FleetMap.helpers.js +1 -1
  35. package/dist/_internal/components/data-display/FleetMap/FleetMap.hooks.js +2 -2
  36. package/dist/_internal/components/data-display/FleetMap/FleetMap.js +6 -5
  37. package/dist/_internal/components/data-display/FleetMap/FleetMap.metrics.js +20 -0
  38. package/dist/_internal/components/data-display/FleetMap/FleetMap.metrics.js.map +1 -0
  39. package/dist/_internal/components/data-display/FleetMap/FleetRoster.js +3 -2
  40. package/dist/_internal/components/data-display/index.js +26 -25
  41. package/dist/_internal/components/index.js +26 -25
  42. package/dist/_internal/platform/aria.js.map +1 -1
  43. package/dist/_internal/user-avatar/user-avatar.js +4 -4
  44. package/dist/charts.js +10 -10
  45. package/dist/{chunk-WGRS4PGY.js → chunk-2C47DEVU.js} +4 -4
  46. package/dist/{chunk-KHWCX4LI.js → chunk-6LGWJFQK.js} +2 -2
  47. package/dist/{chunk-XSF6AXEU.js → chunk-6RZDDUSM.js} +2 -2
  48. package/dist/{chunk-3FLVIVOK.js → chunk-BHIGHSXA.js} +2 -2
  49. package/dist/{chunk-VNNG46TS.js → chunk-BPLFM2GL.js} +6 -6
  50. package/dist/chunk-BTQZ3EX3.js +45 -0
  51. package/dist/chunk-BTQZ3EX3.js.map +1 -0
  52. package/dist/{chunk-LYSSO6WC.js → chunk-CZPU5TW3.js} +4 -4
  53. package/dist/{chunk-BZTFPSWT.js → chunk-DFLTUI7W.js} +7 -7
  54. package/dist/{chunk-URXT5TOY.js → chunk-E46PG577.js} +4 -4
  55. package/dist/{chunk-X5MPHXG5.js → chunk-EZ5Z5UFI.js} +9 -7
  56. package/dist/{chunk-X5MPHXG5.js.map → chunk-EZ5Z5UFI.js.map} +1 -1
  57. package/dist/{chunk-DRHIIUDO.js → chunk-FPWB5ILF.js} +5 -5
  58. package/dist/{chunk-R6SBP5M5.js → chunk-GOWBYHYW.js} +4 -4
  59. package/dist/{chunk-MQNKRIGC.js → chunk-HMV4QMXS.js} +25 -11
  60. package/dist/chunk-HMV4QMXS.js.map +1 -0
  61. package/dist/{chunk-N2TUX4A5.js → chunk-I3E2QHTH.js} +3 -3
  62. package/dist/{chunk-4XZXLB4I.js → chunk-JQ45NDBS.js} +3 -3
  63. package/dist/{chunk-I7HYGIEJ.js → chunk-JUHUKZGU.js} +2 -2
  64. package/dist/{chunk-TWQYDKCA.js → chunk-JZXWCEH5.js} +12 -9
  65. package/dist/chunk-JZXWCEH5.js.map +1 -0
  66. package/dist/{chunk-WQA4LIIK.js → chunk-KT6HWAEN.js} +4 -4
  67. package/dist/{chunk-2UGJIKVA.js → chunk-MZQTHSLN.js} +3 -3
  68. package/dist/{chunk-4SQFO4GW.js → chunk-NA7MIEG6.js} +7 -7
  69. package/dist/{chunk-MYGNIFZC.js → chunk-O4VCZTK4.js} +2 -2
  70. package/dist/{chunk-LS4FMSHY.js → chunk-OAYX4XN7.js} +4 -4
  71. package/dist/{chunk-Y5UCOBQN.js → chunk-OGGHD64H.js} +4 -4
  72. package/dist/{chunk-CBTY36TU.js → chunk-OH3YGJT5.js} +2 -2
  73. package/dist/{chunk-VON4BFK4.js → chunk-PN5CQKA2.js} +2 -2
  74. package/dist/{chunk-CF3FAAPC.js → chunk-RP5FK3SO.js} +2 -2
  75. package/dist/{chunk-L4U23SBX.js → chunk-TSYWI3NP.js} +13 -13
  76. package/dist/{chunk-YQ6TNHAC.js → chunk-TTLI6PF7.js} +5 -5
  77. package/dist/{chunk-OIKQO73C.js → chunk-TX5V42AY.js} +5 -5
  78. package/dist/{chunk-AJGYHDA5.js → chunk-U2YJCNRU.js} +4 -4
  79. package/dist/{chunk-W7Y6RA6W.js → chunk-WR5JFDQ5.js} +4 -4
  80. package/dist/{chunk-ACFGEZ4Q.js → chunk-XO753WHD.js} +3 -3
  81. package/dist/{chunk-5V4UPOWK.js → chunk-XPMGFAJ3.js} +1 -1
  82. package/dist/chunk-XPMGFAJ3.js.map +1 -0
  83. package/dist/{chunk-AEGXHZQT.js → chunk-YW5T6KZ5.js} +7 -7
  84. package/dist/{chunk-F7VBGS4C.js → chunk-ZCPSND5Y.js} +2 -2
  85. package/dist/chunk-ZCPSND5Y.js.map +1 -0
  86. package/dist/{chunk-4LRFGB3K.js → chunk-ZRPGVHTR.js} +2 -2
  87. package/dist/data-display/Avatar.js +3 -3
  88. package/dist/data-display/Badge.js +2 -2
  89. package/dist/data-display/CardKit.js +10 -10
  90. package/dist/data-display/Chart.js +10 -10
  91. package/dist/data-display/Chip.js +3 -3
  92. package/dist/data-display/DataGrid.js +2 -2
  93. package/dist/data-display/DataViews.js +48 -48
  94. package/dist/data-display/FleetMap.js +6 -5
  95. package/dist/native/chunks/{chunk-T6BJPP3E.js → chunk-DN73KYIZ.js} +1 -1
  96. package/dist/native/chunks/chunk-DN73KYIZ.js.map +1 -0
  97. package/dist/native/chunks/chunk-GW7TQ7HO.js +215 -0
  98. package/dist/native/chunks/chunk-GW7TQ7HO.js.map +1 -0
  99. package/dist/native/chunks/chunk-MJVSQXOD.js +9 -0
  100. package/dist/native/chunks/chunk-MJVSQXOD.js.map +1 -0
  101. package/dist/native/chunks/chunk-ZAQ3AZJM.js +292 -0
  102. package/dist/native/chunks/chunk-ZAQ3AZJM.js.map +1 -0
  103. package/dist/native/data-display/Alert.js +4 -4
  104. package/dist/native/data-display/Avatar.js +3 -3
  105. package/dist/native/data-display/Badge.js +4 -4
  106. package/dist/native/data-display/Chip.js +3 -5
  107. package/dist/native/data-display/Chip.js.map +1 -1
  108. package/dist/native/data-display/EmptyState.js +9 -286
  109. package/dist/native/data-display/EmptyState.js.map +1 -1
  110. package/dist/native/data-display/ErrorState.js +6 -6
  111. package/dist/native/data-display/FleetMap.js +498 -0
  112. package/dist/native/data-display/FleetMap.js.map +1 -0
  113. package/dist/native/data-display/LoadingState.js +3 -3
  114. package/dist/native/feedback/Dialog.js +3 -3
  115. package/dist/native/form/Checkbox.js +1 -1
  116. package/dist/native/form/Input.js +1 -1
  117. package/dist/native/form/Select.js +1 -1
  118. package/dist/native/form/Switch.js +1 -1
  119. package/dist/native/layout/Card.js +3 -3
  120. package/dist/native/layout/Skeleton.js +6 -209
  121. package/dist/native/layout/Skeleton.js.map +1 -1
  122. package/dist/types/components/data-display/DataGrid/DataGrid.d.ts +11 -0
  123. package/dist/types/components/data-display/DataGrid/DataGrid.d.ts.map +1 -1
  124. package/dist/types/components/data-display/FleetMap/FleetMap.base.d.ts +141 -0
  125. package/dist/types/components/data-display/FleetMap/FleetMap.base.d.ts.map +1 -0
  126. package/dist/types/components/data-display/FleetMap/FleetMap.d.ts.map +1 -1
  127. package/dist/types/components/data-display/FleetMap/FleetMap.helpers.d.ts +1 -1
  128. package/dist/types/components/data-display/FleetMap/FleetMap.helpers.d.ts.map +1 -1
  129. package/dist/types/components/data-display/FleetMap/FleetMap.hooks.d.ts +20 -3
  130. package/dist/types/components/data-display/FleetMap/FleetMap.hooks.d.ts.map +1 -1
  131. package/dist/types/components/data-display/FleetMap/FleetMap.metrics.d.ts +76 -0
  132. package/dist/types/components/data-display/FleetMap/FleetMap.metrics.d.ts.map +1 -0
  133. package/dist/types/components/data-display/FleetMap/FleetMap.types.d.ts +13 -113
  134. package/dist/types/components/data-display/FleetMap/FleetMap.types.d.ts.map +1 -1
  135. package/dist/types/components/data-display/FleetMap/FleetRoster.d.ts.map +1 -1
  136. package/dist/types/platform/aria.d.ts +13 -0
  137. package/dist/types/platform/aria.d.ts.map +1 -1
  138. package/dist/types-native/components/data-display/FleetMap/FleetMap.base.d.ts +141 -0
  139. package/dist/types-native/components/data-display/FleetMap/FleetMap.base.d.ts.map +1 -0
  140. package/dist/types-native/components/data-display/FleetMap/FleetMap.helpers.d.ts +45 -0
  141. package/dist/types-native/components/data-display/FleetMap/FleetMap.helpers.d.ts.map +1 -0
  142. package/dist/types-native/components/data-display/FleetMap/FleetMap.hooks.d.ts +71 -0
  143. package/dist/types-native/components/data-display/FleetMap/FleetMap.hooks.d.ts.map +1 -0
  144. package/dist/types-native/components/data-display/FleetMap/FleetMap.metrics.d.ts +76 -0
  145. package/dist/types-native/components/data-display/FleetMap/FleetMap.metrics.d.ts.map +1 -0
  146. package/dist/types-native/components/data-display/FleetMap/FleetMap.native.d.ts +30 -0
  147. package/dist/types-native/components/data-display/FleetMap/FleetMap.native.d.ts.map +1 -0
  148. package/dist/types-native/components/data-display/FleetMap/FleetMap.types.native.d.ts +36 -0
  149. package/dist/types-native/components/data-display/FleetMap/FleetMap.types.native.d.ts.map +1 -0
  150. package/dist/types-native/components/data-display/FleetMap/FleetRoster.native.d.ts +17 -0
  151. package/dist/types-native/components/data-display/FleetMap/FleetRoster.native.d.ts.map +1 -0
  152. package/dist/types-native/components/data-display/FleetMap/index.native.d.ts +4 -0
  153. package/dist/types-native/components/data-display/FleetMap/index.native.d.ts.map +1 -0
  154. package/dist/types-native/platform/aria.d.ts +13 -0
  155. package/dist/types-native/platform/aria.d.ts.map +1 -1
  156. package/dist/user-avatar.js +4 -4
  157. package/package.json +5 -1
  158. package/src/components/data-display/DataGrid/DataGrid.tsx +5 -1
  159. package/src/components/data-display/FleetMap/FleetMap.base.ts +144 -0
  160. package/src/components/data-display/FleetMap/FleetMap.helpers.ts +1 -1
  161. package/src/components/data-display/FleetMap/FleetMap.hooks.ts +23 -4
  162. package/src/components/data-display/FleetMap/FleetMap.md +15 -8
  163. package/src/components/data-display/FleetMap/FleetMap.metrics.ts +81 -0
  164. package/src/components/data-display/FleetMap/FleetMap.native.tsx +185 -0
  165. package/src/components/data-display/FleetMap/FleetMap.tsx +8 -14
  166. package/src/components/data-display/FleetMap/FleetMap.types.native.ts +44 -0
  167. package/src/components/data-display/FleetMap/FleetMap.types.ts +19 -114
  168. package/src/components/data-display/FleetMap/FleetRoster.native.tsx +382 -0
  169. package/src/components/data-display/FleetMap/FleetRoster.tsx +17 -11
  170. package/src/components/data-display/FleetMap/NATIVE-NOTES.md +14 -0
  171. package/src/components/data-display/FleetMap/index.native.ts +10 -0
  172. package/src/components/data-display/FleetMap/tests.md +6 -1
  173. package/src/platform/aria.ts +13 -0
  174. package/dist/chunk-5V4UPOWK.js.map +0 -1
  175. package/dist/chunk-F7VBGS4C.js.map +0 -1
  176. package/dist/chunk-MQNKRIGC.js.map +0 -1
  177. package/dist/chunk-TWQYDKCA.js.map +0 -1
  178. package/dist/native/chunks/chunk-T6BJPP3E.js.map +0 -1
  179. /package/dist/{chunk-WGRS4PGY.js.map → chunk-2C47DEVU.js.map} +0 -0
  180. /package/dist/{chunk-KHWCX4LI.js.map → chunk-6LGWJFQK.js.map} +0 -0
  181. /package/dist/{chunk-XSF6AXEU.js.map → chunk-6RZDDUSM.js.map} +0 -0
  182. /package/dist/{chunk-3FLVIVOK.js.map → chunk-BHIGHSXA.js.map} +0 -0
  183. /package/dist/{chunk-VNNG46TS.js.map → chunk-BPLFM2GL.js.map} +0 -0
  184. /package/dist/{chunk-LYSSO6WC.js.map → chunk-CZPU5TW3.js.map} +0 -0
  185. /package/dist/{chunk-BZTFPSWT.js.map → chunk-DFLTUI7W.js.map} +0 -0
  186. /package/dist/{chunk-URXT5TOY.js.map → chunk-E46PG577.js.map} +0 -0
  187. /package/dist/{chunk-DRHIIUDO.js.map → chunk-FPWB5ILF.js.map} +0 -0
  188. /package/dist/{chunk-R6SBP5M5.js.map → chunk-GOWBYHYW.js.map} +0 -0
  189. /package/dist/{chunk-N2TUX4A5.js.map → chunk-I3E2QHTH.js.map} +0 -0
  190. /package/dist/{chunk-4XZXLB4I.js.map → chunk-JQ45NDBS.js.map} +0 -0
  191. /package/dist/{chunk-I7HYGIEJ.js.map → chunk-JUHUKZGU.js.map} +0 -0
  192. /package/dist/{chunk-WQA4LIIK.js.map → chunk-KT6HWAEN.js.map} +0 -0
  193. /package/dist/{chunk-2UGJIKVA.js.map → chunk-MZQTHSLN.js.map} +0 -0
  194. /package/dist/{chunk-4SQFO4GW.js.map → chunk-NA7MIEG6.js.map} +0 -0
  195. /package/dist/{chunk-MYGNIFZC.js.map → chunk-O4VCZTK4.js.map} +0 -0
  196. /package/dist/{chunk-LS4FMSHY.js.map → chunk-OAYX4XN7.js.map} +0 -0
  197. /package/dist/{chunk-Y5UCOBQN.js.map → chunk-OGGHD64H.js.map} +0 -0
  198. /package/dist/{chunk-CBTY36TU.js.map → chunk-OH3YGJT5.js.map} +0 -0
  199. /package/dist/{chunk-VON4BFK4.js.map → chunk-PN5CQKA2.js.map} +0 -0
  200. /package/dist/{chunk-CF3FAAPC.js.map → chunk-RP5FK3SO.js.map} +0 -0
  201. /package/dist/{chunk-L4U23SBX.js.map → chunk-TSYWI3NP.js.map} +0 -0
  202. /package/dist/{chunk-YQ6TNHAC.js.map → chunk-TTLI6PF7.js.map} +0 -0
  203. /package/dist/{chunk-OIKQO73C.js.map → chunk-TX5V42AY.js.map} +0 -0
  204. /package/dist/{chunk-AJGYHDA5.js.map → chunk-U2YJCNRU.js.map} +0 -0
  205. /package/dist/{chunk-W7Y6RA6W.js.map → chunk-WR5JFDQ5.js.map} +0 -0
  206. /package/dist/{chunk-ACFGEZ4Q.js.map → chunk-XO753WHD.js.map} +0 -0
  207. /package/dist/{chunk-AEGXHZQT.js.map → chunk-YW5T6KZ5.js.map} +0 -0
  208. /package/dist/{chunk-4LRFGB3K.js.map → chunk-ZRPGVHTR.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FleetRoster.native.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/FleetMap/FleetRoster.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAe/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAkB,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAsQvF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC3C;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,mBAAmB,EACnB,iBAAiB,EACjB,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,GACV,EAAE,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAiEtC"}
@@ -0,0 +1,4 @@
1
+ export { FleetMap, default } from './FleetMap.native';
2
+ export { FleetRoster } from './FleetRoster.native';
3
+ export type { FleetFreshness, FleetMapBaseProps, FleetMapCopy, FleetMapCopyBase, FleetMapProps, FleetUnit, } from './FleetMap.types.native';
4
+ //# sourceMappingURL=index.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.native.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/FleetMap/index.native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,SAAS,GACV,MAAM,yBAAyB,CAAC"}
@@ -27,6 +27,19 @@ export interface WebAria {
27
27
  * it expands is a listbox — which is what the web's own trigger carries.
28
28
  */
29
29
  'aria-haspopup'?: 'listbox' | 'menu' | 'dialog' | 'true';
30
+ /**
31
+ * Which option a composite widget's single tab stop is currently on — the
32
+ * listbox pattern, where the LIST holds focus and the selection moves inside
33
+ * it. React Native has no equivalent because it has no roving focus to
34
+ * describe: a device selects by touching the row.
35
+ *
36
+ * Note the interaction with `aria-atomic` documented on {@link webAria}:
37
+ * react-native-web reads THIS value when it means the atomic flag, so a
38
+ * component that sets both gets the descendant id emitted as `aria-atomic`.
39
+ * Nothing here sets both, and a component that needs to should pass its own
40
+ * atomic flag directly rather than through this helper.
41
+ */
42
+ 'aria-activedescendant'?: string;
30
43
  }
31
44
  /**
32
45
  * `attrs` as something JSX can spread onto a react-native element without
@@ -1 +1 @@
1
- {"version":3,"file":"aria.d.ts","sourceRoot":"","sources":["../../../src/platform/aria.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,OAAO;IACtB,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC1D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAErD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,MAAM,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,CAExE"}
1
+ {"version":3,"file":"aria.d.ts","sourceRoot":"","sources":["../../../src/platform/aria.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,OAAO;IACtB,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;IACzD;;;;;;;;;;;OAWG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAErD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,MAAM,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,CAExE"}
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  UserAvatar,
3
3
  UserMenu
4
- } from "./chunk-4LRFGB3K.js";
4
+ } from "./chunk-ZRPGVHTR.js";
5
5
  import "./chunk-UDQSIRAP.js";
6
- import "./chunk-I7HYGIEJ.js";
7
- import "./chunk-4SQFO4GW.js";
6
+ import "./chunk-JUHUKZGU.js";
7
+ import "./chunk-NA7MIEG6.js";
8
+ import "./chunk-XRMZMBE3.js";
8
9
  import "./chunk-FAITHWIO.js";
9
10
  import "./chunk-3ZVUNSFF.js";
10
- import "./chunk-XRMZMBE3.js";
11
11
  import "./chunk-VUHLNYAE.js";
12
12
  import "./chunk-2GSIEDGN.js";
13
13
  import "./chunk-AIRWM7KK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.23.0",
3
+ "version": "6.24.1",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -205,6 +205,10 @@
205
205
  "default": "./dist/data-display/EmptyState.js"
206
206
  },
207
207
  "./data-display/FleetMap": {
208
+ "react-native": {
209
+ "types": "./dist/types-native/components/data-display/FleetMap/index.native.d.ts",
210
+ "default": "./dist/native/data-display/FleetMap.js"
211
+ },
208
212
  "types": "./dist/types/components/data-display/FleetMap/index.d.ts",
209
213
  "default": "./dist/data-display/FleetMap.js"
210
214
  },
@@ -112,8 +112,11 @@ export const DataGrid = <T extends Record<string, unknown> = Record<string, unkn
112
112
  * `DataGridProps` extends `HTMLAttributes`, so the rest-spread carries the
113
113
  * grid's own configuration too. React would warn (or worse, serialize an array
114
114
  * of rows into an attribute) if any of it reached the DOM.
115
+ *
116
+ * Exported so a test can render a grid carrying every name on it and assert
117
+ * none reaches the DOM — the check that would have caught `emptyText` missing.
115
118
  */
116
- const GRID_ONLY_PROPS = [
119
+ export const GRID_ONLY_PROPS = [
117
120
  'rows',
118
121
  'columns',
119
122
  'getRowId',
@@ -135,6 +138,7 @@ const GRID_ONLY_PROPS = [
135
138
  'loading',
136
139
  'error',
137
140
  'emptyState',
141
+ 'emptyText',
138
142
  'onRequestData',
139
143
  'ariaLabel',
140
144
  'ariaDescription',
@@ -0,0 +1,144 @@
1
+ /**
2
+ * THE CONTRACT BOTH `FleetMap` RENDERERS HONOUR — and nothing else.
3
+ *
4
+ * This file imports no MUI and no react-native, on purpose: it ships in BOTH
5
+ * declaration outputs (`dist/types` and `dist/types-native`), and a native
6
+ * consumer has no `@mui/material` to resolve a type import against.
7
+ *
8
+ * What is NOT here is as deliberate as what is. `map` (the map control bar's
9
+ * six words) lives on the web copy alone, because the map half is web-only
10
+ * until `MapPreview` is ported — see `NATIVE-NOTES.md`. A native caller that
11
+ * had to supply words for a bar no renderer draws would be asked to describe
12
+ * something it cannot see.
13
+ */
14
+
15
+ /**
16
+ * One tracked unit's newest position, as the caller resolved it.
17
+ *
18
+ * Deliberately says nothing about WHAT is being tracked. A courier, a service
19
+ * van, a field engineer — the component draws a labelled dot with a freshness,
20
+ * and every word that names the domain arrives through the copy object.
21
+ */
22
+ export interface FleetUnit {
23
+ id: string;
24
+ /** The name a human reads, on the pin and in the roster. */
25
+ label: string;
26
+ latitude: number;
27
+ longitude: number;
28
+ /**
29
+ * The fix's accuracy radius in metres, when the platform reported one.
30
+ *
31
+ * Kept because it is what separates a rooftop GPS fix from a cell-tower guess
32
+ * three blocks wide, and a map that draws both identically tells its reader
33
+ * something false.
34
+ */
35
+ accuracyM?: number | null;
36
+ /**
37
+ * Seconds since the fix was ACCEPTED, on the server's clock.
38
+ *
39
+ * The caller computes it, and the distinction matters: a device whose own
40
+ * clock is an hour slow would otherwise report itself as an hour stale while
41
+ * reporting perfectly.
42
+ */
43
+ staleSeconds: number;
44
+ /** A short pre-formatted badge beside the name, e.g. `"2 entregas"`. */
45
+ badge?: string;
46
+ }
47
+
48
+ /** How current a unit's newest fix is, derived from the thresholds below. */
49
+ export type FleetFreshness = 'live' | 'lagging' | 'stale';
50
+
51
+ /**
52
+ * Every word the board prints, and the two it FORMATS — minus the map's own.
53
+ *
54
+ * `lastSeen` and `accuracy` are functions rather than strings because a
55
+ * duration and a distance are locale rules, and this package holds the same
56
+ * line `StatCard` does: presentation only, the caller owns the formatting. A
57
+ * `lastSeenMinutes: string` template would have made "há 2 min" the only
58
+ * shape any consumer could ever render.
59
+ */
60
+ export interface FleetMapCopyBase {
61
+ /** The panel's heading. */
62
+ title: string;
63
+ /** What the roster says when nobody is reporting. */
64
+ emptyTitle: string;
65
+ emptyDescription?: string;
66
+ /** The roster list's accessible name — it is the map's readable half. */
67
+ rosterLabel: string;
68
+ /**
69
+ * What a screen reader hears while the roster reloads, e.g. "Atualizando a
70
+ * frota". OPTIONAL, and silence is the honest default: the busy state marks
71
+ * the region stale without uttering anything, the skeletons are hidden from
72
+ * the reader, and this component cannot invent the sentence in the reader's
73
+ * language. Set it and the panel announces politely; leave it and it stays
74
+ * quiet.
75
+ *
76
+ * Announced on a TRANSITION into loading. A board mounted already loading is
77
+ * silent — as any live region is at mount, since there is no change for a
78
+ * reader to observe.
79
+ */
80
+ loading?: string;
81
+ /** The three freshness words, as a reader sees them on a row. */
82
+ freshness: Record<FleetFreshness, string>;
83
+ /** `(seconds) => "há 2 min"`. */
84
+ lastSeen: (staleSeconds: number) => string;
85
+ /** `(metres) => "±12 m"`. A unit with no accuracy renders none. */
86
+ accuracy: (metres: number) => string;
87
+ }
88
+
89
+ /**
90
+ * The props both renderers take.
91
+ *
92
+ * `height` and `className` are NOT here: the first is a CSS length the web
93
+ * gives the map, and the second is a DOM class. Each side declares its own
94
+ * layout escape hatch beside its own extras.
95
+ */
96
+ export interface FleetMapBaseProps {
97
+ /** Everyone currently reporting. An empty array renders the empty state. */
98
+ units: readonly FleetUnit[];
99
+ /**
100
+ * Which unit is highlighted: the roster marks it selected, and on the web the
101
+ * map centres on it. `null` selects nobody.
102
+ *
103
+ * PASSING IT AT ALL IS THE DECISION. Present on the first render — `null`
104
+ * included — and the board is controlled for life: it renders what you pass
105
+ * and never moves the selection itself. Absent on the first render and it
106
+ * keeps its own, reporting each change through {@link onSelect}. The mode is
107
+ * latched on that first render, the way React latches an `<input>`, so the
108
+ * component cannot change its own semantics halfway through a session.
109
+ *
110
+ * A controlled board reads `undefined` as "nobody is selected", so
111
+ * `useState<string>()` in the caller behaves: clearing it clears the roster.
112
+ */
113
+ selectedId?: string | null;
114
+ /**
115
+ * A click or press on a row, and each arrow-key move on the web.
116
+ *
117
+ * Passing {@link selectedId} WITHOUT this is a selection nothing can move:
118
+ * the arrow keys then decline to act and leave the page to scroll, rather
119
+ * than swallowing a keypress that would do nothing.
120
+ */
121
+ onSelect?: (id: string) => void;
122
+ /**
123
+ * Seconds after which a unit stops reading as `live`, and then as `lagging`.
124
+ *
125
+ * Both carry a GENERIC default (90s and 300s, sized for a phone reporting
126
+ * every twenty seconds) and no domain one, because the answer is entirely a
127
+ * property of the fleet's own ping cadence: a phone reporting every twenty
128
+ * seconds is late at ninety, and a tracker reporting every five minutes is
129
+ * not. A component that picked one would be picking it for every consumer.
130
+ */
131
+ laggingAfterSeconds?: number;
132
+ staleAfterSeconds?: number;
133
+ /**
134
+ * While true the roster is marked busy, and renders skeletons IF no units
135
+ * have landed yet — a poll over a populated roster keeps the list, because
136
+ * unmounting it throws away the focus inside it.
137
+ *
138
+ * It only ANNOUNCES if the copy's `loading` sentence is set: busy is a state,
139
+ * not an utterance.
140
+ */
141
+ loading?: boolean;
142
+ /** Test id for the panel root; every child id is derived from it. */
143
+ dataTestId?: string;
144
+ }
@@ -1,4 +1,4 @@
1
- import type { FleetFreshness, FleetUnit } from './FleetMap.types';
1
+ import type { FleetFreshness, FleetUnit } from './FleetMap.base';
2
2
 
3
3
  /**
4
4
  * How current a fix is, on the two thresholds the caller supplied.
@@ -1,7 +1,26 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { mapCentre, nextSelection, rosterOrder, type LatLng } from './FleetMap.helpers';
4
- import type { FleetUnit } from './FleetMap.types';
4
+ import type { FleetUnit } from './FleetMap.base';
5
+
6
+ /**
7
+ * The half of a key event this hook reads, spelled structurally.
8
+ *
9
+ * NOT `React.KeyboardEvent<HTMLDivElement>`: the native type program is checked
10
+ * without the DOM lib (`tsconfig.native.json`), so a DOM type here would put
11
+ * the whole hook out of reach of the React Native renderer that shares it. A
12
+ * DOM event satisfies this shape, and a handler taking the wider type is
13
+ * assignable wherever the narrower one is expected — so the web passes its own
14
+ * `onKeyDown` unchanged.
15
+ */
16
+ export interface FleetKeyEvent {
17
+ key: string;
18
+ altKey?: boolean;
19
+ ctrlKey?: boolean;
20
+ metaKey?: boolean;
21
+ shiftKey?: boolean;
22
+ preventDefault: () => void;
23
+ }
5
24
 
6
25
  /**
7
26
  * Everything the board derives from its props, in one hook.
@@ -52,11 +71,11 @@ export interface FleetMapState {
52
71
  centre: LatLng | null;
53
72
  select: (id: string) => void;
54
73
  markers: FleetMarker[];
55
- onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;
74
+ onKeyDown: (event: FleetKeyEvent) => void;
56
75
  }
57
76
 
58
77
  /** Which way an arrow moves the selection, or `0` when it is not ours. */
59
- function stepFor(event: React.KeyboardEvent<HTMLDivElement>): 1 | -1 | 0 {
78
+ function stepFor(event: FleetKeyEvent): 1 | -1 | 0 {
60
79
  // A modified arrow is the PAGE's shortcut, not the roster's: Cmd+Down is
61
80
  // "scroll to the end of the document" on macOS, and Shift+Arrow extends a
62
81
  // selection. Capturing them would take a browser gesture away.
@@ -133,7 +152,7 @@ export function useFleetMap(
133
152
  );
134
153
 
135
154
  const onKeyDown = React.useCallback(
136
- (event: React.KeyboardEvent<HTMLDivElement>) => {
155
+ (event: FleetKeyEvent) => {
137
156
  const step = stepFor(event);
138
157
  if (step === 0) return;
139
158
  // Nothing can move a CONTROLLED selection without a handler, so the keys
@@ -165,14 +165,21 @@ picking it for every consumer.
165
165
 
166
166
  ## Known limits
167
167
 
168
- - **Not ported to React Native.** `@12-apps/ui` ships a native build for 26 of
169
- its 141 subpaths; this is not one, and neither is the `MapPreview` it wraps.
170
- Being unported is the ordinary state — 115 subpaths are, at the time of
171
- writing — and it fails LOUDLY under Metro: an unported subpath resolves to the
172
- web file and errors on `@mui/material` at import rather than rendering a blank
173
- view. It is also the right call for this
174
- component: a fleet board is a dispatcher's screen, and the tracked unit's own
175
- device reports a position rather than drawing the fleet.
168
+ - **The React Native build has no MAP.** This subpath now carries a
169
+ `react-native` export condition, and what it renders there is the roster: the
170
+ heading, the rows with their freshness, the empty state, the skeletons and the
171
+ reload announcement. `MapPreview` — ~2,200 lines that project, tile and
172
+ decorate their own surface — is still unported, so the native copy interface
173
+ has neither `map` nor `mapLabel` and a native board draws no canvas. That is a
174
+ smaller loss than it sounds for this component in particular, because the
175
+ roster is already its accessible representation rather than a sidebar: every
176
+ fact a pin carries is on a row. `NATIVE-NOTES.md` holds the full list of
177
+ rendering gaps, and `pnpm native:ledger` counts them.
178
+ - **On a device the roster is touch-only.** The arrow keys, the single tab stop
179
+ and `aria-activedescendant` all reach the list through react-native-web, so a
180
+ browser still walks it exactly as the web build does; React Native delivers no
181
+ key events to a `View`, so on a phone the selection is made by touch and every
182
+ row is its own tab stop.
176
183
  - **The map's own `aria-label` is English**, and comes from `MapPreview` rather
177
184
  than from `copy`. Everything `FleetMap` itself renders is injected; that one
178
185
  sentence is not, and a pt-BR consumer's screen reader will read it in English
@@ -0,0 +1,81 @@
1
+ /**
2
+ * THE NUMBERS BOTH `FleetMap` RENDERERS DRAW WITH.
3
+ *
4
+ * `FleetRoster.tsx` / `FleetMap.tsx` (web, MUI `sx`) and `FleetRoster.native.tsx`
5
+ * / `FleetMap.native.tsx` (React Native) read this one table. The web hands the
6
+ * spacing UNITS to `theme.spacing()` and the radius MULTIPLES to `sx`; native
7
+ * multiplies the units by the theme's own spacing step and the multiples by
8
+ * `shape.borderRadius`.
9
+ *
10
+ * Nothing here imports a renderer, a theme or a colour: a freshness dot's
11
+ * COLOUR is the theme's (`success.main`, `warning.main`, `text.disabled`), and
12
+ * the two sides reach the same palette through different doors.
13
+ *
14
+ * ## Why the radii are MULTIPLES and never pixels
15
+ *
16
+ * MUI's `sx` reads a bare number for `borderRadius` as a multiple of
17
+ * `theme.shape.borderRadius`, so a value that already had the theme's radius
18
+ * divided out of it is applied twice. Measured: `theme.shape.borderRadius / 4`
19
+ * drew 4px on the default theme — which is why it looked right — and 36px on a
20
+ * host that sets its own radius to 12. In a package whose whole premise is
21
+ * being re-themed, only the multiple is portable, and it is the multiple that
22
+ * both renderers store here.
23
+ */
24
+
25
+ /** `theme.spacing(n)` for the panel's own stack. */
26
+ export const FLEET_PANEL = {
27
+ /** Between the heading, the status region and the body. */
28
+ gapUnits: 1.5,
29
+ } as const;
30
+
31
+ /** The roster list box. */
32
+ export const FLEET_ROSTER = {
33
+ /** Between rows. */
34
+ gapUnits: 0.5,
35
+ /** `:focus-visible` ring on the web; a device has no focus ring to draw. */
36
+ focusRingWidth: 2,
37
+ focusRingOffset: 2,
38
+ } as const;
39
+
40
+ /** One roster row. */
41
+ export const FLEET_ROW = {
42
+ /** Between the dot, the text block and the badge. */
43
+ gapUnits: 1.5,
44
+ /** `theme.spacing(y, x)`. */
45
+ paddingYUnits: 1,
46
+ paddingXUnits: 1.5,
47
+ /** A MULTIPLE of `theme.shape.borderRadius` — see the note above. */
48
+ radiusMultiple: 1,
49
+ } as const;
50
+
51
+ /**
52
+ * The freshness dot.
53
+ *
54
+ * A perfect circle, so the radius is half the box on both sides rather than
55
+ * the web's `50%`: React Native resolves a percentage radius against the box
56
+ * it is given, and a square box makes the two spellings agree.
57
+ */
58
+ export const FLEET_DOT = {
59
+ size: 10,
60
+ } as const;
61
+
62
+ /** What stands in for the roster before any unit has landed. */
63
+ export const FLEET_SKELETON = {
64
+ /** Three bars, the height of a row with its padding. */
65
+ rows: 3,
66
+ height: 44,
67
+ /** A MULTIPLE, for the same reason the row's is. */
68
+ radiusMultiple: 1,
69
+ gapUnits: 1,
70
+ } as const;
71
+
72
+ /**
73
+ * How long a fix stays `live`, and then `lagging`, when the caller says nothing.
74
+ *
75
+ * Ninety seconds and five minutes: sized for a phone reporting every twenty
76
+ * seconds, which is the densest cadence a battery tolerates all day. They are
77
+ * DEFAULTS and not a rule — a fleet on five-minute trackers passes its own, and
78
+ * the props exist so it can.
79
+ */
80
+ export const FLEET_DEFAULT_LAGGING_AFTER_SECONDS = 90;
81
+ export const FLEET_DEFAULT_STALE_AFTER_SECONDS = 300;
@@ -0,0 +1,185 @@
1
+ import * as React from 'react';
2
+ import { StyleSheet, Text, View } from 'react-native';
3
+
4
+ import { EmptyState } from '../EmptyState/EmptyState.native';
5
+
6
+ import { useFleetMap } from './FleetMap.hooks';
7
+ import {
8
+ FLEET_DEFAULT_LAGGING_AFTER_SECONDS,
9
+ FLEET_DEFAULT_STALE_AFTER_SECONDS,
10
+ FLEET_PANEL,
11
+ } from './FleetMap.metrics';
12
+ import type { FleetMapProps } from './FleetMap.types.native';
13
+ import { FleetRoster } from './FleetRoster.native';
14
+ import { webAria, webRole } from '../../../platform/aria';
15
+ import { useUiTheme } from '../../../provider/use-ui-theme.native';
16
+ import { MUI_FONT_WEIGHT_MEDIUM, muiTypeStyle } from '../../../tokens/mui-type';
17
+
18
+ /**
19
+ * The reload announcement, and nothing else.
20
+ *
21
+ * A busy region marks itself stale for assistive technology but UTTERS nothing,
22
+ * and the skeletons beside it are hidden from the reader — so a screen-reader
23
+ * user got silence while the roster reloaded. This is the utterance.
24
+ *
25
+ * Optional by design: the sentence is in the reader's language, which only the
26
+ * caller knows, and inventing an English one in a component whose whole premise
27
+ * is that every word arrives through `copy` would be the bug it exists to
28
+ * avoid. No `copy.loading`, no region.
29
+ *
30
+ * ## The node outlives the text, and that is the whole trick
31
+ *
32
+ * A live region has to exist BEFORE its content changes; a reader watches an
33
+ * existing region for mutations rather than announcing one that appears already
34
+ * populated. Mounting region-and-text together in a single commit is the classic
35
+ * silent case — it satisfies a query while announcing nothing. So the region
36
+ * mounts as soon as the caller offers the copy and merely EMPTIES when idle.
37
+ *
38
+ * `role="status"` and `aria-live` reach the DOM through react-native-web; on a
39
+ * device the announcement is made by `AccessibilityInfo` or not at all, which
40
+ * is recorded in `NATIVE-NOTES.md` rather than papered over here.
41
+ */
42
+ function FleetStatus({
43
+ announcement,
44
+ loading,
45
+ testId,
46
+ }: {
47
+ announcement: string | undefined;
48
+ loading: boolean;
49
+ testId: string;
50
+ }): React.JSX.Element | null {
51
+ // No copy, no region at all — the caller has not asked for an announcement.
52
+ if (announcement === undefined) return null;
53
+ return (
54
+ <View
55
+ {...webRole('status')}
56
+ aria-live="polite"
57
+ accessibilityLiveRegion="polite"
58
+ testID={`${testId}-status`}
59
+ style={styles.srOnly}
60
+ >
61
+ <Text>{loading ? announcement : ''}</Text>
62
+ </View>
63
+ );
64
+ }
65
+
66
+ /**
67
+ * FleetMap on React Native — where a set of tracked units is right now.
68
+ *
69
+ * Product-free by construction: it draws a labelled row with a freshness, and
70
+ * every word — the heading, the three freshness states, how a duration and a
71
+ * distance are written — arrives through `copy`. Couriers, service vans and
72
+ * field engineers are all the same list.
73
+ *
74
+ * ## What this renderer does NOT draw, and why
75
+ *
76
+ * There is no map. `MapPreview` — 2,200 lines that project, tile and decorate
77
+ * their own surface — has no React Native build, and porting it is its own
78
+ * change with its own consumers. What that costs is smaller than it sounds,
79
+ * because the web half already calls this list "the accessible representation,
80
+ * not a sidebar": every fact a pin carries — who, how recently, how precisely,
81
+ * what they are carrying — is on a row, in an order the map cannot express. So
82
+ * a native board is the half a screen reader was always given, and a device
83
+ * gets the same answers a dispatcher asks for. The gap is stated in
84
+ * `NATIVE-NOTES.md` rather than claimed away, and it is where the map goes when
85
+ * `MapPreview` is ported.
86
+ *
87
+ * The consequence for the API is one line in `FleetMap.types.native.ts`: the
88
+ * native copy interface has no `map` and no `mapLabel`, because a caller should
89
+ * not be asked to name controls nothing renders.
90
+ */
91
+ export const FleetMap: React.FC<FleetMapProps> = React.memo(
92
+ ({
93
+ units,
94
+ copy,
95
+ // NO default. `undefined` is what tells the hook the caller does not own
96
+ // the selection; defaulting it to `null` made every render look controlled
97
+ // and the uncontrolled path unreachable.
98
+ selectedId,
99
+ onSelect,
100
+ laggingAfterSeconds = FLEET_DEFAULT_LAGGING_AFTER_SECONDS,
101
+ staleAfterSeconds = FLEET_DEFAULT_STALE_AFTER_SECONDS,
102
+ loading = false,
103
+ maxHeight,
104
+ style,
105
+ dataTestId,
106
+ }) => {
107
+ const theme = useUiTheme();
108
+ const testId = dataTestId ?? 'fleet-map';
109
+ const { ordered, active, select, onKeyDown } = useFleetMap(units, selectedId, onSelect);
110
+
111
+ return (
112
+ <View
113
+ {...webRole('region')}
114
+ accessibilityLabel={copy.title}
115
+ testID={testId}
116
+ style={[styles.panel, { gap: theme.spacing(FLEET_PANEL.gapUnits) }, style]}
117
+ >
118
+ <Text
119
+ {...webRole('heading')}
120
+ {...webAria({ 'aria-level': 2 })}
121
+ testID={`${testId}-title`}
122
+ style={[
123
+ muiTypeStyle(theme, 'body1'),
124
+ { color: theme.palette.text.primary, fontWeight: `${MUI_FONT_WEIGHT_MEDIUM}` },
125
+ ]}
126
+ >
127
+ {copy.title}
128
+ </Text>
129
+
130
+ <FleetStatus announcement={copy.loading} loading={loading} testId={testId} />
131
+
132
+ {!loading && ordered.length === 0 ? (
133
+ <EmptyState
134
+ variant="minimal"
135
+ title={copy.emptyTitle}
136
+ description={copy.emptyDescription}
137
+ dataTestId={`${testId}-empty`}
138
+ />
139
+ ) : (
140
+ /* The busy marker belongs on the BODY and never on the panel root —
141
+ the live region announcing the reload is a sibling above, and a
142
+ live region inside a busy subtree is exactly what busy tells
143
+ assistive technology to hold back. Marking the root would silence
144
+ the announcement for the whole of the only window in which it has
145
+ anything to say. The web half splits them the same way, and the
146
+ wrapper is also what the shared loading stories address. */
147
+ <View
148
+ aria-busy={loading || undefined}
149
+ accessibilityState={{ busy: loading }}
150
+ testID={`${testId}-body`}
151
+ style={styles.body}
152
+ >
153
+ <FleetRoster
154
+ units={ordered}
155
+ copy={copy}
156
+ selectedId={active}
157
+ onSelect={select}
158
+ laggingAfterSeconds={laggingAfterSeconds}
159
+ staleAfterSeconds={staleAfterSeconds}
160
+ loading={loading}
161
+ maxHeight={maxHeight}
162
+ testId={testId}
163
+ onKeyDown={onKeyDown}
164
+ />
165
+ </View>
166
+ )}
167
+ </View>
168
+ );
169
+ },
170
+ );
171
+
172
+ FleetMap.displayName = 'FleetMap';
173
+
174
+ const styles = StyleSheet.create({
175
+ body: { flexShrink: 1, minWidth: 0 },
176
+ panel: { flexDirection: 'column', minWidth: 0 },
177
+ /**
178
+ * Off screen but still in the tree, which is what a live region needs. A
179
+ * `display: 'none'` view is not announced, and a zero-size one clips its text
180
+ * on some readers.
181
+ */
182
+ srOnly: { height: 1, opacity: 0, overflow: 'hidden', position: 'absolute', width: 1 },
183
+ });
184
+
185
+ export default FleetMap;
@@ -8,19 +8,13 @@ import { EmptyState } from '../EmptyState/EmptyState';
8
8
 
9
9
  import { FleetBody } from './FleetBody';
10
10
  import { useFleetMap } from './FleetMap.hooks';
11
+ import {
12
+ FLEET_DEFAULT_LAGGING_AFTER_SECONDS,
13
+ FLEET_DEFAULT_STALE_AFTER_SECONDS,
14
+ FLEET_PANEL,
15
+ } from './FleetMap.metrics';
11
16
  import type { FleetMapProps } from './FleetMap.types';
12
17
 
13
- /**
14
- * How long a fix stays `live`, and then `lagging`, when the caller says nothing.
15
- *
16
- * Ninety seconds and five minutes: sized for a phone reporting every twenty
17
- * seconds, which is the densest cadence a battery tolerates all day. They are
18
- * DEFAULTS and not a rule — a fleet on five-minute trackers passes its own, and
19
- * the props exist so it can.
20
- */
21
- const DEFAULT_LAGGING_AFTER_SECONDS = 90;
22
- const DEFAULT_STALE_AFTER_SECONDS = 300;
23
-
24
18
  /** The map's own default height. Overridable, and never a hardcoded viewport. */
25
19
  const DEFAULT_HEIGHT = '420px';
26
20
 
@@ -100,8 +94,8 @@ export const FleetMap: React.FC<FleetMapProps> = React.memo(
100
94
  // controlled and the uncontrolled path unreachable.
101
95
  selectedId,
102
96
  onSelect,
103
- laggingAfterSeconds = DEFAULT_LAGGING_AFTER_SECONDS,
104
- staleAfterSeconds = DEFAULT_STALE_AFTER_SECONDS,
97
+ laggingAfterSeconds = FLEET_DEFAULT_LAGGING_AFTER_SECONDS,
98
+ staleAfterSeconds = FLEET_DEFAULT_STALE_AFTER_SECONDS,
105
99
  height = DEFAULT_HEIGHT,
106
100
  loading = false,
107
101
  className,
@@ -122,7 +116,7 @@ export const FleetMap: React.FC<FleetMapProps> = React.memo(
122
116
  aria-labelledby={headingId}
123
117
  className={className}
124
118
  data-testid={testId}
125
- sx={{ display: 'flex', flexDirection: 'column', gap: theme.spacing(1.5), minWidth: 0 }}
119
+ sx={{ display: 'flex', flexDirection: 'column', gap: theme.spacing(FLEET_PANEL.gapUnits), minWidth: 0 }}
126
120
  >
127
121
  <Typography
128
122
  id={headingId}