@elaraai/e3-ui 1.0.86 → 1.0.87

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 (385) hide show
  1. package/dist/src/bind/data-indexed.d.ts +159 -0
  2. package/dist/src/bind/data-indexed.d.ts.map +1 -0
  3. package/dist/src/bind/data-indexed.js +105 -0
  4. package/dist/src/bind/data-indexed.js.map +1 -0
  5. package/dist/src/bind/data.d.ts +5 -3
  6. package/dist/src/bind/data.d.ts.map +1 -1
  7. package/dist/src/bind/data.js +6 -0
  8. package/dist/src/bind/data.js.map +1 -1
  9. package/dist/src/bind/record.d.ts +34 -23
  10. package/dist/src/bind/record.d.ts.map +1 -1
  11. package/dist/src/bind/record.js +46 -19
  12. package/dist/src/bind/record.js.map +1 -1
  13. package/dist/src/calendar/index.d.ts +2923 -0
  14. package/dist/src/calendar/index.d.ts.map +1 -0
  15. package/dist/src/calendar/index.js +135 -0
  16. package/dist/src/calendar/index.js.map +1 -0
  17. package/dist/src/decision/queue.d.ts +3 -3
  18. package/dist/src/decision/queue.d.ts.map +1 -1
  19. package/dist/src/decision/queue.js +2 -2
  20. package/dist/src/decision/queue.js.map +1 -1
  21. package/dist/src/decision/types.d.ts +4 -0
  22. package/dist/src/decision/types.d.ts.map +1 -1
  23. package/dist/src/decision/types.js +4 -0
  24. package/dist/src/decision/types.js.map +1 -1
  25. package/dist/src/flowchart/canvas.d.ts +114 -0
  26. package/dist/src/flowchart/canvas.d.ts.map +1 -0
  27. package/dist/src/flowchart/canvas.js +84 -0
  28. package/dist/src/flowchart/canvas.js.map +1 -0
  29. package/dist/src/flowchart/flowchart.d.ts +79 -0
  30. package/dist/src/flowchart/flowchart.d.ts.map +1 -0
  31. package/dist/src/flowchart/flowchart.js +9 -0
  32. package/dist/src/flowchart/flowchart.js.map +1 -0
  33. package/dist/src/flowchart/index.d.ts +305 -0
  34. package/dist/src/flowchart/index.d.ts.map +1 -0
  35. package/dist/src/flowchart/index.js +267 -0
  36. package/dist/src/flowchart/index.js.map +1 -0
  37. package/dist/src/flowchart/library.d.ts +720 -0
  38. package/dist/src/flowchart/library.d.ts.map +1 -0
  39. package/dist/src/flowchart/library.js +611 -0
  40. package/dist/src/flowchart/library.js.map +1 -0
  41. package/dist/src/flowchart/over.d.ts +234 -0
  42. package/dist/src/flowchart/over.d.ts.map +1 -0
  43. package/dist/src/flowchart/over.js +220 -0
  44. package/dist/src/flowchart/over.js.map +1 -0
  45. package/dist/src/flowchart/patch.d.ts +64 -0
  46. package/dist/src/flowchart/patch.d.ts.map +1 -0
  47. package/dist/src/flowchart/patch.js +67 -0
  48. package/dist/src/flowchart/patch.js.map +1 -0
  49. package/dist/src/flowchart/payload.d.ts +8220 -0
  50. package/dist/src/flowchart/payload.d.ts.map +1 -0
  51. package/dist/src/flowchart/payload.js +521 -0
  52. package/dist/src/flowchart/payload.js.map +1 -0
  53. package/dist/src/flowchart/types.d.ts +393 -0
  54. package/dist/src/flowchart/types.d.ts.map +1 -0
  55. package/dist/src/flowchart/types.js +216 -0
  56. package/dist/src/flowchart/types.js.map +1 -0
  57. package/dist/src/flowchart/values.d.ts +267 -0
  58. package/dist/src/flowchart/values.d.ts.map +1 -0
  59. package/dist/src/flowchart/values.js +216 -0
  60. package/dist/src/flowchart/values.js.map +1 -0
  61. package/dist/src/flowchart/window.d.ts +521 -0
  62. package/dist/src/flowchart/window.d.ts.map +1 -0
  63. package/dist/src/flowchart/window.js +69 -0
  64. package/dist/src/flowchart/window.js.map +1 -0
  65. package/dist/src/index.d.ts +25 -1
  66. package/dist/src/index.d.ts.map +1 -1
  67. package/dist/src/index.js +48 -1
  68. package/dist/src/index.js.map +1 -1
  69. package/dist/src/internal.d.ts +35 -3
  70. package/dist/src/internal.d.ts.map +1 -1
  71. package/dist/src/internal.js +72 -3
  72. package/dist/src/internal.js.map +1 -1
  73. package/dist/src/plan/assemble.d.ts +2224 -0
  74. package/dist/src/plan/assemble.d.ts.map +1 -0
  75. package/dist/src/plan/assemble.js +210 -0
  76. package/dist/src/plan/assemble.js.map +1 -0
  77. package/dist/src/plan/builders.d.ts +790 -0
  78. package/dist/src/plan/builders.d.ts.map +1 -0
  79. package/dist/src/plan/builders.js +749 -0
  80. package/dist/src/plan/builders.js.map +1 -0
  81. package/dist/src/plan/event-rows.d.ts +2565 -0
  82. package/dist/src/plan/event-rows.d.ts.map +1 -0
  83. package/dist/src/plan/event-rows.js +689 -0
  84. package/dist/src/plan/event-rows.js.map +1 -0
  85. package/dist/src/plan/factories.d.ts +458 -0
  86. package/dist/src/plan/factories.d.ts.map +1 -0
  87. package/dist/src/plan/factories.js +484 -0
  88. package/dist/src/plan/factories.js.map +1 -0
  89. package/dist/src/plan/index.d.ts +672 -0
  90. package/dist/src/plan/index.d.ts.map +1 -0
  91. package/dist/src/plan/index.js +532 -0
  92. package/dist/src/plan/index.js.map +1 -0
  93. package/dist/src/plan/ir.d.ts +2594 -0
  94. package/dist/src/plan/ir.d.ts.map +1 -0
  95. package/dist/src/plan/ir.js +78 -0
  96. package/dist/src/plan/ir.js.map +1 -0
  97. package/dist/src/plan/library.d.ts +772 -0
  98. package/dist/src/plan/library.d.ts.map +1 -0
  99. package/dist/src/plan/library.js +424 -0
  100. package/dist/src/plan/library.js.map +1 -0
  101. package/dist/src/plan/ordered-source.d.ts +27 -0
  102. package/dist/src/plan/ordered-source.d.ts.map +1 -0
  103. package/dist/src/plan/ordered-source.js +48 -0
  104. package/dist/src/plan/ordered-source.js.map +1 -0
  105. package/dist/src/plan/over.d.ts +247 -0
  106. package/dist/src/plan/over.d.ts.map +1 -0
  107. package/dist/src/plan/over.js +266 -0
  108. package/dist/src/plan/over.js.map +1 -0
  109. package/dist/src/plan/pick.d.ts +78 -0
  110. package/dist/src/plan/pick.d.ts.map +1 -0
  111. package/dist/src/plan/pick.js +146 -0
  112. package/dist/src/plan/pick.js.map +1 -0
  113. package/dist/src/plan/plan.d.ts +11611 -0
  114. package/dist/src/plan/plan.d.ts.map +1 -0
  115. package/dist/src/plan/plan.js +428 -0
  116. package/dist/src/plan/plan.js.map +1 -0
  117. package/dist/src/plan/refs.d.ts +218 -0
  118. package/dist/src/plan/refs.d.ts.map +1 -0
  119. package/dist/src/plan/refs.js +226 -0
  120. package/dist/src/plan/refs.js.map +1 -0
  121. package/dist/src/plan/root.d.ts +290 -0
  122. package/dist/src/plan/root.d.ts.map +1 -0
  123. package/dist/src/plan/root.js +463 -0
  124. package/dist/src/plan/root.js.map +1 -0
  125. package/dist/src/plan/series.d.ts +13554 -0
  126. package/dist/src/plan/series.d.ts.map +1 -0
  127. package/dist/src/plan/series.js +1921 -0
  128. package/dist/src/plan/series.js.map +1 -0
  129. package/dist/src/plan/types.d.ts +12400 -0
  130. package/dist/src/plan/types.d.ts.map +1 -0
  131. package/dist/src/plan/types.js +1335 -0
  132. package/dist/src/plan/types.js.map +1 -0
  133. package/dist/src/roster/coverage.d.ts +240 -0
  134. package/dist/src/roster/coverage.d.ts.map +1 -0
  135. package/dist/src/roster/coverage.js +109 -0
  136. package/dist/src/roster/coverage.js.map +1 -0
  137. package/dist/src/roster/index.d.ts +4028 -0
  138. package/dist/src/roster/index.d.ts.map +1 -0
  139. package/dist/src/roster/index.js +115 -0
  140. package/dist/src/roster/index.js.map +1 -0
  141. package/dist/src/roster/library.d.ts +106 -0
  142. package/dist/src/roster/library.d.ts.map +1 -0
  143. package/dist/src/roster/library.js +78 -0
  144. package/dist/src/roster/library.js.map +1 -0
  145. package/dist/src/roster/people.d.ts +50 -0
  146. package/dist/src/roster/people.d.ts.map +1 -0
  147. package/dist/src/roster/people.js +32 -0
  148. package/dist/src/roster/people.js.map +1 -0
  149. package/dist/src/roster/rules.d.ts +275 -0
  150. package/dist/src/roster/rules.d.ts.map +1 -0
  151. package/dist/src/roster/rules.js +162 -0
  152. package/dist/src/roster/rules.js.map +1 -0
  153. package/dist/src/roster/types.d.ts +672 -0
  154. package/dist/src/roster/types.d.ts.map +1 -0
  155. package/dist/src/roster/types.js +248 -0
  156. package/dist/src/roster/types.js.map +1 -0
  157. package/dist/src/roster/visibility.d.ts +66 -0
  158. package/dist/src/roster/visibility.d.ts.map +1 -0
  159. package/dist/src/roster/visibility.js +25 -0
  160. package/dist/src/roster/visibility.js.map +1 -0
  161. package/dist/src/schedule/days.d.ts +54 -0
  162. package/dist/src/schedule/days.d.ts.map +1 -0
  163. package/dist/src/schedule/days.js +68 -0
  164. package/dist/src/schedule/days.js.map +1 -0
  165. package/dist/src/schedule/events.d.ts +433 -0
  166. package/dist/src/schedule/events.d.ts.map +1 -0
  167. package/dist/src/schedule/events.js +851 -0
  168. package/dist/src/schedule/events.js.map +1 -0
  169. package/dist/src/schedule/index.d.ts +150 -0
  170. package/dist/src/schedule/index.d.ts.map +1 -0
  171. package/dist/src/schedule/index.js +132 -0
  172. package/dist/src/schedule/index.js.map +1 -0
  173. package/dist/src/schedule/library.d.ts +70 -0
  174. package/dist/src/schedule/library.d.ts.map +1 -0
  175. package/dist/src/schedule/library.js +102 -0
  176. package/dist/src/schedule/library.js.map +1 -0
  177. package/dist/src/schedule/patch.d.ts +74 -0
  178. package/dist/src/schedule/patch.d.ts.map +1 -0
  179. package/dist/src/schedule/patch.js +68 -0
  180. package/dist/src/schedule/patch.js.map +1 -0
  181. package/dist/src/schedule/record-window.d.ts +189 -0
  182. package/dist/src/schedule/record-window.d.ts.map +1 -0
  183. package/dist/src/schedule/record-window.js +95 -0
  184. package/dist/src/schedule/record-window.js.map +1 -0
  185. package/dist/src/schedule/resources.d.ts +165 -0
  186. package/dist/src/schedule/resources.d.ts.map +1 -0
  187. package/dist/src/schedule/resources.js +234 -0
  188. package/dist/src/schedule/resources.js.map +1 -0
  189. package/dist/src/schedule/templates.d.ts +47 -0
  190. package/dist/src/schedule/templates.d.ts.map +1 -0
  191. package/dist/src/schedule/templates.js +61 -0
  192. package/dist/src/schedule/templates.js.map +1 -0
  193. package/dist/src/schedule/types.d.ts +1677 -0
  194. package/dist/src/schedule/types.d.ts.map +1 -0
  195. package/dist/src/schedule/types.js +374 -0
  196. package/dist/src/schedule/types.js.map +1 -0
  197. package/dist/src/schedule/window.d.ts +154 -0
  198. package/dist/src/schedule/window.d.ts.map +1 -0
  199. package/dist/src/schedule/window.js +363 -0
  200. package/dist/src/schedule/window.js.map +1 -0
  201. package/dist/src/sheet/bridge.d.ts +385 -0
  202. package/dist/src/sheet/bridge.d.ts.map +1 -0
  203. package/dist/src/sheet/bridge.js +809 -0
  204. package/dist/src/sheet/bridge.js.map +1 -0
  205. package/dist/src/sheet/columns.d.ts +410 -0
  206. package/dist/src/sheet/columns.d.ts.map +1 -0
  207. package/dist/src/sheet/columns.js +99 -0
  208. package/dist/src/sheet/columns.js.map +1 -0
  209. package/dist/src/sheet/context-bridge.d.ts +48 -0
  210. package/dist/src/sheet/context-bridge.d.ts.map +1 -0
  211. package/dist/src/sheet/context-bridge.js +216 -0
  212. package/dist/src/sheet/context-bridge.js.map +1 -0
  213. package/dist/src/sheet/draft-bridge.d.ts +37 -0
  214. package/dist/src/sheet/draft-bridge.d.ts.map +1 -0
  215. package/dist/src/sheet/draft-bridge.js +153 -0
  216. package/dist/src/sheet/draft-bridge.js.map +1 -0
  217. package/dist/src/sheet/drafts.d.ts +14 -0
  218. package/dist/src/sheet/drafts.d.ts.map +1 -0
  219. package/dist/src/sheet/drafts.js +14 -0
  220. package/dist/src/sheet/drafts.js.map +1 -0
  221. package/dist/src/sheet/editing-bridge.d.ts +40 -0
  222. package/dist/src/sheet/editing-bridge.d.ts.map +1 -0
  223. package/dist/src/sheet/editing-bridge.js +231 -0
  224. package/dist/src/sheet/editing-bridge.js.map +1 -0
  225. package/dist/src/sheet/editing-types.d.ts +268 -0
  226. package/dist/src/sheet/editing-types.d.ts.map +1 -0
  227. package/dist/src/sheet/editing-types.js +34 -0
  228. package/dist/src/sheet/editing-types.js.map +1 -0
  229. package/dist/src/sheet/edits.d.ts +37 -0
  230. package/dist/src/sheet/edits.d.ts.map +1 -0
  231. package/dist/src/sheet/edits.js +31 -0
  232. package/dist/src/sheet/edits.js.map +1 -0
  233. package/dist/src/sheet/fields.d.ts +693 -0
  234. package/dist/src/sheet/fields.d.ts.map +1 -0
  235. package/dist/src/sheet/fields.js +280 -0
  236. package/dist/src/sheet/fields.js.map +1 -0
  237. package/dist/src/sheet/group.d.ts +165 -0
  238. package/dist/src/sheet/group.d.ts.map +1 -0
  239. package/dist/src/sheet/group.js +52 -0
  240. package/dist/src/sheet/group.js.map +1 -0
  241. package/dist/src/sheet/index.d.ts +542 -0
  242. package/dist/src/sheet/index.d.ts.map +1 -0
  243. package/dist/src/sheet/index.js +387 -0
  244. package/dist/src/sheet/index.js.map +1 -0
  245. package/dist/src/sheet/library.d.ts +284 -0
  246. package/dist/src/sheet/library.d.ts.map +1 -0
  247. package/dist/src/sheet/library.js +259 -0
  248. package/dist/src/sheet/library.js.map +1 -0
  249. package/dist/src/sheet/link.d.ts +398 -0
  250. package/dist/src/sheet/link.d.ts.map +1 -0
  251. package/dist/src/sheet/link.js +228 -0
  252. package/dist/src/sheet/link.js.map +1 -0
  253. package/dist/src/sheet/record.d.ts +75 -0
  254. package/dist/src/sheet/record.d.ts.map +1 -0
  255. package/dist/src/sheet/record.js +128 -0
  256. package/dist/src/sheet/record.js.map +1 -0
  257. package/dist/src/sheet/registers.d.ts +681 -0
  258. package/dist/src/sheet/registers.d.ts.map +1 -0
  259. package/dist/src/sheet/registers.js +671 -0
  260. package/dist/src/sheet/registers.js.map +1 -0
  261. package/dist/src/sheet/root.d.ts +329 -0
  262. package/dist/src/sheet/root.d.ts.map +1 -0
  263. package/dist/src/sheet/root.js +511 -0
  264. package/dist/src/sheet/root.js.map +1 -0
  265. package/dist/src/sheet/seed-bridge.d.ts +14 -0
  266. package/dist/src/sheet/seed-bridge.d.ts.map +1 -0
  267. package/dist/src/sheet/seed-bridge.js +51 -0
  268. package/dist/src/sheet/seed-bridge.js.map +1 -0
  269. package/dist/src/sheet/sheet.d.ts +11819 -0
  270. package/dist/src/sheet/sheet.d.ts.map +1 -0
  271. package/dist/src/sheet/sheet.js +236 -0
  272. package/dist/src/sheet/sheet.js.map +1 -0
  273. package/dist/src/sheet/sub-rows.d.ts +299 -0
  274. package/dist/src/sheet/sub-rows.d.ts.map +1 -0
  275. package/dist/src/sheet/sub-rows.js +272 -0
  276. package/dist/src/sheet/sub-rows.js.map +1 -0
  277. package/dist/src/sheet/templates.d.ts +161 -0
  278. package/dist/src/sheet/templates.d.ts.map +1 -0
  279. package/dist/src/sheet/templates.js +122 -0
  280. package/dist/src/sheet/templates.js.map +1 -0
  281. package/dist/src/sheet/transactions.d.ts +76 -0
  282. package/dist/src/sheet/transactions.d.ts.map +1 -0
  283. package/dist/src/sheet/transactions.js +40 -0
  284. package/dist/src/sheet/transactions.js.map +1 -0
  285. package/dist/src/sheet/types.d.ts +15226 -0
  286. package/dist/src/sheet/types.d.ts.map +1 -0
  287. package/dist/src/sheet/types.js +1070 -0
  288. package/dist/src/sheet/types.js.map +1 -0
  289. package/dist/src/sheet/views.d.ts +29 -0
  290. package/dist/src/sheet/views.d.ts.map +1 -0
  291. package/dist/src/sheet/views.js +43 -0
  292. package/dist/src/sheet/views.js.map +1 -0
  293. package/dist/src/studio/component.d.ts.map +1 -1
  294. package/dist/src/studio/component.js +11 -8
  295. package/dist/src/studio/component.js.map +1 -1
  296. package/dist/src/utils/derive.d.ts +134 -8
  297. package/dist/src/utils/derive.d.ts.map +1 -1
  298. package/dist/src/utils/derive.js +409 -7
  299. package/dist/src/utils/derive.js.map +1 -1
  300. package/dist/src/utils/ordered-source.d.ts +38 -0
  301. package/dist/src/utils/ordered-source.d.ts.map +1 -0
  302. package/dist/src/utils/ordered-source.js +45 -0
  303. package/dist/src/utils/ordered-source.js.map +1 -0
  304. package/dist/src/utils/row-inspector.d.ts +36 -0
  305. package/dist/src/utils/row-inspector.d.ts.map +1 -0
  306. package/dist/src/utils/row-inspector.js +49 -0
  307. package/dist/src/utils/row-inspector.js.map +1 -0
  308. package/dist/test/bind/data/data.examples.d.ts +21 -19
  309. package/dist/test/bind/data/data.examples.d.ts.map +1 -1
  310. package/dist/test/bind/data/data.examples.js +122 -122
  311. package/dist/test/bind/data/data.examples.js.map +1 -1
  312. package/dist/test/bind/record/record.examples.d.ts.map +1 -1
  313. package/dist/test/bind/record/record.examples.js +9 -9
  314. package/dist/test/bind/record/record.examples.js.map +1 -1
  315. package/dist/test/calendar/calendar-records.examples.d.ts +228 -0
  316. package/dist/test/calendar/calendar-records.examples.d.ts.map +1 -0
  317. package/dist/test/calendar/calendar-records.examples.js +91 -0
  318. package/dist/test/calendar/calendar-records.examples.js.map +1 -0
  319. package/dist/test/calendar/calendar.examples.d.ts +776 -0
  320. package/dist/test/calendar/calendar.examples.d.ts.map +1 -0
  321. package/dist/test/calendar/calendar.examples.js +336 -0
  322. package/dist/test/calendar/calendar.examples.js.map +1 -0
  323. package/dist/test/decision/journal.examples.d.ts +28 -28
  324. package/dist/test/decision/journal.examples.d.ts.map +1 -1
  325. package/dist/test/decision/journal.examples.js +4 -4
  326. package/dist/test/decision/journal.examples.js.map +1 -1
  327. package/dist/test/decision/queue.examples.d.ts.map +1 -1
  328. package/dist/test/decision/queue.examples.js +5 -7
  329. package/dist/test/decision/queue.examples.js.map +1 -1
  330. package/dist/test/flowchart/flowchart.examples.d.ts +3713 -0
  331. package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -0
  332. package/dist/test/flowchart/flowchart.examples.js +465 -0
  333. package/dist/test/flowchart/flowchart.examples.js.map +1 -0
  334. package/dist/test/plan/plan-events.examples.d.ts +3666 -0
  335. package/dist/test/plan/plan-events.examples.d.ts.map +1 -0
  336. package/dist/test/plan/plan-events.examples.js +773 -0
  337. package/dist/test/plan/plan-events.examples.js.map +1 -0
  338. package/dist/test/plan/plan-records.examples.d.ts +3 -0
  339. package/dist/test/plan/plan-records.examples.d.ts.map +1 -0
  340. package/dist/test/plan/plan-records.examples.js +45 -0
  341. package/dist/test/plan/plan-records.examples.js.map +1 -0
  342. package/dist/test/plan/plan-windows.examples.d.ts +588 -0
  343. package/dist/test/plan/plan-windows.examples.d.ts.map +1 -0
  344. package/dist/test/plan/plan-windows.examples.js +170 -0
  345. package/dist/test/plan/plan-windows.examples.js.map +1 -0
  346. package/dist/test/plan/plan.examples.d.ts +8095 -0
  347. package/dist/test/plan/plan.examples.d.ts.map +1 -0
  348. package/dist/test/plan/plan.examples.js +2500 -0
  349. package/dist/test/plan/plan.examples.js.map +1 -0
  350. package/dist/test/roster/domain.examples.d.ts +367 -0
  351. package/dist/test/roster/domain.examples.d.ts.map +1 -0
  352. package/dist/test/roster/domain.examples.js +72 -0
  353. package/dist/test/roster/domain.examples.js.map +1 -0
  354. package/dist/test/roster/roster-records.examples.d.ts +729 -0
  355. package/dist/test/roster/roster-records.examples.d.ts.map +1 -0
  356. package/dist/test/roster/roster-records.examples.js +86 -0
  357. package/dist/test/roster/roster-records.examples.js.map +1 -0
  358. package/dist/test/roster/roster.examples.d.ts +750 -0
  359. package/dist/test/roster/roster.examples.d.ts.map +1 -0
  360. package/dist/test/roster/roster.examples.js +198 -0
  361. package/dist/test/roster/roster.examples.js.map +1 -0
  362. package/dist/test/roster/warehouse.d.ts +87 -0
  363. package/dist/test/roster/warehouse.d.ts.map +1 -0
  364. package/dist/test/roster/warehouse.js +883 -0
  365. package/dist/test/roster/warehouse.js.map +1 -0
  366. package/dist/test/sheet/sheet-link.examples.d.ts +7 -0
  367. package/dist/test/sheet/sheet-link.examples.d.ts.map +1 -0
  368. package/dist/test/sheet/sheet-link.examples.js +41 -0
  369. package/dist/test/sheet/sheet-link.examples.js.map +1 -0
  370. package/dist/test/sheet/sheet-records.examples.d.ts +421 -0
  371. package/dist/test/sheet/sheet-records.examples.d.ts.map +1 -0
  372. package/dist/test/sheet/sheet-records.examples.js +79 -0
  373. package/dist/test/sheet/sheet-records.examples.js.map +1 -0
  374. package/dist/test/sheet/sheet-transactions.examples.d.ts +7 -0
  375. package/dist/test/sheet/sheet-transactions.examples.d.ts.map +1 -0
  376. package/dist/test/sheet/sheet-transactions.examples.js +58 -0
  377. package/dist/test/sheet/sheet-transactions.examples.js.map +1 -0
  378. package/dist/test/sheet/sheet.examples.d.ts +5562 -0
  379. package/dist/test/sheet/sheet.examples.d.ts.map +1 -0
  380. package/dist/test/sheet/sheet.examples.js +1178 -0
  381. package/dist/test/sheet/sheet.examples.js.map +1 -0
  382. package/dist/test/studio/studio.examples.d.ts.map +1 -1
  383. package/dist/test/studio/studio.examples.js +11 -11
  384. package/dist/test/studio/studio.examples.js.map +1 -1
  385. package/package.json +12 -9
@@ -0,0 +1,2500 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/e3-ui/jsx-runtime";
2
+ /**
3
+ * Copyright (c) 2025 Elara AI Pty Ltd
4
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
5
+ */
6
+ /** @jsxImportSource @elaraai/e3-ui */
7
+ import { ArrayType, BooleanType, DateTimeType, DictType, East, FloatType, IntegerType, NullType, OptionType, RecursiveType, StringType, StructType, VariantType, example, none, some, variant, } from "@elaraai/east";
8
+ import { DragEventType, Editing, EventStateType, State, StatusValueType, Style, UIComponentType } from "@elaraai/east-ui";
9
+ import { Badge, Box, Button, Chart, Configurator, Format, HStack, Progress, Reactive, SegmentGroup, Select, Slice, Sparkline, Text, VStack } from "@elaraai/east-ui";
10
+ import { Data, Plan, Record } from "@elaraai/e3-ui";
11
+ import e3 from "@elaraai/e3";
12
+ // The corpus — every canvas is DEFINED the one way (`Plan Data Interface.md`
13
+ // §3.5): `data` (RAW domain rows — print jobs, sheets, lifecycle states; row
14
+ // series discriminated by a field; no factory-built values in the data) +
15
+ // `series` (one `Plan.series.*` value per row series, `$.const`-bound and typed
16
+ // by the `Plan.Types.Series(Row)` constructor — its accessors DERIVE the
17
+ // canvas vocabulary from the raw fields client-side: labels, quantity
18
+ // displays, element values via the `Plan.run`/`chip`/`event` expression
19
+ // builders) + the root RESOLVER functions (`popover` / `hover` /
20
+ // `expandRender`). The series list IS the layout — one block per series, top
21
+ // to bottom — and hierarchy comes only from the data's own nesting (#822): a
22
+ // series' `children` walk what an entry holds, and a flat source is grouped in
23
+ // a data step first (`groupToDicts`). A row's id is its series and the path of
24
+ // entry keys to it (`Plan.ref`). The kind factories are subtree vocabulary only
25
+ // — the one-off chrome a `Plan.series.rows` entry places (`planTargetState`'s
26
+ // planned works).
27
+ //
28
+ // Every canvas binds its data from e3, so each runs on e3-web in the showcase
29
+ // (#1178): the source is an `e3.input` or an `e3.record` declared beside the
30
+ // example, its small literal default the dataset's initial value, bound in the
31
+ // body with `Data.bind`; a record an editing session commits to is paged with
32
+ // `Data.bindPaged` and written through its patch door (`Record.onApply`). A
33
+ // fixture made by a rule rather than written out — a slice's horizon, a
34
+ // stress source — is made where data is made, by an `e3.task` over a count.
35
+ // `State` holds only what the viewer owns: a configurator's axes, a pick, a
36
+ // gesture log, the interaction state. Instants are stored as instants, and a
37
+ // measure as its readings from its first week, which the series turn into
38
+ // points and cells; lifecycle states are plain `EventStateType` variants (the
39
+ // shared contracts vocabulary a plan dataset stores). Everything else an
40
+ // example needs — its series, axis, resolvers, presets and policy tables — is
41
+ // an East value bound once in its body. The declarations an example reaches
42
+ // travel with it into its docs and the plugin index.
43
+ // ============================================================================
44
+ // planTargetState — the data flagship: series over ONE raw source, every row
45
+ // kind, every series option, on a bounded canvas
46
+ // ============================================================================
47
+ /** A delivery on the horizon — the rows the slice narrows: when, which
48
+ * hall, whether it is at risk of running late, and the sheets booked so far
49
+ * (0 = nothing yet). */
50
+ export const TargetHorizonRow = StructType({ key: StringType, at: DateTimeType, hall: StringType, risk: StringType, sheets: FloatType });
51
+ /** How many deliveries the horizon holds — a small authored constant;
52
+ * {@link planTargetHorizon} makes the rows. */
53
+ export const planTargetHorizonCount = e3.input("plan_target_horizon_count", IntegerType, variant("value", 36n));
54
+ /**
55
+ * The horizon's deliveries, generated from their count and spread over
56
+ * the 27 weeks from W21: every third at risk of running late (the `Late
57
+ * risk` cohort counts 12 of 36), every seventh with no sheets booked yet (the
58
+ * `Empty` cohort).
59
+ */
60
+ export const generateTargetHorizon = East.function([IntegerType], ArrayType(TargetHorizonRow), ($, count) => {
61
+ // Monday of ISO week 1, 2026.
62
+ const w1 = $.const(new Date("2025-12-29T00:00:00Z"), DateTimeType);
63
+ return East.Array.generate(count, TargetHorizonRow, (_$, i) => ({
64
+ key: East.str `h${East.print(i.add(1n))}`,
65
+ at: w1.addWeeks(i.multiply(27n).divide(count).add(20n)),
66
+ hall: i.remainder(2n).equal(0n).ifElse(() => "Hall 1", () => "Hall 2"),
67
+ risk: i.remainder(3n).equal(0n).ifElse(() => "late", () => "on-time"),
68
+ sheets: i.remainder(7n).equal(6n).ifElse(() => 0.0, () => i.multiply(11n).remainder(40n).toFloat().add(20.0)),
69
+ }));
70
+ });
71
+ /** The task that generates the horizon — its output is the rows the slice narrows. */
72
+ export const planTargetHorizon = e3.task("plan_target_horizon", [planTargetHorizonCount], generateTargetHorizon);
73
+ /** A job on a press — the RAW record an ops dataset stores: its phase and
74
+ * optional job ticket, its window, optional sheets, lifecycle state and alert. */
75
+ export const TargetJob = StructType({
76
+ key: StringType, phase: StringType, ticket: OptionType(StringType),
77
+ start: DateTimeType, end: DateTimeType,
78
+ sheets: OptionType(FloatType), state: EventStateType,
79
+ alert: OptionType(StatusValueType),
80
+ });
81
+ /** A crew's shift — its window, its hours and its lifecycle state. */
82
+ export const TargetShift = StructType({
83
+ key: StringType, from: DateTimeType, to: DateTimeType,
84
+ hours: FloatType, state: EventStateType,
85
+ });
86
+ /** A van drop — its week and its lifecycle state. */
87
+ export const TargetAlloc = StructType({ key: StringType, at: DateTimeType, state: EventStateType });
88
+ /**
89
+ * The ops row — every series' rows in ONE keyed source, discriminated by the
90
+ * kind variant (the natural ops-dataset shape). The arms carry RAW fields: a
91
+ * measure is its weekly readings from its first week, and presence — a
92
+ * status, an expand declaration (`detail`) — is per-row Option data the
93
+ * accessors pass through. A row that stands in a hall names it (`hall`), and a
94
+ * data step groups those rows into their hall's entry (the `hall` arm, which
95
+ * the dataset itself never stores). The row is recursive, so the hierarchy is
96
+ * the data's own (#822): a hall holds its members, and a contract the presses
97
+ * it books (`presses`), to whatever depth the data has. A press names the span
98
+ * series that draws it (`series`): a hall's presses, the proposal flavours,
99
+ * the detail line, a contract rolled up by union or by status, a delivery.
100
+ */
101
+ export const TargetOpsRow = RecursiveType((self) => StructType({
102
+ hall: OptionType(StringType),
103
+ kind: VariantType({
104
+ kpi: StructType({ name: StringType, headline: StringType, pinned: BooleanType,
105
+ from: DateTimeType, weekly: ArrayType(FloatType), detail: OptionType(Plan.Types.Expand) }),
106
+ press: StructType({ series: StringType, rate: OptionType(FloatType), sub: OptionType(StringType),
107
+ status: OptionType(StatusValueType), detail: OptionType(Plan.Types.Expand),
108
+ jobs: ArrayType(TargetJob),
109
+ decisions: ArrayType(Plan.Types.DecisionMark),
110
+ ports: ArrayType(Plan.Types.Port),
111
+ presses: DictType(StringType, self) }),
112
+ load: StructType({ name: StringType, sub: StringType, from: DateTimeType,
113
+ weekly: ArrayType(OptionType(FloatType)), detail: OptionType(Plan.Types.Expand) }),
114
+ van: StructType({ name: StringType, region: StringType, sub: OptionType(StringType), detail: OptionType(Plan.Types.Expand),
115
+ lanes: ArrayType(Plan.Types.Lane), drops: ArrayType(TargetAlloc),
116
+ tiles: ArrayType(Plan.Types.BucketEvent), markers: ArrayType(Plan.Types.CellMarker) }),
117
+ crew: StructType({ name: StringType, pool: BooleanType, sub: OptionType(StringType), value: OptionType(StringType),
118
+ detail: OptionType(Plan.Types.Expand), shifts: ArrayType(TargetShift), chips: ArrayType(Plan.Types.Chip) }),
119
+ stream: StructType({ name: StringType, cadence: OptionType(StringType), detail: OptionType(Plan.Types.Expand),
120
+ marks: ArrayType(Plan.Types.EventMark) }),
121
+ tally: StructType({ name: StringType, detail: OptionType(Plan.Types.Expand), cells: ArrayType(Plan.Types.TableCell) }),
122
+ finisher: StructType({ name: StringType, jobs: ArrayType(TargetJob), util: ArrayType(FloatType),
123
+ sheets: ArrayType(Plan.Types.TableCell) }),
124
+ hall: StructType({ members: DictType(StringType, self) }),
125
+ }),
126
+ }));
127
+ /**
128
+ * The ONE ops source — every series' rows in one KEYED collection, RAW: no
129
+ * display strings the accessors can derive, no built elements; the instants an
130
+ * element record stores ride the axis's `time` arm. A key is its entry's
131
+ * identity, never its place: a row's id is the series that made it and the
132
+ * path of keys to it (`Plan.ref("presses", "Hall 1", "H1-P03")`), which is
133
+ * what `links`, `popover`, `ui` and `onSelect` speak — the layout is the
134
+ * series list. Weeks W27–W38.
135
+ */
136
+ export const planTargetOps = e3.input("plan_target_ops", DictType(StringType, TargetOpsRow), variant("value", new Map([
137
+ // ── The pinned KPI ──
138
+ ["ontime", { hall: none, kind: variant("kpi", { name: "On-time", headline: "94.2%", pinned: true,
139
+ from: new Date("2026-06-29T00:00:00Z"), weekly: [96.1, 96.4, 96.8, 97.0, 96.2, 95.1, 93.4, 91.0, 88.9, 91.4, 93.8, 94.2],
140
+ detail: some({ height: some("150px"), axis: variant("keep", null) }) }) }],
141
+ // ── Deliveries — Van 1's drops are raw, its tiles derived; Van 2's are
142
+ // stored tile records in its AM/PM lanes. Van 1's Aug 24 cell holds two
143
+ // drops, more than its room: a `+n` holds the rest. ──
144
+ ["van1", { hall: none, kind: variant("van", { name: "Van 1", region: "local", sub: some("drops/wk"), detail: none, lanes: [],
145
+ drops: [
146
+ { key: "a0", at: new Date("2026-06-29T00:00:00Z"), state: variant("confirmed", null) },
147
+ { key: "a1", at: new Date("2026-07-06T00:00:00Z"), state: variant("confirmed", null) },
148
+ { key: "a2", at: new Date("2026-07-13T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
149
+ { key: "a3", at: new Date("2026-07-20T00:00:00Z"), state: variant("confirmed", null) },
150
+ { key: "a4", at: new Date("2026-07-27T00:00:00Z"), state: variant("confirmed", null) },
151
+ { key: "a5", at: new Date("2026-08-03T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
152
+ { key: "a6", at: new Date("2026-08-10T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
153
+ { key: "a7", at: new Date("2026-08-24T00:00:00Z"), state: variant("confirmed", null) },
154
+ { key: "a8", at: new Date("2026-08-24T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
155
+ ],
156
+ tiles: [],
157
+ markers: [
158
+ { at: variant("time", new Date("2026-07-13T00:00:00Z")), lane: none, status: variant("warning", null), message: "capacity 90%" },
159
+ { at: variant("time", new Date("2026-08-24T00:00:00Z")), lane: none, status: variant("warning", null), message: "capacity breach — 2 drops" },
160
+ ] }) }],
161
+ ["van2", { hall: none, kind: variant("van", { name: "Van 2", region: "regional", sub: some("day · am/pm"),
162
+ detail: some({ height: some("112px"), axis: variant("keep", null) }),
163
+ lanes: [{ key: "am", label: some("AM") }, { key: "pm", label: some("PM") }],
164
+ drops: [],
165
+ tiles: [
166
+ { key: "m1", at: variant("time", new Date("2026-06-29T00:00:00Z")), lane: some("am"), label: none, icon: none, state: variant("confirmed", null),
167
+ tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none },
168
+ { key: "m2", at: variant("time", new Date("2026-06-29T00:00:00Z")), lane: some("pm"), label: none, icon: none, state: variant("confirmed", null),
169
+ tone: some(variant("warning", null)), color: none, colorPalette: none, stretch: none, content: none, animation: none },
170
+ { key: "m3", at: variant("time", new Date("2026-07-06T00:00:00Z")), lane: some("am"), label: none, icon: none, state: variant("proposed", variant("recommended", null)),
171
+ tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: some(variant("pulse", null)) },
172
+ { key: "m4", at: variant("time", new Date("2026-07-13T00:00:00Z")), lane: none, label: some("MIXED"), icon: none, state: variant("confirmed", null),
173
+ tone: none, color: none, colorPalette: none, stretch: some(variant("horizontal", null)),
174
+ content: some({ horizontal: some(variant("center", null)), vertical: none }), animation: none },
175
+ { key: "m5", at: variant("time", new Date("2026-07-20T00:00:00Z")), lane: some("pm"), label: none,
176
+ icon: some({ prefix: "fas", name: "truck", label: none, style: none }),
177
+ state: variant("proposed", variant("recommended", null)),
178
+ tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none },
179
+ { key: "m6", at: variant("time", new Date("2026-07-27T00:00:00Z")), lane: some("am"), label: some("PROOF"), icon: none, state: variant("estimated", null),
180
+ tone: none, color: none, colorPalette: none, stretch: none, content: none, animation: none },
181
+ // The colour channels: `color` is a raw token override,
182
+ // `colorPalette` recolours the whole lifecycle treatment.
183
+ { key: "m7", at: variant("time", new Date("2026-08-03T00:00:00Z")), lane: some("am"), label: some("S-A"), icon: none, state: variant("confirmed", null),
184
+ tone: none, color: some("teal.solid"), colorPalette: none, stretch: none, content: none, animation: none },
185
+ { key: "m8", at: variant("time", new Date("2026-08-03T00:00:00Z")), lane: some("pm"), label: some("S-B"), icon: none, state: variant("confirmed", null),
186
+ tone: none, color: none, colorPalette: some(variant("brand", null)), stretch: none, content: none, animation: none },
187
+ ],
188
+ markers: [{ at: variant("time", new Date("2026-07-13T00:00:00Z")), lane: none, status: variant("danger", null), message: "capacity breach" }] }) }],
189
+ // ── The halls' presses and loads: each names its hall, and the data
190
+ // step groups them under it. ──
191
+ ["H1-P03", { hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(12.0), sub: none,
192
+ status: some(variant("success", null)), detail: none,
193
+ jobs: [
194
+ { key: "mr", phase: "MAKEREADY", ticket: none, start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-06T00:00:00Z"), sheets: none, state: variant("actual", null), alert: none },
195
+ { key: "j4642", phase: "RUN", ticket: some("J-4642"), start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: some(96.0), state: variant("in-progress", null), alert: none },
196
+ { key: "wash", phase: "WASH-UP", ticket: none, start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-03T00:00:00Z"), sheets: none, state: variant("confirmed", null), alert: none },
197
+ { key: "j4663", phase: "RUN", ticket: some("J-4663"), start: new Date("2026-08-03T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: some(88.0), state: variant("proposed", variant("recommended", null)), alert: none },
198
+ ],
199
+ decisions: [{ key: "d1", at: variant("time", new Date("2026-08-03T00:00:00Z")), applied: false }],
200
+ ports: [{ at: variant("time", new Date("2026-07-27T00:00:00Z")), label: some("−24 k sheets") }],
201
+ presses: new Map() }) }],
202
+ ["H1-P04", { hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(12.0), sub: none, status: none,
203
+ // The expand declaration — a stored plain-data record; presence is a
204
+ // per-row fact, and the ROOT's expandRender mounts the body.
205
+ detail: some({ height: some("152px"), axis: variant("keep", null) }),
206
+ jobs: [
207
+ { key: "j4624", phase: "RUN", ticket: some("J-4624"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), sheets: some(112.0), state: variant("actual", null), alert: none },
208
+ { key: "plates", phase: "PLATES", ticket: none, start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: none, state: variant("confirmed", null), alert: none },
209
+ { key: "proof", phase: "PROOF", ticket: none, start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: none, state: variant("confirmed", null), alert: none },
210
+ { key: "j4693", phase: "RUN", ticket: some("J-4693"), start: new Date("2026-08-10T00:00:00Z"), end: new Date("2026-10-05T00:00:00Z"), sheets: some(104.0), state: variant("proposed", variant("recommended", null)), alert: none },
211
+ ],
212
+ decisions: [], ports: [], presses: new Map() }) }],
213
+ ["H1-P05", { hall: some("Hall 1"), kind: variant("press", { series: "presses", rate: some(8.0), sub: none,
214
+ status: some(variant("warning", null)), detail: none,
215
+ jobs: [
216
+ { key: "j4591", phase: "PLATES", ticket: some("J-4591"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: none, state: variant("actual", null), alert: some(variant("warning", null)) },
217
+ { key: "wash", phase: "WASH-UP", ticket: none, start: new Date("2026-08-17T00:00:00Z"), end: new Date("2026-08-31T00:00:00Z"), sheets: none, state: variant("proposed", variant("recommended", null)), alert: none },
218
+ ],
219
+ decisions: [], ports: [], presses: new Map() }) }],
220
+ // A load reading nothing in the week of 17 August: its cell is no data.
221
+ ["h1-load", { hall: some("Hall 1"), kind: variant("load", { name: "H1 load", sub: "%/wk", from: new Date("2026-06-29T00:00:00Z"),
222
+ weekly: [some(46.0), some(58.0), some(66.0), some(72.0), some(84.0), some(90.0), some(96.0), none, some(92.0), some(88.0), some(81.0), some(77.0)],
223
+ detail: some({ height: some("132px"), axis: variant("keep", null) }) }) }],
224
+ ["H2-P11", { hall: some("Hall 2"), kind: variant("press", { series: "presses", rate: some(10.0), sub: none, status: none, detail: none,
225
+ jobs: [{ key: "j4723", phase: "RUN", ticket: some("J-4723"), start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: some(92.0), state: variant("confirmed", null), alert: none }],
226
+ decisions: [], ports: [], presses: new Map() }) }],
227
+ ["H2-P12", { hall: some("Hall 2"), kind: variant("press", { series: "presses", rate: some(10.0), sub: none, status: none, detail: none,
228
+ jobs: [{ key: "j4594", phase: "RUN", ticket: some("J-4594"), start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), sheets: some(64.0), state: variant("proposed", variant("recommended", null)), alert: none }],
229
+ decisions: [], ports: [], presses: new Map() }) }],
230
+ ["h2-load", { hall: some("Hall 2"), kind: variant("load", { name: "H2 load", sub: "%/wk", from: new Date("2026-06-29T00:00:00Z"),
231
+ weekly: [some(46.0), some(52.0), some(58.0), some(61.0), some(66.0), some(72.0), some(78.0), some(84.0), some(90.0), some(96.0), some(98.0), some(92.0)],
232
+ detail: none }) }],
233
+ ["H3-P21", { hall: some("Hall 3"), kind: variant("press", { series: "presses", rate: some(6.0), sub: none, status: none, detail: none,
234
+ jobs: [{ key: "j4903", phase: "RUN", ticket: some("J-4903"), start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: some(48.0), state: variant("confirmed", null), alert: none }],
235
+ decisions: [], ports: [], presses: new Map() }) }],
236
+ ["H3-P22", { hall: some("Hall 3"), kind: variant("press", { series: "presses", rate: some(6.0), sub: none, status: none, detail: none,
237
+ jobs: [{ key: "j4906", phase: "RUN", ticket: some("J-4906"), start: new Date("2026-08-10T00:00:00Z"), end: new Date("2026-09-07T00:00:00Z"), sheets: some(54.0), state: variant("proposed", variant("recommended", null)), alert: none }],
238
+ decisions: [], ports: [], presses: new Map() }) }],
239
+ ["h3-load", { hall: some("Hall 3"), kind: variant("load", { name: "H3 load", sub: "%/wk", from: new Date("2026-06-29T00:00:00Z"),
240
+ weekly: [some(38.0), some(42.0), some(47.0), some(51.0), some(55.0), some(61.0), some(66.0), some(70.0), some(75.0), some(80.0), some(83.0), some(78.0)],
241
+ detail: none }) }],
242
+ // ── The span grammar: proposal flavours (a forecast ghost, a proposed
243
+ // cut, a declined run) ──
244
+ ["H1-P07", { hall: none, kind: variant("press", { series: "flavours", rate: some(8.0), sub: none, status: none, detail: none,
245
+ jobs: [
246
+ { key: "run", phase: "RUN", ticket: some("J-4580"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), sheets: some(64.0), state: variant("in-progress", null), alert: none },
247
+ { key: "gho", phase: "FORECAST", ticket: none, start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-03T00:00:00Z"), sheets: none, state: variant("estimated", null), alert: none },
248
+ { key: "rem", phase: "CUT", ticket: none, start: new Date("2026-08-10T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: none, state: variant("proposed", variant("removed", null)), alert: none },
249
+ { key: "rej", phase: "DECLINED", ticket: none, start: new Date("2026-08-31T00:00:00Z"), end: new Date("2026-09-14T00:00:00Z"), sheets: none, state: variant("rejected", null), alert: none },
250
+ ],
251
+ decisions: [], ports: [], presses: new Map() }) }],
252
+ // …sheets, an applied decision and a port on a two-line gutter, its
253
+ // expand declaration dimming the axis behind the render…
254
+ ["H1-P09", { hall: none, kind: variant("press", { series: "detail", rate: none, sub: some("12k sheets/h"), status: none,
255
+ detail: some({ height: some("152px"), axis: variant("dim", null) }),
256
+ jobs: [
257
+ { key: "a", phase: "RUN", ticket: some("J-4624"), start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: some(112.0), state: variant("actual", null), alert: none },
258
+ { key: "b", phase: "RUN", ticket: some("J-4693"), start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), sheets: some(104.0), state: variant("proposed", variant("recommended", null)), alert: none },
259
+ ],
260
+ decisions: [{ key: "d1", at: variant("time", new Date("2026-07-27T00:00:00Z")), applied: true }],
261
+ ports: [{ at: variant("time", new Date("2026-07-27T00:00:00Z")), label: some("−24 k sheets") }],
262
+ presses: new Map() }) }],
263
+ // …a contract and the presses it books — its row rolls their runs up
264
+ // into union bands (renderer-derived)…
265
+ ["Contract A", { hall: none, kind: variant("press", { series: "rollup", rate: none, sub: none, status: none, detail: none,
266
+ jobs: [], decisions: [], ports: [],
267
+ presses: new Map([
268
+ ["H1-P03", { hall: none, kind: variant("press", { series: "rollup", rate: none, sub: none, status: none, detail: none,
269
+ jobs: [
270
+ { key: "j4642", phase: "RUN", ticket: some("J-4642"), start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), sheets: some(96.0), state: variant("actual", null), alert: none },
271
+ { key: "j4663", phase: "RUN", ticket: some("J-4663"), start: new Date("2026-08-03T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), sheets: some(88.0), state: variant("proposed", variant("recommended", null)), alert: none },
272
+ ],
273
+ decisions: [], ports: [], presses: new Map() }) }],
274
+ ["H2-P11", { hall: none, kind: variant("press", { series: "rollup", rate: none, sub: none, status: none, detail: none,
275
+ jobs: [{ key: "j4723", phase: "RUN", ticket: some("J-4723"), start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: some(92.0), state: variant("confirmed", null), alert: none }],
276
+ decisions: [], ports: [], presses: new Map() }) }],
277
+ ]) }) }],
278
+ // …a linked delivery whose run starts BEYOND the window — in links focus
279
+ // its landing is a slot on the plot's edge…
280
+ ["dlv", { hall: none, kind: variant("press", { series: "delivery", rate: none, sub: none, status: none, detail: none,
281
+ jobs: [{ key: "d1", phase: "DELIVER", ticket: none, start: new Date("2026-09-21T00:00:00Z"), end: new Date("2026-10-12T00:00:00Z"), sheets: some(91.0), state: variant("proposed", variant("recommended", null)), alert: none }],
282
+ decisions: [], ports: [], presses: new Map() }) }],
283
+ // …and a contract rolled up by status.
284
+ ["Contract B", { hall: none, kind: variant("press", { series: "contracts", rate: none, sub: none, status: none, detail: none,
285
+ jobs: [], decisions: [], ports: [],
286
+ presses: new Map([
287
+ ["H2-P12", { hall: none, kind: variant("press", { series: "contracts", rate: none, sub: none, status: none, detail: none,
288
+ jobs: [
289
+ { key: "r1", phase: "RUN", ticket: some("J-4594"), start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-08-03T00:00:00Z"), sheets: some(64.0), state: variant("actual", null), alert: none },
290
+ { key: "r2", phase: "RUN", ticket: some("J-4606"), start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), sheets: some(40.0), state: variant("proposed", variant("recommended", null)), alert: none },
291
+ ],
292
+ decisions: [], ports: [], presses: new Map() }) }],
293
+ ]) }) }],
294
+ // ── A second heat series of the same kind, its own entry ──
295
+ ["quality", { hall: none, kind: variant("load", { name: "Quality index", sub: "%", from: new Date("2026-06-29T00:00:00Z"),
296
+ weekly: [some(94.0), some(95.0), some(96.0), some(95.0), some(97.0), some(96.0), some(93.0), some(92.0), some(94.0), some(95.0), some(96.0), some(97.0)],
297
+ detail: none }) }],
298
+ // ── Crews — Crew A's chips derive from raw shifts; the relief pool's are
299
+ // chip records stored as they draw. ──
300
+ ["crewA", { hall: none, kind: variant("crew", { name: "Crew A", pool: false, sub: some("152h → 168h"), value: none,
301
+ detail: some({ height: some("120px"), axis: variant("keep", null) }),
302
+ shifts: [
303
+ { key: "s1", from: new Date("2026-06-29T00:00:00Z"), to: new Date("2026-07-13T00:00:00Z"), hours: 80.0, state: variant("confirmed", null) },
304
+ { key: "s2", from: new Date("2026-07-13T00:00:00Z"), to: new Date("2026-07-27T00:00:00Z"), hours: 56.0, state: variant("proposed", variant("removed", null)) },
305
+ { key: "s3", from: new Date("2026-07-27T00:00:00Z"), to: new Date("2026-08-10T00:00:00Z"), hours: 64.0, state: variant("proposed", variant("recommended", null)) },
306
+ { key: "s4", from: new Date("2026-08-17T00:00:00Z"), to: new Date("2026-08-24T00:00:00Z"), hours: 48.0, state: variant("estimated", null) },
307
+ { key: "s5", from: new Date("2026-08-31T00:00:00Z"), to: new Date("2026-09-14T00:00:00Z"), hours: 56.0, state: variant("proposed", variant("recommended", null)) },
308
+ ],
309
+ chips: [] }) }],
310
+ ["crewB", { hall: none, kind: variant("crew", { name: "Crew B", pool: true, sub: none, value: some("128h"), detail: none, shifts: [],
311
+ chips: [
312
+ { key: "b1", from: variant("time", new Date("2026-07-06T00:00:00Z")), to: variant("time", new Date("2026-07-27T00:00:00Z")), label: "96h", state: variant("confirmed", null),
313
+ icon: some({ prefix: "fas", name: "user-group", label: none, style: none }) },
314
+ { key: "b2", from: variant("time", new Date("2026-08-10T00:00:00Z")), to: variant("time", new Date("2026-08-31T00:00:00Z")), label: "+32h", state: variant("proposed", variant("recommended", null)), icon: none },
315
+ ] }) }],
316
+ // ── Instant marks — every kind: milestones, decisions applied and
317
+ // pending, an exception, a custom icon; the releases on a cadence. ──
318
+ ["milestones", { hall: none, kind: variant("stream", { name: "Milestones", cadence: none,
319
+ detail: some({ height: some("112px"), axis: variant("dim", null) }),
320
+ marks: [
321
+ { key: "kick", at: variant("time", new Date("2026-07-06T00:00:00Z")), kind: variant("milestone", null), icon: none, label: some("KICKOFF") },
322
+ { key: "d1", at: variant("time", new Date("2026-07-27T00:00:00Z")), kind: variant("decision", { applied: true }), icon: none, label: none },
323
+ { key: "rel", at: variant("time", new Date("2026-08-10T00:00:00Z")), kind: variant("milestone", null),
324
+ icon: some({ prefix: "fas", name: "rocket", label: none, style: none }), label: some("REL 2.4") },
325
+ { key: "audit", at: variant("time", new Date("2026-08-24T00:00:00Z")), kind: variant("exception", null), icon: none, label: some("AUDIT") },
326
+ { key: "d2", at: variant("time", new Date("2026-09-07T00:00:00Z")), kind: variant("decision", { applied: false }), icon: none, label: some("×3") },
327
+ ] }) }],
328
+ ["releases", { hall: none, kind: variant("stream", { name: "Releases", cadence: some("6-wk cadence"), detail: none,
329
+ marks: [
330
+ { key: "r1", at: variant("time", new Date("2026-07-13T00:00:00Z")), kind: variant("milestone", null), icon: none, label: some("2.3") },
331
+ { key: "r2", at: variant("time", new Date("2026-08-31T00:00:00Z")), kind: variant("milestone", null), icon: none, label: some("2.4") },
332
+ ] }) }],
333
+ // ── Delivered sheets, a week apiece — negatives, and a week with none;
334
+ // its render draws its own canvas, so the shared lines leave its row. ──
335
+ ["delivered", { hall: none, kind: variant("tally", { name: "Delivered · k",
336
+ detail: some({ height: some("120px"), axis: variant("off", null) }),
337
+ cells: [
338
+ { at: variant("time", new Date("2026-06-29T00:00:00Z")), value: some(128.0), text: none, tone: none },
339
+ { at: variant("time", new Date("2026-07-06T00:00:00Z")), value: some(134.0), text: none, tone: none },
340
+ { at: variant("time", new Date("2026-07-13T00:00:00Z")), value: some(119.0), text: none, tone: none },
341
+ { at: variant("time", new Date("2026-07-20T00:00:00Z")), value: some(-96.0), text: none, tone: none },
342
+ { at: variant("time", new Date("2026-07-27T00:00:00Z")), value: some(-88.0), text: none, tone: none },
343
+ { at: variant("time", new Date("2026-08-03T00:00:00Z")), value: none, text: none, tone: none },
344
+ { at: variant("time", new Date("2026-08-10T00:00:00Z")), value: some(151.0), text: none, tone: none },
345
+ { at: variant("time", new Date("2026-08-17T00:00:00Z")), value: some(162.0), text: none, tone: none },
346
+ { at: variant("time", new Date("2026-08-24T00:00:00Z")), value: some(144.0), text: none, tone: none },
347
+ ] }) }],
348
+ // ── The finishing line's machines — each one shown three ways ──
349
+ ["FN-1", { hall: none, kind: variant("finisher", { name: "Folder FN-1",
350
+ jobs: [{ key: "f1", phase: "FOLD", ticket: some("J-4642"), start: new Date("2026-07-27T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), sheets: some(96.0), state: variant("confirmed", null), alert: none }],
351
+ util: [46.0, 52.0, 58.0, 66.0, 72.0, 78.0, 84.0, 80.0, 74.0, 68.0, 62.0, 58.0],
352
+ sheets: [
353
+ { at: variant("time", new Date("2026-07-27T00:00:00Z")), value: some(48.0), text: none, tone: none },
354
+ { at: variant("time", new Date("2026-08-03T00:00:00Z")), value: some(-8.0), text: none, tone: none },
355
+ ] }) }],
356
+ ["FN-2", { hall: none, kind: variant("finisher", { name: "Cutter FN-2",
357
+ jobs: [{ key: "f2", phase: "CUT", ticket: some("J-4663"), start: new Date("2026-08-24T00:00:00Z"), end: new Date("2026-09-07T00:00:00Z"), sheets: some(88.0), state: variant("proposed", variant("recommended", null)), alert: none }],
358
+ util: [30.0, 34.0, 38.0, 42.0, 46.0, 50.0, 54.0, 58.0, 62.0, 66.0, 70.0, 74.0],
359
+ sheets: [
360
+ { at: variant("time", new Date("2026-08-24T00:00:00Z")), value: some(44.0), text: none, tone: none },
361
+ { at: variant("time", new Date("2026-08-31T00:00:00Z")), value: some(44.0), text: none, tone: none },
362
+ ] }) }],
363
+ ])));
364
+ /**
365
+ * The data flagship: every row kind on one axis, from ONE raw ops source bound
366
+ * from e3, laid out by a series list — the list IS the layout (#822), and a
367
+ * row's id is its series and the path of keys to it.
368
+ *
369
+ * - **The data step.** The source is flat: a row standing in a hall names it.
370
+ * One `groupToDicts` makes each hall an entry holding its rows, and the
371
+ * halls join the rest of the source — hierarchy is the data's own.
372
+ * - **Every row kind.** A pinned KPI chart (spark, breach, its value scale);
373
+ * bucket rows — tiles derived from raw drops with cell markers, and stored
374
+ * tile records in AM/PM lanes with tones, colours, stretch, content, a pulse
375
+ * and an icon; one group strip per hall, nesting its presses and its load,
376
+ * Hall 2 resting as its members' mean; span rows — proposal flavours, an
377
+ * applied decision and a port on a two-line gutter, contracts rolling the
378
+ * presses they book up by union and by status, a delivery past the window;
379
+ * a collapsed section of a second heat series; cards rows — chips derived
380
+ * from hours × lifecycle, and stored chips with an icon; event rows with
381
+ * every mark kind; a table row; a `views` series showing one machine three
382
+ * ways; and literal rows (`Plan.series.rows` + `Plan.span`, a planned
383
+ * shutdown rolling up two trades' runs).
384
+ * - **Links** between runs (`Plan.ref`), each weighed by its quantity: the
385
+ * routing permutations — forward, a same-row seam feed, loopbacks, a rising
386
+ * loop, a landing past the window — and the W31 transfer between two
387
+ * presses.
388
+ * - **Expand in place** on a row of every kind: each row declares it as data
389
+ * (`detail`), the root renders the plot (`expandRender`) and the gutter
390
+ * (`expandGutter`), and the unfocused rows collapse to strips.
391
+ * - **The series are picked** (`Plan.pick`): the library's Series tab lists
392
+ * them, one hidden to start.
393
+ * - **A bound `ui` state** held by the host, its controls beside the frame: a
394
+ * picker brings a press into view, buttons fold and open the halls and
395
+ * expand the KPI, and a readout says what the state holds.
396
+ * - **Popover, hover and every callback** over the element refs and row ids,
397
+ * a section's strip among them (`Plan.sectionRef`).
398
+ * - **The slice** over a horizon an e3 task generates (cohort, filter, search,
399
+ * range, resolution, brush, summary), and the status footer.
400
+ * - **A bounded host**: the Plan fills it (`style.height: "fill"`), and the
401
+ * canvas scrolls its own rows in main.
402
+ * - **Its narrow layout**: below 480px of CONTAINER width — a phone, a pane, a
403
+ * box — the same definition reflows to three tabs over one slice (Groups ·
404
+ * Rows · Measures), the halls as hottest-first strip cards and the rows as
405
+ * cards; only the box changes.
406
+ */
407
+ export const planTargetState = example({
408
+ keywords: [
409
+ "Plan", "canvas", "data", "series", "match", "axis", "window", "resolution", "now", "data step",
410
+ "span", "run", "group", "groups", "strip", "section", "heat", "buckets", "cards", "chip", "events", "mark", "chart",
411
+ "table", "views", "rows", "layers", "rollup", "union", "byStatus", "bands", "footer", "milestone",
412
+ "decision", "exception", "pinned", "port", "hovercard", "popover", "hover",
413
+ "slice", "brush", "horizon", "toolbar", "affordances", "cohort", "filter", "search", "summary",
414
+ "expand", "expandRender", "expandGutter", "keep", "dim", "off", "collapse", "context strip",
415
+ "resolver", "data-driven", "accessor", "raw", "target state", "flagship",
416
+ "layout", "row id", "Plan.ref", "Plan.sectionRef", "links", "link", "focus", "quantity", "Plan.quantity", "unit",
417
+ "onSelect", "onElementClick", "onGroupToggle", "onGrainChange", "callback", "ElementRef",
418
+ "estimated", "removed", "rejected", "proposal", "flavours", "stacked", "gutter", "two-line",
419
+ "children", "Plan.children", "nested", "recursive", "RecursiveType", "groupToDicts", "grouping",
420
+ "summaryAggregate", "collapsed", "member count", "heterogeneous",
421
+ "lane", "lanes", "AM", "PM", "tile", "marker", "tone", "color", "colorPalette", "stretch", "pulse", "icon",
422
+ "lifecycle", "confirmed", "recommended", "same kind", "own entry", "duplicate",
423
+ "one asset", "multiple views", "literal", "Plan.span", "series.rows", "kind factory", "chrome",
424
+ "pick", "Plan.pick", "library", "Plan.library.series", "Series tab", "hidden", "eye",
425
+ "ui", "UiState", "Plan.uiState", "State.bind", "controlled", "selected", "fold", "open", "charts",
426
+ "scroll to row", "external", "write back", "Select", "Button",
427
+ "fill", "height", "bounded", "Box", "scroll", "virtual", "virtualization", "id",
428
+ "Reactive", "Data.bind", "bound", "e3.input", "e3.task", "generated", "dataset", "#822", "#824", "#590", "#320",
429
+ "state", "event", "label", "format", "readings", "variant", "row", "sub", "spark", "expanded", "R2", "markKind", "applied",
430
+ "Planner", "Roster", "mixed", "unbucketed", "sum per unit", "Series", "one source", "one-off", "subtree", "parent",
431
+ "step down", "nesting", "Pick", "Panel", "panel", "pickItems", "kind icon", "toggle", "show", "hide", "choose",
432
+ "persisted", "same entity", "adjacent", "seek", "order", "State", "bind", "selection", "deep link",
433
+ "sizing", "maxHeight", "variable", "estimateSize", "interleave",
434
+ "narrow", "mobile", "phone", "responsive", "compact", "container", "breakpoint", "tabs", "Groups", "Rows", "Measures",
435
+ "hottest", "two-finger", "pan", "review", "§10",
436
+ ],
437
+ description: "The data flagship — every row kind on one axis from one raw ops source bound from e3, grouped by hall in one data step: a pinned KPI, bucket rows derived and stored in lanes, a group strip per hall, span rows with proposal flavours, decisions and ports, contracts rolling up by union and by status, links weighed by quantity, a collapsed section, cards and event rows, a table, a views series and literal rows; expand in place on a row of every kind, the series picked in the library's Series tab, a bound ui state the host drives beside the frame, popover, hover and every callback, slice chrome over a horizon an e3 task generates, and the Plan filling a bounded host; in a box under 480px the same canvas is its narrow layout (Groups · Rows · Measures)",
438
+ fn: East.function([], UIComponentType, (_$) => {
439
+ return (_jsx(Reactive, { children: $ => {
440
+ // The ONE ops source, bound from e3 — every series reads it.
441
+ const ops = $.let(Data.bind(planTargetOps));
442
+ // The horizon the slice narrows — the deliveries a task
443
+ // generates (the canvas rows are the ops source; the slice is
444
+ // chrome over THESE).
445
+ const horizon = $.let(Data.bind(planTargetHorizon));
446
+ // Monday of ISO week n, 2026 (W1 Monday = 2025-12-29). The
447
+ // window is W27–W38 (half-open at W39); now = W31.
448
+ const week = $.const(East.function([IntegerType], DateTimeType, ($2, n) => {
449
+ const w1 = $2.const(new Date("2025-12-29T00:00:00Z"), DateTimeType);
450
+ return w1.addWeeks(n.subtract(1n));
451
+ }));
452
+ // ── The data step ──────────────────────────────────────────
453
+ // Hierarchy is the DATA's (#822): one `groupToDicts` makes each
454
+ // hall an entry holding the rows that stand in it, keyed as the
455
+ // source keys them, and the halls join the rest of the source.
456
+ const raw = $.let(ops.read());
457
+ const halls = $.let(raw.filter((_$2, r) => r.unwrap().hall.hasTag("some"))
458
+ .groupToDicts((_$2, r) => r.unwrap().hall.unwrap("some"), (_$2, _r, k) => k));
459
+ const rows = $.let(raw.filter((_$2, r) => r.unwrap().hall.hasTag("none"))
460
+ .union(halls.map((_$2, members) => East.value({ hall: none, kind: variant("hall", { members }) }, TargetOpsRow))));
461
+ // ── What the accessors share ───────────────────────────────
462
+ const MeasureRow = StructType({ week: DateTimeType, pct: FloatType });
463
+ // A measure's weekly readings, from its first week, as a chart's
464
+ // points, and as heat cells printing their values — a week with
465
+ // none read the no-data cell.
466
+ const weeklyPoints = $.const(East.function([DateTimeType, ArrayType(FloatType)], ArrayType(MeasureRow), (_$2, from, weekly) => East.Array.generate(weekly.size(), MeasureRow, (_$3, i) => ({ week: from.addWeeks(i), pct: weekly.get(i) }))));
467
+ const weeklyCells = $.const(East.function([DateTimeType, ArrayType(OptionType(FloatType))], ArrayType(Plan.Types.HeatCell), (_$2, from, weekly) => East.Array.generate(weekly.size(), Plan.Types.HeatCell, ($3, i) => {
468
+ const value = $3.let(weekly.get(i), OptionType(FloatType));
469
+ const label = $3.let(none, OptionType(StringType));
470
+ $3.match(value, { some: ($4, v) => { $4.assign(label, some(East.Float.printFixed(v, 0n))); } });
471
+ return { at: Plan.at.time(from.addWeeks(i)), value, label };
472
+ })));
473
+ // A press's rate, a thousand sheets an hour, as its gutter value.
474
+ const rateText = $.const(East.function([OptionType(FloatType)], OptionType(StringType), (_$2, rate) => rate.match({
475
+ some: (_$3, k) => East.value(some(East.str `${East.Float.printFixed(k, 0n)}k/h`), OptionType(StringType)),
476
+ none: (_$3) => East.value(none, OptionType(StringType)),
477
+ })));
478
+ // Raw jobs → runs: ONE bound mapping function — the bar label and
479
+ // the quantity derive CLIENT-SIDE from the raw phase/ticket/sheets
480
+ // fields. A quantity is ONE value — the number, its unit and how it
481
+ // prints (#824) — so the bar's caption and a rollup's sum can never
482
+ // disagree. The run is written as a RECORD, so its instants are
483
+ // spelled with `Plan.at.time` — the `Plan.run` builder would wrap a
484
+ // DateTime field by itself.
485
+ const sheetsFormat = $.const(Format.Number({ maximumFractionDigits: 0n }));
486
+ const jobRuns = $.const(East.function([ArrayType(TargetJob)], ArrayType(Plan.Types.Run), (_$2, jobs) => jobs.map(($3, j) => {
487
+ const noQuantity = $3.const(none, OptionType(Plan.Types.Quantity));
488
+ const quantity = $3.let(j.sheets.match({
489
+ some: (_$4, t) => East.value(some(Plan.quantity(t, { unit: "k sheets", format: sheetsFormat })), OptionType(Plan.Types.Quantity)),
490
+ none: (_$4) => noQuantity,
491
+ }), OptionType(Plan.Types.Quantity));
492
+ const label = $3.let(j.ticket.match({
493
+ some: (_$4, b) => East.str `${j.phase} · ${b}`,
494
+ none: (_$4) => j.phase,
495
+ }), StringType);
496
+ const run = $3.let({
497
+ key: j.key, start: Plan.at.time(j.start), end: Plan.at.time(j.end), label,
498
+ quantity, state: j.state, status: j.alert,
499
+ moved: none, icon: none,
500
+ }, Plan.Types.Run);
501
+ return run;
502
+ })));
503
+ // Raw shifts → chips: hours print as the chip label, an ADDED
504
+ // proposal wearing the `+` prefix — display derives from the
505
+ // lifecycle, down to the proposal's flavour (a `removed` shift is
506
+ // a proposal too, and `+` would read as its opposite).
507
+ const shiftChips = $.const(East.function([ArrayType(TargetShift)], ArrayType(Plan.Types.Chip), (_$2, shifts) => shifts.map(($3, s) => {
508
+ const hrs = $3.let(East.Float.printFixed(s.hours, 0n), StringType);
509
+ const label = $3.let(s.state.match({
510
+ proposed: (_$4, p) => p.hasTag("removed").ifElse(() => East.str `${hrs}h`, () => East.str `+${hrs}h`),
511
+ }, _$4 => East.str `${hrs}h`), StringType);
512
+ return Plan.chip({ key: s.key, from: s.from, to: s.to, label, state: s.state });
513
+ })));
514
+ const whole = $.const(Format.Number({ maximumFractionDigits: 0n }));
515
+ // ── The series — the list IS the layout (#822): one block per
516
+ // series, top to bottom, a section titling the blocks beneath
517
+ // it. Every one is picked (below), so the whole list is an East
518
+ // value typed by the constructor. ──
519
+ const all = $.const([
520
+ // The KPI, pinned under the ruler: its gutter opens the spark
521
+ // to the expanded chart, and it declares expand-in-place too,
522
+ // its plot keeping its value scale.
523
+ Plan.series.chart(TargetOpsRow, {
524
+ key: "ontime", title: "On-time",
525
+ match: r => r.kind.hasTag("kpi"),
526
+ label: r => r.kind.unwrap("kpi").name, id: true,
527
+ pinned: r => r.kind.unwrap("kpi").pinned,
528
+ value: r => some(r.kind.unwrap("kpi").headline),
529
+ status: _r => some(variant("warning", null)),
530
+ expand: r => r.kind.unwrap("kpi").detail,
531
+ height: "spark", expandable: true,
532
+ left: { domain: [80, 110], tickValues: [80, 100] },
533
+ layers: r => [
534
+ Plan.layer(Chart.Line(weeklyPoints(r.kind.unwrap("kpi").from, r.kind.unwrap("kpi").weekly), { x: p => p.week, y: p => p.pct }), { breach: { below: 92 } }),
535
+ Chart.refLine({ y: 100, label: "TARGET 100" }),
536
+ ],
537
+ }),
538
+ Plan.series.section(TargetOpsRow, { key: "vans-local", title: "Deliveries · Local", meta: "1 row", value: "9 drops", status: "warning" }, [
539
+ // Raw drops → resting tiles, in the accessor.
540
+ Plan.series.buckets(TargetOpsRow, {
541
+ key: "vans", title: "Vans",
542
+ match: r => r.kind.hasTag("van").and(() => r.kind.unwrap("van").region.equal("local")),
543
+ label: r => r.kind.unwrap("van").name,
544
+ sub: r => r.kind.unwrap("van").sub,
545
+ events: r => r.kind.unwrap("van").drops.map((_$2, a) => Plan.event({ key: a.key, at: a.at, state: a.state })),
546
+ markers: r => r.kind.unwrap("van").markers,
547
+ }),
548
+ ]),
549
+ Plan.series.section(TargetOpsRow, { key: "vans-regional", title: "Deliveries · Regional", meta: "1 row" }, [
550
+ // Stored tile records pass straight through, in their lanes.
551
+ Plan.series.buckets(TargetOpsRow, {
552
+ key: "regional", title: "Regional",
553
+ match: r => r.kind.hasTag("van").and(() => r.kind.unwrap("van").region.equal("regional")),
554
+ label: r => r.kind.unwrap("van").name,
555
+ sub: r => r.kind.unwrap("van").sub,
556
+ expand: r => r.kind.unwrap("van").detail,
557
+ lanes: r => r.kind.unwrap("van").lanes,
558
+ events: r => r.kind.unwrap("van").tiles,
559
+ markers: r => r.kind.unwrap("van").markers,
560
+ }),
561
+ ]),
562
+ // One strip PER HALL — the data step's entries — its members
563
+ // stepped down into (`Plan.children`) and laid out like a
564
+ // top-level list: the hall's presses, then its load. Hall 2
565
+ // rests folded, as its DECLARED mean strip with its member
566
+ // count; the host's state folds Hall 3.
567
+ Plan.series.group(TargetOpsRow, {
568
+ key: "halls", title: "Halls",
569
+ match: r => r.kind.hasTag("hall"),
570
+ label: (_r, hall) => hall,
571
+ collapsed: (_r, hall) => hall.equal("Hall 2"),
572
+ summaryAggregate: "mean",
573
+ children: Plan.children(r => r.kind.unwrap("hall").members, [
574
+ Plan.series.span(TargetOpsRow, {
575
+ key: "presses", title: "Presses",
576
+ match: r => r.kind.hasTag("press"),
577
+ label: (_r, k) => k, id: true,
578
+ value: r => rateText(r.kind.unwrap("press").rate),
579
+ status: r => r.kind.unwrap("press").status,
580
+ expand: r => r.kind.unwrap("press").detail,
581
+ runs: r => jobRuns(r.kind.unwrap("press").jobs),
582
+ decisions: r => r.kind.unwrap("press").decisions,
583
+ ports: r => r.kind.unwrap("press").ports,
584
+ }),
585
+ Plan.series.heat(TargetOpsRow, {
586
+ key: "hall-load", title: "Hall load",
587
+ match: r => r.kind.hasTag("load"),
588
+ label: r => r.kind.unwrap("load").name,
589
+ sub: r => some(r.kind.unwrap("load").sub),
590
+ expand: r => r.kind.unwrap("load").detail,
591
+ cells: r => Plan.heatCells(weeklyCells(r.kind.unwrap("load").from, r.kind.unwrap("load").weekly), { min: 0, max: 100, warnAt: 95 }),
592
+ }),
593
+ ]),
594
+ }),
595
+ // The span grammar — proposal flavours (a forecast ghost, a
596
+ // proposed cut, a declined run)…
597
+ Plan.series.span(TargetOpsRow, {
598
+ key: "flavours", title: "Flavours",
599
+ match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("flavours")),
600
+ label: (_r, k) => k, id: true,
601
+ value: r => rateText(r.kind.unwrap("press").rate),
602
+ runs: r => jobRuns(r.kind.unwrap("press").jobs),
603
+ }),
604
+ // …sheets, an applied decision and a port on a stacked two-line gutter…
605
+ Plan.series.span(TargetOpsRow, {
606
+ key: "detail", title: "Detail",
607
+ match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("detail")),
608
+ label: (_r, k) => k, id: true, stacked: true,
609
+ sub: r => r.kind.unwrap("press").sub,
610
+ expand: r => r.kind.unwrap("press").detail,
611
+ runs: r => jobRuns(r.kind.unwrap("press").jobs),
612
+ decisions: r => r.kind.unwrap("press").decisions,
613
+ ports: r => r.kind.unwrap("press").ports,
614
+ }),
615
+ // …a contract's presses nest under it (`children` — more of
616
+ // this series, to any depth), and its row rolls their runs up…
617
+ Plan.series.span(TargetOpsRow, {
618
+ key: "rollup", title: "Rollup",
619
+ match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("rollup")),
620
+ label: (_r, k) => k, id: true,
621
+ runs: r => jobRuns(r.kind.unwrap("press").jobs),
622
+ children: r => r.kind.unwrap("press").presses, rollup: "union",
623
+ }),
624
+ // …a delivery whose run lands past the window…
625
+ Plan.series.span(TargetOpsRow, {
626
+ key: "delivery", title: "Deliveries",
627
+ match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("delivery")),
628
+ label: (_r, k) => k, id: true,
629
+ runs: r => jobRuns(r.kind.unwrap("press").jobs),
630
+ }),
631
+ // …and a contract rolled up by status, under a section.
632
+ Plan.series.section(TargetOpsRow, { key: "contract-block", title: "Contracts", meta: "1 row" }, [
633
+ Plan.series.span(TargetOpsRow, {
634
+ key: "contracts", title: "Contracts",
635
+ match: r => r.kind.hasTag("press").and(() => r.kind.unwrap("press").series.equal("contracts")),
636
+ label: (_r, k) => k, id: true,
637
+ runs: r => jobRuns(r.kind.unwrap("press").jobs),
638
+ children: r => r.kind.unwrap("press").presses, rollup: "byStatus",
639
+ }),
640
+ ]),
641
+ // A section folded to start, resting as its strip — its
642
+ // members' mean — with its value and status; a second heat
643
+ // series of the same KIND, its own entry.
644
+ Plan.series.section(TargetOpsRow, { key: "quality-block", title: "Quality", meta: "1 row", value: "95%", status: "warning", collapsed: true, summaryAggregate: "mean" }, [
645
+ Plan.series.heat(TargetOpsRow, {
646
+ key: "quality", title: "Quality index", subtitle: "same kind, own entry",
647
+ match: r => r.kind.hasTag("load"),
648
+ label: r => r.kind.unwrap("load").name,
649
+ sub: r => some(r.kind.unwrap("load").sub),
650
+ cells: r => Plan.heatCells(weeklyCells(r.kind.unwrap("load").from, r.kind.unwrap("load").weekly), { min: 0, max: 100 }),
651
+ }),
652
+ ]),
653
+ // Chips derived from raw shifts on a two-line gutter…
654
+ Plan.series.cards(TargetOpsRow, {
655
+ key: "crews", title: "Crews",
656
+ match: r => r.kind.hasTag("crew").and(() => r.kind.unwrap("crew").pool.not()),
657
+ label: r => r.kind.unwrap("crew").name, stacked: true,
658
+ sub: r => r.kind.unwrap("crew").sub,
659
+ expand: r => r.kind.unwrap("crew").detail,
660
+ chips: r => shiftChips(r.kind.unwrap("crew").shifts),
661
+ }),
662
+ // …and chip records stored as they draw, one with an icon.
663
+ Plan.series.section(TargetOpsRow, { key: "relief", title: "Relief pool", meta: "1 row" }, [
664
+ Plan.series.cards(TargetOpsRow, {
665
+ key: "pool", title: "Pool",
666
+ match: r => r.kind.hasTag("crew").and(() => r.kind.unwrap("crew").pool),
667
+ label: r => r.kind.unwrap("crew").name,
668
+ value: r => r.kind.unwrap("crew").value,
669
+ chips: r => r.kind.unwrap("crew").chips,
670
+ }),
671
+ ]),
672
+ Plan.series.events(TargetOpsRow, {
673
+ key: "milestones", title: "Milestones",
674
+ match: r => r.kind.hasTag("stream").and(() => r.kind.unwrap("stream").cadence.hasTag("none")),
675
+ label: r => r.kind.unwrap("stream").name, id: true,
676
+ value: r => some(East.print(r.kind.unwrap("stream").marks.length())),
677
+ expand: r => r.kind.unwrap("stream").detail,
678
+ marks: r => r.kind.unwrap("stream").marks,
679
+ }),
680
+ Plan.series.section(TargetOpsRow, { key: "programs", title: "Programs", meta: "1 row" }, [
681
+ Plan.series.events(TargetOpsRow, {
682
+ key: "releases", title: "Releases",
683
+ match: r => r.kind.hasTag("stream").and(() => r.kind.unwrap("stream").cadence.hasTag("some")),
684
+ label: r => r.kind.unwrap("stream").name, id: true, stacked: true,
685
+ sub: r => r.kind.unwrap("stream").cadence,
686
+ marks: r => r.kind.unwrap("stream").marks,
687
+ }),
688
+ ]),
689
+ Plan.series.table(TargetOpsRow, {
690
+ key: "delivered", title: "Delivered sheets",
691
+ match: r => r.kind.hasTag("tally"),
692
+ label: r => r.kind.unwrap("tally").name,
693
+ expand: r => r.kind.unwrap("tally").detail,
694
+ cells: r => r.kind.unwrap("tally").cells,
695
+ format: whole,
696
+ }),
697
+ // ONE machine, THREE views — a `views` series gives each
698
+ // machine one row per member series, adjacent and in this
699
+ // order: its jobs, its utilisation, its sheets. The label stays
700
+ // the machine; the gutter's sub-line names the view.
701
+ Plan.series.views(TargetOpsRow, {
702
+ key: "finishers", title: "Finishing", subtitle: "one machine, three views",
703
+ match: r => r.kind.hasTag("finisher"),
704
+ }, [
705
+ Plan.series.span(TargetOpsRow, {
706
+ key: "finisher-jobs", title: "Finisher jobs",
707
+ label: r => r.kind.unwrap("finisher").name, id: true,
708
+ runs: r => jobRuns(r.kind.unwrap("finisher").jobs),
709
+ }),
710
+ Plan.series.chart(TargetOpsRow, {
711
+ key: "finisher-util", title: "Finisher · utilisation",
712
+ label: r => r.kind.unwrap("finisher").name, stacked: true, sub: _r => some("utilisation %"), height: "spark",
713
+ layers: r => [Chart.Line(weeklyPoints(week(27n), r.kind.unwrap("finisher").util), { x: p => p.week, y: p => p.pct })],
714
+ }),
715
+ Plan.series.table(TargetOpsRow, {
716
+ key: "finisher-sheets", title: "Finisher · sheets",
717
+ label: r => r.kind.unwrap("finisher").name, stacked: true, sub: _r => some("sheets · plan Δ"),
718
+ cells: r => r.kind.unwrap("finisher").sheets,
719
+ format: whole,
720
+ }),
721
+ ]),
722
+ // Rows no dataset holds — the planned shutdown, written out
723
+ // once. `Plan.span` nests: the parent DECLARES its rollup and
724
+ // the canvas derives the band from its two rows' runs.
725
+ Plan.series.rows(TargetOpsRow, { key: "works", title: "Planned works", subtitle: "literal rows" }, [
726
+ Plan.span({
727
+ key: "shutdown", label: "Shutdown", rollup: "union", rows: [
728
+ Plan.span({ key: "elec", label: "Electrical", runs: [
729
+ Plan.run({ key: "iso", start: week(33n), end: week(34n), label: "ISOLATE", state: "confirmed" }),
730
+ ] }),
731
+ Plan.span({ key: "mech", label: "Mechanical", runs: [
732
+ Plan.run({ key: "rollers", start: week(34n), end: week(36n), label: "ROLLERS", state: "recommended" }),
733
+ ] }),
734
+ ],
735
+ }),
736
+ ]),
737
+ ], ArrayType(Plan.Types.Series(TargetOpsRow)));
738
+ // The series are PICKED: the library's Series tab lists every one
739
+ // by title, subtitle and kind, each with its eye, and the canvas
740
+ // shows those switched on, in this order. Which are off is the
741
+ // viewer's own state, persisted under its key; Programs starts off.
742
+ const shown = $.let(Plan.pick("ex.plan.target.pick", all, { hidden: ["programs"] }));
743
+ const axis = $.const(Plan.axis({
744
+ window: { min: week(27n), max: week(39n) },
745
+ resolution: "week", resolutions: ["month", "week", "day"], now: week(31n),
746
+ }));
747
+ // The toolbar is SEEDED slice state: the applied window is the
748
+ // range chip (`JUN 29 – SEP 20 · 84d`), and two saved cohorts sit
749
+ // beside the filter builder with `Late risk` active — their
750
+ // counts are live over the horizon, never printed. A slice range
751
+ // is CLOSED — both ends inclusive — so the twelve weeks W27–W38
752
+ // end the millisecond before W39.
753
+ const slice = $.let(Slice.bind("ex.plan.target", horizon.read(), {
754
+ fields: {
755
+ at: { label: "Delivered", format: { date: "MMM D" } },
756
+ hall: { label: "Hall" },
757
+ risk: { label: "Risk", hints: ["late", "on-time"] },
758
+ sheets: { label: "Sheets" },
759
+ },
760
+ range: "at", search: "hall",
761
+ initial: { at: { from: week(27n), to: week(39n).addMilliseconds(-1n) } },
762
+ presets: [
763
+ { id: "late", name: "Late risk", where: { risk: ["late"] }, active: true },
764
+ { id: "empty", name: "Empty", where: { sheets: { lte: 0.0 } } },
765
+ ],
766
+ }));
767
+ // ── Expand in place ────────────────────────────────────────
768
+ // ONE resolver serves every declaring row, called with the row's
769
+ // id at interaction time. The canvas hands it the PLOT column with
770
+ // the shared grid and now line drawn behind it, so `Sparkline` —
771
+ // the axis-free chart — fills that column edge to edge and lines up
772
+ // with the buckets above.
773
+ const util = $.let(East.Array.generate(12n, FloatType, (_$2, i) => i.multiply(19n).remainder(48n).toFloat().add(50.0)));
774
+ const expandRender = $.const(East.function([Plan.Types.RowId], UIComponentType, (_$2, _id) => (_jsx(Sparkline, { data: util, type: "area", color: "link", width: "100%", height: "100%" }))));
775
+ // The GUTTER half: an expanded row's gutter grows with it, and what
776
+ // fills the room is the author's — identity lines, then a fill
777
+ // meter (the shared Progress at its smallest size).
778
+ const GutterFacts = StructType({ a: StringType, b: StringType, fill: FloatType });
779
+ const expandGutter = $.const(East.function([Plan.Types.RowId], UIComponentType, ($2, id) => {
780
+ const facts = $2.const(new Map([
781
+ ["ontime", { a: "TARGET 100 · MIN 92", b: "BREACH W34–W36 · 3 wk", fill: 0.94 }],
782
+ ["van2", { a: "8 TILES · 2 LANES", b: "BREACH W29", fill: 0.6 }],
783
+ ["H1-P04", { a: "12k/h · FILL", b: "J-4624 · 112 k sheets · 74%", fill: 0.74 }],
784
+ ["h1-load", { a: "MEAN 73 · PEAK 96", b: "BREACH W33 · 1 wk", fill: 0.96 }],
785
+ ["H1-P09", { a: "12k sheets/h · FILL", b: "J-4693 · 104 k sheets · 81%", fill: 0.81 }],
786
+ ["crewA", { a: "152h → 168h", b: "1 CUT · 2 PROPOSED", fill: 0.9 }],
787
+ ["milestones", { a: "5 MARKS", b: "1 EXCEPTION · W34", fill: 0.2 }],
788
+ ["delivered", { a: "NET 754 k sheets", b: "2 SHORT WEEKS", fill: 0.55 }],
789
+ ]), DictType(StringType, GutterFacts));
790
+ const blank = $2.const({ a: "", b: "", fill: 0.0 }, GutterFacts);
791
+ // A row's id is its series and the path of keys to it — its
792
+ // last key is its entry's, which the facts are keyed by.
793
+ const path = $2.let(id.unwrap("entry").path);
794
+ const f = $2.let(facts.get(path.get(path.size().subtract(1n)), () => blank));
795
+ return (_jsxs(Box, { children: [_jsx(Text, { children: f.a }), _jsx(Text, { children: f.b }), _jsx(Box, { width: "108px", children: _jsx(Progress, { value: f.fill.multiply(100.0), size: "xs", tone: "brand" }) })] }));
796
+ }));
797
+ // ── Popover and hover ──────────────────────────────────────
798
+ // ONE stored function each over Plan.Types.ElementRef — every arm
799
+ // carries the row's id; rich bodies build lazily at interaction
800
+ // time, and a none result opens no surface.
801
+ const popover = $.const(East.function([Plan.Types.ElementRef], OptionType(UIComponentType), ($2, ref) => {
802
+ const noBody = $2.const(none, OptionType(UIComponentType));
803
+ const p03 = $2.const(Plan.ref("presses", "Hall 1", "H1-P03"));
804
+ const p09 = $2.const(Plan.ref("detail", "H1-P09"));
805
+ // A section's header is a row too: its strip's cells name it.
806
+ const quality = $2.const(Plan.sectionRef("quality-block"));
807
+ return ref.match({
808
+ run: (_$3, ev) => ev.run.equal("j4663").ifElse(() => some(_jsx(Text, { children: "Proposed by run 412 \u2014 fills the W32 idle window." })), () => noBody),
809
+ mark: (_$3, ev) => East.equal(ev.row, p03).and(() => ev.mark.equal("d1")).ifElse(() => some(_jsx(Text, { children: "Schedule J-4663" })), () => East.equal(ev.row, p09).and(() => ev.mark.equal("d1")).ifElse(() => some(_jsx(Text, { children: "Approved by run 411." })), () => ev.mark.equal("rel").ifElse(() => some(_jsx(Text, { children: "Go/no-go review." })), () => noBody))),
810
+ event: (_$3, ev) => ev.event.equal("m5").ifElse(() => some(_jsx(Text, { children: "Load 41 \u00B7 8 pallets" })), () => noBody),
811
+ chip: (_$3, ev) => ev.chip.equal("s3").ifElse(() => some(_jsx(Text, { children: "Overtime proposal." })), () => noBody),
812
+ cell: (_$3, ev) => East.equal(ev.row, quality).ifElse(() => some(_jsx(Text, { children: "Quality held above 92 all quarter." })), () => noBody),
813
+ }, _$3 => noBody);
814
+ }));
815
+ const hover = $.const(East.function([Plan.Types.ElementRef], OptionType(UIComponentType), ($2, ref) => {
816
+ const noBody = $2.const(none, OptionType(UIComponentType));
817
+ return ref.match({
818
+ run: (_$3, ev) => ev.run.equal("j4591").ifElse(() => some(_jsx(Text, { children: "Waiting on proof 4 \u2014 2.6\u00D7 median dwell." })), () => noBody),
819
+ event: (_$3, ev) => ev.event.equal("m3").ifElse(() => some(_jsx(Text, { children: "Urgent \u2014 overtime window" })), () => noBody),
820
+ }, _$3 => noBody);
821
+ }));
822
+ // Behavior props — bound once so memoized renderers keep identity.
823
+ const onRow = $.const(East.function([Plan.Types.RowId], NullType, (_$2, _id) => null));
824
+ // ONE element callback (#824) — a run, tile, mark, chip, cell or
825
+ // link ribbon, by the same ref the popover resolver receives.
826
+ const onElementClick = $.const(East.function([Plan.Types.ElementRef], NullType, (_$2, _ref) => null));
827
+ const onGroupToggle = $.const(East.function([Plan.Types.GroupToggleEvent], NullType, (_$2, _e) => null));
828
+ const onGrainChange = $.const(East.function([Plan.Types.Grain], NullType, (_$2, _g) => null));
829
+ // ── The bound ui state, held by the host ───────────────────
830
+ // The canvas's selection, the rows folded or opened against what
831
+ // they declare, the expanded charts, and a row to bring into view
832
+ // — in `State.bind` at `Plan.Types.UiState`, seeded by
833
+ // `Plan.uiState(…)`. The canvas reads it and writes the user's
834
+ // actions back; the host's controls write it too. Every row is
835
+ // named by its id: a hall is `Plan.ref("halls", hall)`, a press
836
+ // `Plan.ref("presses", hall, press)`. Hall 3 starts folded.
837
+ const ui = $.let(State.bind([Plan.Types.UiState], "ex.plan.target.ui", Plan.uiState({ collapsed: [Plan.ref("halls", "Hall 3")] })));
838
+ const now = $.let(ui.read());
839
+ const hallIds = $.let(halls.toArray((_$2, _g, hall) => Plan.ref("halls", hall)));
840
+ const fleet = $.let(raw.filter((_$2, r) => r.unwrap().kind.hasTag("press").and(() => r.unwrap().hall.hasTag("some"))));
841
+ const kpi = $.const(Plan.ref("ontime", "ontime"));
842
+ // Bring a press into view: select it, and REQUEST its focus — the
843
+ // canvas opens its hall if it is folded, scrolls to it, and clears
844
+ // the request once it has.
845
+ const goTo = $.const(East.function([StringType], NullType, ($2, key) => {
846
+ const s = $2.let(ui.read());
847
+ const id = $2.let(Plan.ref("presses", fleet.get(key).unwrap().hall.unwrap("some"), key));
848
+ $2(ui.write(East.value({
849
+ selected: some(id), collapsed: s.collapsed, expanded: s.expanded, charts: s.charts, focus: some(id),
850
+ }, Plan.Types.UiState)));
851
+ }));
852
+ const foldAll = $.const(East.function([], NullType, ($2) => {
853
+ const s = $2.let(ui.read());
854
+ $2(ui.write(East.value({
855
+ selected: s.selected, collapsed: hallIds, expanded: [], charts: s.charts, focus: s.focus,
856
+ }, Plan.Types.UiState)));
857
+ }));
858
+ const openAll = $.const(East.function([], NullType, ($2) => {
859
+ const s = $2.let(ui.read());
860
+ $2(ui.write(East.value({
861
+ selected: s.selected, collapsed: [], expanded: hallIds, charts: s.charts, focus: s.focus,
862
+ }, Plan.Types.UiState)));
863
+ }));
864
+ const noCharts = $.const([], ArrayType(Plan.Types.RowId));
865
+ const kpiChart = $.const([kpi], ArrayType(Plan.Types.RowId));
866
+ const chart = $.const(East.function([], NullType, ($2) => {
867
+ const s = $2.let(ui.read());
868
+ const charts = $2.let(s.charts.size().greater(0n).ifElse(() => noCharts, () => kpiChart));
869
+ $2(ui.write(East.value({
870
+ selected: s.selected, collapsed: s.collapsed, expanded: s.expanded, charts, focus: s.focus,
871
+ }, Plan.Types.UiState)));
872
+ }));
873
+ // The picker's presses are the source's own keys.
874
+ const keys = $.let(fleet.toArray((_$2, _r, k) => k));
875
+ const picked = $.let(now.selected.match({
876
+ some: (_$2, id) => East.print(id),
877
+ none: (_$2) => "nothing",
878
+ }), StringType);
879
+ return (_jsxs(VStack, { gap: "2", align: "stretch", children: [_jsxs(HStack, { gap: "2", children: [_jsx(Select, { value: "H3-P22", onChange: goTo, size: "sm", items: keys.map((_$2, k) => Select.Item(k, East.str `Go to ${k}`)) }), _jsx(Button, { size: "xs", onClick: foldAll, children: "Fold halls" }), _jsx(Button, { size: "xs", onClick: openAll, children: "Open halls" }), _jsx(Button, { size: "xs", onClick: chart, children: "On-time chart" })] }), _jsx(Box, { height: "720px", children: _jsx(Plan, { id: "target", slice: { slice, affordances: ["cohort", "filter", "search", "range", "resolution", "brush", "summary"] }, axis: axis,
880
+ // The link graph (R1) — the edges cover the routing
881
+ // permutations: forward, a same-row seam feed,
882
+ // loopbacks, a rising loop and a landing past the
883
+ // window; then the W31 transfer between two presses,
884
+ // its `text` the author's way of saying it. Each moves
885
+ // a QUANTITY (#824): its value weighs the ribbon and its
886
+ // caption prints on it. Hover a linked row for its
887
+ // links-focus control.
888
+ links: [
889
+ Plan.link({ key: "l1", from: Plan.ref("flavours", "H1-P07"), fromRun: "run", to: Plan.ref("detail", "H1-P09"), toRun: "a", quantity: Plan.quantity(24, { unit: "k sheets" }) }),
890
+ Plan.link({ key: "l2", from: Plan.ref("detail", "H1-P09"), fromRun: "a", to: Plan.ref("detail", "H1-P09"), toRun: "b", quantity: Plan.quantity(40, { unit: "k sheets" }) }),
891
+ Plan.link({ key: "l3", from: Plan.ref("detail", "H1-P09"), fromRun: "b", to: Plan.ref("rollup", "Contract A", "H1-P03"), toRun: "j4663", quantity: Plan.quantity(88, { unit: "k sheets" }) }),
892
+ Plan.link({ key: "l4", from: Plan.ref("rollup", "Contract A", "H1-P03"), fromRun: "j4642", to: Plan.ref("rollup", "Contract A", "H2-P11"), toRun: "j4723", quantity: Plan.quantity(32, { unit: "k sheets" }) }),
893
+ Plan.link({ key: "l5", from: Plan.ref("rollup", "Contract A", "H2-P11"), fromRun: "j4723", to: Plan.ref("detail", "H1-P09"), toRun: "b", quantity: Plan.quantity(18, { unit: "k sheets" }) }),
894
+ Plan.link({ key: "l6", from: Plan.ref("detail", "H1-P09"), fromRun: "b", to: Plan.ref("delivery", "dlv"), toRun: "d1", quantity: Plan.quantity(91, { unit: "k sheets" }) }),
895
+ Plan.link({
896
+ key: "t-w31",
897
+ from: Plan.ref("presses", "Hall 1", "H1-P03"), fromRun: "j4642",
898
+ to: Plan.ref("presses", "Hall 1", "H1-P04"), toRun: "proof",
899
+ quantity: Plan.quantity(24, { unit: "k sheets", text: "−24 k sheets" }),
900
+ }),
901
+ ], data: rows, pick: shown, library: [Plan.library.series()], ui: ui, expandRender: expandRender, expandGutter: expandGutter, popover: popover, hover: hover, onSelect: onRow, onElementClick: onElementClick, onGroupToggle: onGroupToggle, onGrainChange: onGrainChange, footer: [
902
+ { text: "512 RESOURCES · 12 GROUPS · 3 IN VIEW" },
903
+ { text: "318 OBSERVED · 966 PLANNED" },
904
+ { text: "3 EXCEPTIONS", tone: "warning" },
905
+ { text: "RUN 412 · W27–W38", end: true },
906
+ ], style: { height: "fill" } }) }), _jsx(Text.MonoLabel, { children: East.str `SELECTED · ${picked} · ${East.print(now.collapsed.size())} FOLDED · ${East.print(now.expanded.size())} OPENED` })] }));
907
+ } }));
908
+ }),
909
+ inputs: [],
910
+ });
911
+ // ============================================================================
912
+ // planVariants — THE Plan configurator (#571): axis presets × style sweeps
913
+ // ============================================================================
914
+ /** A press job, its label already composed. */
915
+ export const VariantsJob = StructType({
916
+ key: StringType, label: StringType,
917
+ start: DateTimeType, end: DateTimeType, state: EventStateType,
918
+ });
919
+ /** One fortnightly reading of a measure. */
920
+ export const VariantsMeasure = StructType({ week: DateTimeType, pct: FloatType });
921
+ /** A van drop. */
922
+ export const VariantsAlloc = StructType({ key: StringType, at: DateTimeType, state: EventStateType });
923
+ /** A crew shift, its label already composed. */
924
+ export const VariantsShift = StructType({
925
+ key: StringType, from: DateTimeType, to: DateTimeType, label: StringType, state: EventStateType,
926
+ });
927
+ /** One table reading — a bucket's instant and its value, if it has one. */
928
+ export const VariantsReading = StructType({ at: DateTimeType, value: OptionType(FloatType) });
929
+ /**
930
+ * ONE flat source: `series` names the series that claims the row, and every
931
+ * channel a row's kind does not read stays empty.
932
+ */
933
+ export const VariantsOpsRow = StructType({
934
+ series: StringType, label: StringType,
935
+ sub: OptionType(StringType),
936
+ jobs: ArrayType(VariantsJob),
937
+ points: ArrayType(VariantsMeasure),
938
+ cells: ArrayType(Plan.Types.HeatCell),
939
+ allocs: ArrayType(VariantsAlloc),
940
+ act: ArrayType(VariantsReading),
941
+ plan: ArrayType(VariantsReading),
942
+ shifts: ArrayType(VariantsShift),
943
+ marks: ArrayType(Plan.Types.EventMark),
944
+ });
945
+ /**
946
+ * Every row kind on purpose: the density sweep re-rhythms them all at once —
947
+ * compact tightens the shared row and the bars in it, while a two-line gutter
948
+ * keeps its floor and a stacked table a line per position — and the gutter
949
+ * sweep re-widths every label. The readings are fortnightly from W27.
950
+ */
951
+ export const planVariantsOps = e3.input("plan_variants_ops", DictType(StringType, VariantsOpsRow), variant("value", new Map([
952
+ ["util", { series: "util", label: "Util %", sub: none, jobs: [],
953
+ points: [
954
+ { week: new Date("2026-06-29T00:00:00Z"), pct: 46.0 }, { week: new Date("2026-07-13T00:00:00Z"), pct: 58.0 },
955
+ { week: new Date("2026-07-27T00:00:00Z"), pct: 66.0 }, { week: new Date("2026-08-10T00:00:00Z"), pct: 72.0 },
956
+ { week: new Date("2026-08-24T00:00:00Z"), pct: 84.0 }, { week: new Date("2026-09-07T00:00:00Z"), pct: 96.0 },
957
+ ],
958
+ cells: [], allocs: [], act: [], plan: [], shifts: [], marks: [] }],
959
+ ["p03", { series: "press", label: "H1-P03", sub: some("12k sheets/h"),
960
+ jobs: [
961
+ { key: "j4642", label: "RUN · J-4642", start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), state: variant("in-progress", null) },
962
+ { key: "j4663", label: "RUN · J-4663", start: new Date("2026-08-03T00:00:00Z"), end: new Date("2026-08-24T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
963
+ ],
964
+ points: [], cells: [], allocs: [], act: [], plan: [], shifts: [], marks: [] }],
965
+ ["p04", { series: "press", label: "H1-P04", sub: none,
966
+ jobs: [
967
+ { key: "j4624", label: "RUN · J-4624", start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), state: variant("actual", null) },
968
+ ],
969
+ points: [], cells: [], allocs: [], act: [], plan: [], shifts: [], marks: [] }],
970
+ ["load", { series: "load", label: "H2 load", sub: some("%/wk"), jobs: [], points: [],
971
+ cells: [
972
+ { at: variant("time", new Date("2026-06-29T00:00:00Z")), value: some(46.0), label: some("46") },
973
+ { at: variant("time", new Date("2026-07-13T00:00:00Z")), value: some(58.0), label: some("58") },
974
+ { at: variant("time", new Date("2026-07-27T00:00:00Z")), value: some(66.0), label: some("66") },
975
+ { at: variant("time", new Date("2026-08-10T00:00:00Z")), value: some(72.0), label: some("72") },
976
+ { at: variant("time", new Date("2026-08-24T00:00:00Z")), value: some(84.0), label: some("84") },
977
+ { at: variant("time", new Date("2026-09-07T00:00:00Z")), value: some(96.0), label: some("96") },
978
+ ],
979
+ allocs: [], act: [], plan: [], shifts: [], marks: [] }],
980
+ ["van1", { series: "van", label: "Van 1", sub: none, jobs: [], points: [], cells: [],
981
+ allocs: [
982
+ { key: "a1", at: new Date("2026-07-06T00:00:00Z"), state: variant("confirmed", null) },
983
+ { key: "a2", at: new Date("2026-07-27T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
984
+ { key: "a3", at: new Date("2026-08-10T00:00:00Z"), state: variant("confirmed", null) },
985
+ ],
986
+ act: [], plan: [], shifts: [], marks: [] }],
987
+ // Delivered sheets and their signed Δ against plan — the table row's
988
+ // two positions.
989
+ ["desp", { series: "desp", label: "Delivered · k", sub: none, jobs: [], points: [], cells: [], allocs: [],
990
+ act: [
991
+ { at: new Date("2026-06-29T00:00:00Z"), value: some(69.0) }, { at: new Date("2026-07-13T00:00:00Z"), value: some(87.0) },
992
+ { at: new Date("2026-07-27T00:00:00Z"), value: some(99.0) }, { at: new Date("2026-08-10T00:00:00Z"), value: some(108.0) },
993
+ { at: new Date("2026-08-24T00:00:00Z"), value: some(126.0) }, { at: new Date("2026-09-07T00:00:00Z"), value: some(144.0) },
994
+ ],
995
+ plan: [
996
+ { at: new Date("2026-06-29T00:00:00Z"), value: some(-24.0) }, { at: new Date("2026-07-13T00:00:00Z"), value: some(-12.0) },
997
+ { at: new Date("2026-07-27T00:00:00Z"), value: some(-4.0) }, { at: new Date("2026-08-10T00:00:00Z"), value: some(2.0) },
998
+ { at: new Date("2026-08-24T00:00:00Z"), value: some(14.0) }, { at: new Date("2026-09-07T00:00:00Z"), value: some(26.0) },
999
+ ],
1000
+ shifts: [], marks: [] }],
1001
+ ["crewA", { series: "crew", label: "Crew A", sub: none, jobs: [], points: [], cells: [], allocs: [], act: [], plan: [],
1002
+ shifts: [
1003
+ { key: "s1", from: new Date("2026-06-29T00:00:00Z"), to: new Date("2026-07-13T00:00:00Z"), label: "80h", state: variant("confirmed", null) },
1004
+ { key: "s2", from: new Date("2026-07-27T00:00:00Z"), to: new Date("2026-08-10T00:00:00Z"), label: "+64h", state: variant("proposed", variant("recommended", null)) },
1005
+ ],
1006
+ marks: [] }],
1007
+ ["ms", { series: "ms", label: "Milestones", sub: none, jobs: [], points: [], cells: [], allocs: [], act: [], plan: [], shifts: [],
1008
+ marks: [
1009
+ { key: "k", at: variant("time", new Date("2026-07-13T00:00:00Z")), kind: variant("milestone", null), icon: none, label: some("KICKOFF") },
1010
+ { key: "a", at: variant("time", new Date("2026-08-17T00:00:00Z")), kind: variant("exception", null), icon: none, label: some("AUDIT") },
1011
+ ] }],
1012
+ ["p11", { series: "gpress", label: "H3-P11", sub: none,
1013
+ jobs: [
1014
+ { key: "j4903", label: "RUN · J-4903", start: new Date("2026-07-13T00:00:00Z"), end: new Date("2026-08-10T00:00:00Z"), state: variant("confirmed", null) },
1015
+ ],
1016
+ points: [], cells: [], allocs: [], act: [], plan: [], shifts: [], marks: [] }],
1017
+ ])));
1018
+ /**
1019
+ * THE Plan configurator — the slot-2 variant-space surface the retired Gantt /
1020
+ * Planner configurators held (#571). ONE live canvas; every axis feeds it as
1021
+ * an expression. The window presets are whole `Plan.axis` VALUES riding a
1022
+ * typed preset struct: the ops week window, the year roadmap at month
1023
+ * resolution, and the 14-day sprint at day resolution with `ddd DD` labels
1024
+ * (the #309 pinned-day-columns contract the Planner's `day` preset and the
1025
+ * AlignedStack date-axis panel guarded). The style sweeps ride `style` —
1026
+ * density rhythm and gutter width — over a canvas holding every row kind, so
1027
+ * one click re-rhythms them all; every callback (select / element click /
1028
+ * group toggle / grain change) logs to the aside, the retired configurators'
1029
+ * pattern. The canvas reads a source bound from e3; the configurator's axes
1030
+ * are the viewer's own state. Fill sizing, and each kind's whole grammar, are
1031
+ * `planTargetState`'s — the measure rows' `planMeasures`'.
1032
+ */
1033
+ export const planVariants = example({
1034
+ keywords: [
1035
+ "Plan", "configurator", "Configurator", "variants", "preset", "axis", "window",
1036
+ "resolution", "month", "week", "day", "roadmap", "sprint", "ops", "format",
1037
+ "ddd", "#309", "density", "condensed", "compact", "comfortable", "gutterWidth",
1038
+ "gutter", "style", "onSelect", "onElementClick", "ElementRef", "onGroupToggle", "onGrainChange",
1039
+ "callback", "aside", "Reactive", "State", "SegmentGroup", "Select", "getTag",
1040
+ "every row kind", "span", "chart", "expandable", "heat", "buckets", "table",
1041
+ "tableSeries", "vertical", "cards", "events", "group", "section", "row id", "print",
1042
+ "Data.bind", "bound", "e3.input",
1043
+ ],
1044
+ description: "Plan configurator — axis window presets (ops week / year roadmap / 14-day sprint) with density and gutter-width sweeps over every row kind on one live canvas bound from e3; every callback logs to the aside",
1045
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
1046
+ // The source, bound from e3 — every row kind, one flat dataset.
1047
+ const ops = $.let(Data.bind(planVariantsOps));
1048
+ // Monday of ISO week n, 2026 — the ops window is W27–W38
1049
+ // (half-open at W39), now = W31; the roadmap preset walks the
1050
+ // whole ISO year and the sprint preset a 14-day slice of it.
1051
+ const week = $.const(East.function([IntegerType], DateTimeType, ($, n) => {
1052
+ const w1 = $.const(new Date("2025-12-29T00:00:00Z"), DateTimeType);
1053
+ return w1.addWeeks(n.subtract(1n));
1054
+ }));
1055
+ const series = $.const([
1056
+ // The spark's gutter opens it to the expanded chart, at any density.
1057
+ Plan.series.chart(VariantsOpsRow, {
1058
+ key: "util", title: "Utilisation",
1059
+ match: r => r.series.equal("util"),
1060
+ label: r => r.label, id: true, height: "spark", expandable: true,
1061
+ layers: r => [Chart.Line(r.points, { x: p => p.week, y: p => p.pct })],
1062
+ }),
1063
+ Plan.series.span(VariantsOpsRow, {
1064
+ key: "press", title: "Press jobs",
1065
+ match: r => r.series.equal("press"),
1066
+ label: r => r.label, id: true, sub: r => r.sub,
1067
+ runs: r => r.jobs.map((_$, j) => Plan.run({
1068
+ key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
1069
+ })),
1070
+ }),
1071
+ Plan.series.heat(VariantsOpsRow, {
1072
+ key: "load", title: "Hall load",
1073
+ match: r => r.series.equal("load"),
1074
+ label: r => r.label, sub: r => r.sub,
1075
+ cells: r => Plan.heatCells(r.cells, { min: 0, max: 100 }),
1076
+ }),
1077
+ Plan.series.buckets(VariantsOpsRow, {
1078
+ key: "van", title: "Van drops",
1079
+ match: r => r.series.equal("van"),
1080
+ label: r => r.label,
1081
+ events: r => r.allocs.map((_$, a) => Plan.event({ key: a.key, at: a.at, state: a.state })),
1082
+ }),
1083
+ // The positions STACKED — the row grows a line per position.
1084
+ Plan.series.table(VariantsOpsRow, {
1085
+ key: "desp", title: "Deliveries",
1086
+ match: r => r.series.equal("desp"),
1087
+ label: r => r.label, split: "vertical",
1088
+ series: r => [
1089
+ Plan.tableSeries({ strong: true, cells: Plan.tableCells(r.act) }),
1090
+ Plan.tableSeries({
1091
+ tone: "muted",
1092
+ format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
1093
+ cells: Plan.tableCells(r.plan),
1094
+ }),
1095
+ ],
1096
+ format: Format.Number({ maximumFractionDigits: 0n }),
1097
+ }),
1098
+ Plan.series.cards(VariantsOpsRow, {
1099
+ key: "crew", title: "Crew shifts",
1100
+ match: r => r.series.equal("crew"),
1101
+ label: r => r.label,
1102
+ chips: r => r.shifts.map((_$, s) => Plan.chip({ key: s.key, from: s.from, to: s.to, label: s.label, state: s.state })),
1103
+ }),
1104
+ Plan.series.events(VariantsOpsRow, {
1105
+ key: "ms", title: "Milestones",
1106
+ match: r => r.series.equal("ms"),
1107
+ label: r => r.label, id: true,
1108
+ marks: r => r.marks,
1109
+ }),
1110
+ // A section header, so the group-toggle callback has something to fire on.
1111
+ Plan.series.section(VariantsOpsRow, { key: "hall3", title: "Hall 3", meta: "1 row" }, [
1112
+ Plan.series.span(VariantsOpsRow, {
1113
+ key: "gpress", title: "Grouped jobs",
1114
+ match: r => r.series.equal("gpress"),
1115
+ label: r => r.label, id: true,
1116
+ runs: r => r.jobs.map((_$, j) => Plan.run({
1117
+ key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
1118
+ })),
1119
+ }),
1120
+ ]),
1121
+ ], ArrayType(Plan.Types.Series(VariantsOpsRow)));
1122
+ // Every preset is DATA — a whole axis value in a typed struct, so
1123
+ // switching presets swaps window, resolution, format and now
1124
+ // through ONE expression-fed prop.
1125
+ const presets = $.const([
1126
+ { key: "ops", axis: Plan.axis({
1127
+ window: { min: week(27n), max: week(39n) },
1128
+ resolution: "week", resolutions: ["month", "week", "day"], now: week(31n),
1129
+ }) },
1130
+ { key: "roadmap", axis: Plan.axis({
1131
+ window: { min: week(1n), max: week(53n) },
1132
+ resolution: "month", format: "MMM", now: week(31n),
1133
+ }) },
1134
+ { key: "sprint", axis: Plan.axis({
1135
+ window: { min: week(30n), max: week(32n) },
1136
+ resolution: "day", format: "ddd DD", now: week(31n),
1137
+ }) },
1138
+ ], ArrayType(StructType({ key: StringType, axis: Plan.Types.Axis })));
1139
+ const presetKeys = $.const(["ops", "roadmap", "sprint"], ArrayType(StringType));
1140
+ const densities = $.const([
1141
+ variant("condensed", null), variant("compact", null), variant("comfortable", null),
1142
+ ], ArrayType(Style.Types.Density));
1143
+ const gutters = $.const(["168px", "200px", "240px"], ArrayType(StringType));
1144
+ const presetBind = $.let(State.bind([StringType], "plan_variants_preset", "ops"));
1145
+ const densityBind = $.let(State.bind([StringType], "plan_variants_density", "compact"));
1146
+ const gutterBind = $.let(State.bind([StringType], "plan_variants_gutter", "168px"));
1147
+ const lastEventBind = $.let(State.bind([StringType], "plan_variants_last_event", ""));
1148
+ const pKey = $.let(presetBind.read());
1149
+ const dKey = $.let(densityBind.read());
1150
+ const gKey = $.let(gutterBind.read());
1151
+ const lastEvent = $.let(lastEventBind.read());
1152
+ const onPreset = $.const(East.function([StringType], NullType, ($, next) => { $(presetBind.write(next)); }));
1153
+ const onDensity = $.const(East.function([StringType], NullType, ($, next) => { $(densityBind.write(next)); }));
1154
+ const onGutter = $.const(East.function([StringType], NullType, ($, next) => { $(gutterBind.write(next)); }));
1155
+ // The interactive surface — every callback writes the aside line,
1156
+ // naming the row by its id (the series that made it and the path
1157
+ // of keys to it) in its `.east` text.
1158
+ const onSelect = $.const(East.function([Plan.Types.RowId], NullType, ($, id) => {
1159
+ $(lastEventBind.write(East.str `onSelect · ${East.print(id)}`));
1160
+ }));
1161
+ // ONE callback for every element (#824) — the ref's arm says which
1162
+ // kind was clicked, and each arm names its row and element.
1163
+ const onElementClick = $.const(East.function([Plan.Types.ElementRef], NullType, ($, ref) => {
1164
+ $(lastEventBind.write(East.str `onElementClick · ${ref.getTag()} · ${East.print(ref)}`));
1165
+ }));
1166
+ const onGroupToggle = $.const(East.function([Plan.Types.GroupToggleEvent], NullType, ($, ev) => {
1167
+ const state = $.let(ev.expanded.ifElse(() => "expanded", () => "collapsed"), StringType);
1168
+ $(lastEventBind.write(East.str `onGroupToggle · ${East.print(ev.row)} → ${state}`));
1169
+ }));
1170
+ const onGrainChange = $.const(East.function([Plan.Types.Grain], NullType, ($, grain) => {
1171
+ $(lastEventBind.write(East.str `onGrainChange · ${grain.getTag()}`));
1172
+ }));
1173
+ const sel = $.let(presets.filter((_$, o) => o.key.equal(pKey)).get(0n, _$ => presets.get(0n)));
1174
+ const densitySel = $.let(densities.filter((_$, v) => v.getTag().equal(dKey)).get(0n));
1175
+ return (_jsx(Configurator, { controls: [
1176
+ Configurator.Control("Preset", pKey, _jsx(Select, { value: pKey, onChange: onPreset, size: "sm", items: presetKeys.map((_$, s) => Select.Item(s, s)) })),
1177
+ Configurator.Control("Density", dKey, _jsx(SegmentGroup, { value: dKey, onChange: onDensity, size: "sm", items: densities.map((_$, v) => SegmentGroup.Item(v.getTag(), _jsx(Text, { children: v.getTag().upperCase() }))) })),
1178
+ Configurator.Control("Gutter", gKey, _jsx(SegmentGroup, { value: gKey, onChange: onGutter, size: "sm", items: gutters.map((_$, g) => SegmentGroup.Item(g, _jsx(Text, { children: g }))) })),
1179
+ ], preview: _jsx(Plan, { axis: sel.axis, data: ops, series: series, onSelect: onSelect, onElementClick: onElementClick, onGroupToggle: onGroupToggle, onGrainChange: onGrainChange, style: { density: densitySel, gutterWidth: gKey } }), live: true, aside: {
1180
+ label: "Events · Reactive",
1181
+ body: (_jsx(Badge, { colorPalette: "brand", variant: "outline", children: East.equal(lastEvent.length(), 0n).ifElse(_$ => "Interact with the canvas", _$ => lastEvent) })),
1182
+ }, spec: [
1183
+ Configurator.Spec("Resolution", sel.axis.unwrap("time").resolution.getTag()),
1184
+ Configurator.Spec("Rows", East.print(ops.read().size())),
1185
+ ] }));
1186
+ } }))),
1187
+ inputs: [],
1188
+ });
1189
+ // ============================================================================
1190
+ // planMeasures — measure rows over weekly readings, folded to the resolution
1191
+ // the slice states (#824)
1192
+ // ============================================================================
1193
+ /**
1194
+ * A measure — its readings a week apiece from W23, the window's first week, a
1195
+ * week with none read nothing: `readings` the series its row draws, and
1196
+ * `second` the set a row pairs with it — the stacked columns' second hall, the
1197
+ * dual chart's on-time line, a table's Δ against plan; `lo` / `hi` a band's
1198
+ * bounds; and a finishing line's capacity, stored in the element vocabulary
1199
+ * itself (`segments`). A measure nests (`children`): a hall holds its presses,
1200
+ * a top its contracts, a contract its orders — the hierarchy is the data's own
1201
+ * (#822), to whatever depth it has.
1202
+ */
1203
+ export const Measure = RecursiveType((self) => StructType({
1204
+ series: StringType, label: StringType,
1205
+ sub: OptionType(StringType), value: OptionType(StringType),
1206
+ readings: ArrayType(OptionType(FloatType)),
1207
+ second: ArrayType(OptionType(FloatType)),
1208
+ lo: ArrayType(FloatType), hi: ArrayType(FloatType),
1209
+ segments: ArrayType(Plan.Types.SegmentCell),
1210
+ children: DictType(StringType, self),
1211
+ }));
1212
+ /** On-time %, a week apiece from W23: July's weeks run under the 92 breach. */
1213
+ const MEASURE_ON_TIME = [some(96.1), some(96.4), some(96.8), some(97.0), some(96.2), some(91.0), some(89.4), some(88.9), some(90.6), some(93.8), some(94.0), some(94.2)];
1214
+ /** Sheets printed, thousands a week — Hall 1's, then Hall 2's. */
1215
+ const MEASURE_PRINTED_H1 = [some(28.0), some(34.0), some(40.0), some(29.0), some(35.0), some(41.0), some(30.0), some(36.0), some(42.0), some(31.0), some(37.0), some(43.0)];
1216
+ const MEASURE_PRINTED_H2 = [some(14.0), some(19.0), some(24.0), some(16.0), some(21.0), some(26.0), some(18.0), some(23.0), some(15.0), some(20.0), some(25.0), some(17.0)];
1217
+ /** An order's delivered sheets, thousands a week — the week of 3 August none. */
1218
+ const MEASURE_ACT = [some(40.0), some(47.0), some(54.0), some(61.0), some(68.0), some(75.0), some(82.0), some(89.0), some(96.0), none, some(110.0), some(117.0)];
1219
+ /** The Δ against plan beside them — some weeks none. */
1220
+ const MEASURE_DELTA = [some(-8.0), some(-6.5), some(-5.0), none, some(-2.0), some(-0.5), some(1.0), none, some(4.0), some(5.5), some(7.0), none];
1221
+ /** What left as the sheets came in. */
1222
+ const MEASURE_OUT = [some(-12.0), some(-15.0), some(-18.0), some(-21.0), some(-24.0), some(-27.0), some(-30.0), some(-33.0), some(-36.0), some(-39.0), some(-42.0), some(-45.0)];
1223
+ /**
1224
+ * Twelve weeks of every measure, W23–W34 — from the first Monday of June, so
1225
+ * the first month's column starts with the first week; the weeks after now
1226
+ * (W27) read ahead.
1227
+ */
1228
+ export const planMeasureReadings = e3.input("plan_measure_readings", DictType(StringType, Measure), variant("value", new Map([
1229
+ // ── Output ──
1230
+ ["spark", { series: "spark", label: "On-time", sub: none, value: some("94.2%"),
1231
+ readings: MEASURE_ON_TIME, second: [], lo: [], hi: [], segments: [], children: new Map() }],
1232
+ // A running total: each week the sheets printed so far.
1233
+ ["cum", { series: "cum", label: "Cumulative · k", sub: none, value: some("194 k sheets"),
1234
+ readings: [some(40.0), some(54.0), some(68.0), some(82.0), some(96.0), some(110.0), some(124.0), some(138.0), some(152.0), some(166.0), some(180.0), some(194.0)],
1235
+ second: [], lo: [], hi: [], segments: [], children: new Map() }],
1236
+ ["stacked", { series: "stacked", label: "Printed · k", sub: some("k/wk"), value: none,
1237
+ readings: MEASURE_PRINTED_H1, second: MEASURE_PRINTED_H2, lo: [], hi: [], segments: [], children: new Map() }],
1238
+ ["ppm", { series: "ppm", label: "Defects · ppm", sub: none, value: some("161"),
1239
+ readings: [some(120.0), some(157.0), some(134.0), some(171.0), some(148.0), some(125.0), some(162.0), some(139.0), some(176.0), some(153.0), some(130.0), some(167.0)],
1240
+ second: [], lo: [], hi: [], segments: [], children: new Map() }],
1241
+ ["refs", { series: "refs", label: "On-time + refs", sub: none, value: none,
1242
+ readings: MEASURE_ON_TIME, second: [], lo: [], hi: [], segments: [], children: new Map() }],
1243
+ // Output columns on the left axis; the on-time line and its ±3 band on the right.
1244
+ ["dual", { series: "dual", label: "Out + on-time", sub: none, value: none,
1245
+ readings: MEASURE_PRINTED_H1, second: MEASURE_ON_TIME,
1246
+ lo: [93.1, 93.4, 93.8, 94.0, 93.2, 88.0, 86.4, 85.9, 87.6, 90.8, 91.0, 91.2],
1247
+ hi: [99.1, 99.4, 99.8, 100.0, 99.2, 94.0, 92.4, 91.9, 93.6, 96.8, 97.0, 97.2],
1248
+ segments: [], children: new Map() }],
1249
+ // ── Load ──
1250
+ // A hall with no readings of its own: its row is its presses' per-bucket
1251
+ // mean. H1-P03 read nothing in the week of 29 June; H1-P04 nothing in
1252
+ // August, which folds to a month with no data.
1253
+ ["hall1", { series: "depth", label: "Hall 1", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1254
+ children: new Map([
1255
+ ["p03h", { series: "depth", label: "H1-P03", sub: none, value: none, second: [], lo: [], hi: [], segments: [], children: new Map(),
1256
+ readings: [some(46.0), some(52.0), some(58.0), some(61.0), none, some(72.0), some(78.0), some(84.0), some(90.0), some(96.0), some(98.0), some(92.0)] }],
1257
+ ["p04h", { series: "depth", label: "H1-P04", sub: none, value: none, second: [], lo: [], hi: [], segments: [], children: new Map(),
1258
+ readings: [some(44.0), some(50.0), some(55.0), some(60.0), some(63.0), some(70.0), some(74.0), some(80.0), some(86.0), none, none, none] }],
1259
+ ]) }],
1260
+ ["peak", { series: "peak", label: "Hall 2 peak %", sub: none, value: none,
1261
+ readings: [some(45.0), some(62.0), some(79.0), some(46.0), some(63.0), some(80.0), some(47.0), some(64.0), some(81.0), some(48.0), some(65.0), some(82.0)],
1262
+ second: [], lo: [], hi: [], segments: [], children: new Map() }],
1263
+ // Crew A's booked fraction of its hours, a week apiece.
1264
+ ["booked", { series: "booked", label: "Crew A", sub: some("booked h"), value: none,
1265
+ readings: [some(0.9), some(0.84), some(0.79), some(0.74), some(0.68), some(0.62), some(0.57), some(0.52), some(0.46), some(0.41), some(0.35), some(0.3)],
1266
+ second: [], lo: [], hi: [], segments: [], children: new Map() }],
1267
+ // ── Finishing ──
1268
+ // Capacity compositions — plain `{ fill, weight, label }` records. June's
1269
+ // and August's are their month's only one, so they keep their labels.
1270
+ ["finishing", { series: "segments", label: "Finishing line", sub: some("capacity"), value: none,
1271
+ readings: [], second: [], lo: [], hi: [], children: new Map(),
1272
+ segments: [
1273
+ { at: variant("time", new Date("2026-06-29T00:00:00Z")), segments: [
1274
+ { fill: variant("success", null), weight: 60.0, label: some("60%") },
1275
+ { fill: variant("warning", null), weight: 25.0, label: some("25%") },
1276
+ { fill: variant("slack", null), weight: 15.0, label: none },
1277
+ ] },
1278
+ { at: variant("time", new Date("2026-07-06T00:00:00Z")), segments: [
1279
+ { fill: variant("success", null), weight: 70.0, label: some("70%") },
1280
+ { fill: variant("slack", null), weight: 30.0, label: none },
1281
+ ] },
1282
+ { at: variant("time", new Date("2026-07-13T00:00:00Z")), segments: [
1283
+ { fill: variant("danger", null), weight: 40.0, label: some("40%") },
1284
+ { fill: variant("free", null), weight: 60.0, label: none },
1285
+ ] },
1286
+ { at: variant("time", new Date("2026-08-10T00:00:00Z")), segments: [
1287
+ { fill: variant("success", null), weight: 55.0, label: some("55%") },
1288
+ { fill: variant("warning", null), weight: 30.0, label: some("30%") },
1289
+ { fill: variant("slack", null), weight: 15.0, label: none },
1290
+ ] },
1291
+ ] }],
1292
+ // ── Deliveries ──
1293
+ // Two levels of nesting — a top holds its contracts, a contract its
1294
+ // orders; every level with no readings of its own is a subtotal.
1295
+ ["deliveries", { series: "orders", label: "Deliveries", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1296
+ children: new Map([
1297
+ ["contract-a", { series: "orders", label: "Contract A", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1298
+ children: new Map([
1299
+ ["j6188", { series: "orders", label: "J-6188", sub: none, value: none, readings: MEASURE_ACT, second: [], lo: [], hi: [], segments: [], children: new Map() }],
1300
+ ["j6204", { series: "orders", label: "J-6204", sub: none, value: none, readings: MEASURE_ACT, second: [], lo: [], hi: [], segments: [], children: new Map() }],
1301
+ ]) }],
1302
+ ["contract-b", { series: "orders", label: "Contract B", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1303
+ children: new Map([
1304
+ ["j6219", { series: "orders", label: "J-6219", sub: none, value: none, readings: MEASURE_ACT, second: [], lo: [], hi: [], segments: [], children: new Map() }],
1305
+ ]) }],
1306
+ ]) }],
1307
+ ["reprints", { series: "orders", label: "Reprints", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1308
+ children: new Map([
1309
+ ["contract-b", { series: "orders", label: "Contract B", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1310
+ children: new Map([
1311
+ ["rp-0031", { series: "orders", label: "RP-0031", sub: none, value: none, readings: MEASURE_ACT, second: [], lo: [], hi: [], segments: [], children: new Map() }],
1312
+ ]) }],
1313
+ ]) }],
1314
+ // Footer emphasis, a negative and the em-dash: July read nothing at all.
1315
+ ["net", { series: "net", label: "Net flow", sub: none, value: none,
1316
+ readings: [some(22.0), some(-26.0), some(8.0), some(-4.0), some(12.0), none, none, none, none, some(-30.0), some(12.0), some(6.0)],
1317
+ second: [], lo: [], hi: [], segments: [], children: new Map() }],
1318
+ // Multi-value rows — the actuals and the Δ per row. The SPLIT (how the
1319
+ // positions sit against each other) and the GUTTER (one line or two) are
1320
+ // independent choices, so all four combinations are here: the pair that
1321
+ // reads well depends on the numbers, not on the split.
1322
+ ["actplan", { series: "actplan", label: "Act · Δ plan", sub: some("k/wk"), value: none,
1323
+ readings: MEASURE_ACT, second: MEASURE_DELTA, lo: [], hi: [], segments: [], children: new Map() }],
1324
+ ["inout", { series: "inout", label: "In / out", sub: none, value: none,
1325
+ readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
1326
+ // Horizontal, on a ONE-line gutter — the pair reads as a single fact
1327
+ // ("booked beside free"), so a sub label would only repeat it.
1328
+ ["sidebyside", { series: "sidebyside", label: "Booked · free", sub: none, value: none,
1329
+ readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
1330
+ // Vertical, on a TWO-line gutter — the stack needs the unit spelled out,
1331
+ // because the positions are the same measure at two times.
1332
+ ["overunder", { series: "overunder", label: "Act / plan", sub: some("k/wk"), value: none,
1333
+ readings: MEASURE_ACT, second: MEASURE_DELTA, lo: [], hi: [], segments: [], children: new Map() }],
1334
+ // NESTED and multi-value: the subtotal parent mirrors its members, an act
1335
+ // subtotal beside a Δ subtotal.
1336
+ ["lots", { series: "lot", label: "Lots", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1337
+ children: new Map([
1338
+ ["lt-1", { series: "lot", label: "LT-2201", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_DELTA, lo: [], hi: [], segments: [], children: new Map() }],
1339
+ ["lt-2", { series: "lot", label: "LT-2202", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_DELTA, lo: [], hi: [], segments: [], children: new Map() }],
1340
+ ]) }],
1341
+ // The same, STACKED: members and their subtotal both put the two positions
1342
+ // on their own lines, so the parent has to grow too.
1343
+ ["stacks", { series: "stack", label: "Stacks", sub: none, value: none, readings: [], second: [], lo: [], hi: [], segments: [],
1344
+ children: new Map([
1345
+ ["st-1", { series: "stack", label: "ST-3301", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
1346
+ ["st-2", { series: "stack", label: "ST-3302", sub: none, value: none, readings: MEASURE_ACT, second: MEASURE_OUT, lo: [], hi: [], segments: [], children: new Map() }],
1347
+ ]) }],
1348
+ // A balance: the paper in stock, thousands of sheets, each week's close.
1349
+ ["stock", { series: "stock", label: "Paper stock · k", sub: none, value: none,
1350
+ readings: [some(420.0), some(398.0), some(376.0), some(354.0), some(332.0), some(310.0), some(288.0), some(266.0), some(244.0), some(222.0), some(200.0), some(178.0)],
1351
+ second: [], lo: [], hi: [], segments: [], children: new Map() }],
1352
+ ])));
1353
+ /** A week the canvas shows — the rows its slice narrows. */
1354
+ export const MeasureWeek = StructType({ week: DateTimeType });
1355
+ /**
1356
+ * Measure rows — the chart, heat and table grammars over weekly readings, and
1357
+ * the temporal fold (#824) that shows them at whatever resolution the slice
1358
+ * states. At MONTH, where it opens, every row shows ONE value per month — the
1359
+ * fold of its weeks there — where it would stack four or five on top of each
1360
+ * other. Each cell builder and chart layer declares its fold, defaulting to
1361
+ * what its values mean: a heat level, a weight fraction and a line by `mean`,
1362
+ * a table numeral and a column by `sum`. A row overrides it where the meaning
1363
+ * differs — peak load by `max`; a running total and a closing stock by
1364
+ * `last`; and the dual chart's output columns by `mean`, so its fixed scale
1365
+ * holds at every resolution. A bucket with ONE week keeps it as it is, and a
1366
+ * month with no reading is no data.
1367
+ *
1368
+ * - **Chart rows**: a line with its breach (July's mean falls under 92), an
1369
+ * area, columns stacked by series id on a two-line gutter, a scatter that
1370
+ * draws every reading, every annotation (`refLine`, `refBand`, `refDot`) at
1371
+ * expanded density, and a fixed-height dual-axis composition — columns
1372
+ * left, a line and its band right, swatches, domains and ticks.
1373
+ * - **Heat rows**: colour depth under a hall averaging its presses on its
1374
+ * `scale`, with the no-data hatch — a week, and a whole month, with no
1375
+ * reading — weight bars whose planned tail is pale, and status segments.
1376
+ * - **Table rows**: subtotals at every depth of the data's nesting, footer
1377
+ * emphasis with a negative and the em-dash, and multi-value positions —
1378
+ * strong and muted, signed, `rollup` — in every split × gutter combination.
1379
+ *
1380
+ * The switch is the toolbar's (#1258): a slice bound over the weeks the
1381
+ * canvas shows declares the `resolution` affordance, and the axis the
1382
+ * resolutions it offers, so the toolbar's segment switches MONTH and WEEK. A
1383
+ * switch keeps the canvas's column count — three months, then the first three
1384
+ * weeks — and the horizon brush moves the window across the twelve.
1385
+ */
1386
+ export const planMeasures = example({
1387
+ keywords: [
1388
+ "Plan", "measures", "data", "series", "chart", "layers", "spark", "expanded", "fixed", "refLine",
1389
+ "refBand", "refDot", "breach", "stacked", "dual-axis", "swatches", "Area", "Band", "Scatter", "Column",
1390
+ "Line", "domain", "tickValues", "heat", "Matrix", "cells", "depth", "aggregate", "mean", "children",
1391
+ "nested", "recursive", "RecursiveType", "scale", "warnAt", "heatCells", "weightCells", "segmentCells",
1392
+ "segment", "no-data", "hatch", "table", "tableCells", "subtotal", "sum", "format", "emphasis", "footer",
1393
+ "em-dash", "neg", "tableSeries", "split", "horizontal", "vertical", "multi-value", "multi-cell", "two-line",
1394
+ "strong", "muted", "rollup", "mirror", "position", "fold", "temporal fold", "resolution", "week", "month",
1395
+ "rebucket", "bucket", "max", "last", "count", "default", "override", "layer", "Plan.layer", "column",
1396
+ "line", "Slice", "Slice.bind", "affordances", "toolbar", "range", "brush", "section", "match", "gutter",
1397
+ "raw", "readings", "Reactive", "Data.bind", "bound", "e3.input", "#824", "#1258",
1398
+ ],
1399
+ description: "Measure rows over weekly readings bound from e3, at MONTH to start — chart rows (a line with its breach, an area, stacked columns, a scatter, every annotation, and a fixed dual-axis composition with its band and swatches), heat rows (colour depth under a hall averaging its presses on its scale with the no-data hatch, weight bars with a planned tail, status segments) and table rows (subtotals at every depth, footer emphasis with a negative and the em-dash, multi-value positions in every split × gutter combination); each row folds a month's weeks by what its values mean, or by its override (peak load by max, a running total and the paper stock by last, the dual chart's columns by mean), and the toolbar's resolution segment, over a slice of the weeks shown, switches MONTH and WEEK",
1400
+ fn: East.function([], UIComponentType, (_$) => {
1401
+ return (_jsx(Reactive, { children: $ => {
1402
+ const measures = $.let(Data.bind(planMeasureReadings));
1403
+ // Monday of ISO week n, 2026 — twelve weeks, W23–W34.
1404
+ const week = $.const(East.function([IntegerType], DateTimeType, ($, n) => {
1405
+ const w1 = $.const(new Date("2025-12-29T00:00:00Z"), DateTimeType);
1406
+ return w1.addWeeks(n.subtract(1n));
1407
+ }));
1408
+ const Point = StructType({ week: DateTimeType, v: FloatType });
1409
+ const BandPoint = StructType({ week: DateTimeType, lo: FloatType, hi: FloatType });
1410
+ const Weekly = StructType({ at: DateTimeType, value: OptionType(FloatType) });
1411
+ // A measure's readings from W23, as each row kind reads them: a
1412
+ // chart's points (a week with no reading draws none), heat cells
1413
+ // printing their values (a week with none the no-data hatch),
1414
+ // booked fractions (the weeks after now the planned, pale tail),
1415
+ // and table cells (a week with none the em-dash).
1416
+ const points = $.const(East.function([ArrayType(OptionType(FloatType))], ArrayType(Point), ($, readings) => readings.filterMap((_$2, r, i) => r.match({
1417
+ some: (_$3, v) => East.value(some({ week: week(i.add(23n)), v }), OptionType(Point)),
1418
+ none: (_$3) => East.value(none, OptionType(Point)),
1419
+ }))));
1420
+ const band = $.const(East.function([ArrayType(FloatType), ArrayType(FloatType)], ArrayType(BandPoint), ($, lo, hi) => East.Array.generate(lo.size(), BandPoint, (_$2, i) => ({ week: week(i.add(23n)), lo: lo.get(i), hi: hi.get(i) }))));
1421
+ const heat = $.const(East.function([ArrayType(OptionType(FloatType))], ArrayType(Plan.Types.HeatCell), ($, readings) => East.Array.generate(readings.size(), Plan.Types.HeatCell, ($2, i) => {
1422
+ const value = $2.let(readings.get(i), OptionType(FloatType));
1423
+ const label = $2.let(none, OptionType(StringType));
1424
+ $2.match(value, { some: ($3, v) => { $3.assign(label, some(East.Float.printFixed(v, 0n))); } });
1425
+ return { at: Plan.at.time(week(i.add(23n))), value, label };
1426
+ })));
1427
+ const booked = $.const(East.function([ArrayType(OptionType(FloatType))], ArrayType(Plan.Types.WeightCell), ($, readings) => readings.filterMap((_$2, r, i) => r.match({
1428
+ some: (_$3, f) => East.value(some({ at: Plan.at.time(week(i.add(23n))), fraction: f, planned: i.add(23n).greater(27n) }), OptionType(Plan.Types.WeightCell)),
1429
+ none: (_$3) => East.value(none, OptionType(Plan.Types.WeightCell)),
1430
+ }))));
1431
+ const weekly = $.const(East.function([ArrayType(OptionType(FloatType))], ArrayType(Weekly), ($, readings) => East.Array.generate(readings.size(), Weekly, (_$2, i) => ({ at: week(i.add(23n)), value: readings.get(i) }))));
1432
+ const whole = $.const(Format.Number({ maximumFractionDigits: 0n }));
1433
+ const series = $.const([
1434
+ Plan.series.section(Measure, { key: "output", title: "Output", meta: "5 rows" }, [
1435
+ // A level: the on-time line folds a month's weeks by their
1436
+ // MEAN, a line's default — and July's falls through the
1437
+ // breach. Its caret opens it to a custom 120px
1438
+ // (expandedHeight, default 88).
1439
+ Plan.series.chart(Measure, {
1440
+ key: "spark", title: "On-time",
1441
+ match: r => r.series.equal("spark"),
1442
+ label: r => r.label, id: true,
1443
+ value: r => r.value, status: _r => some(variant("warning", null)),
1444
+ height: "spark", expandable: true, expandedHeight: "120px",
1445
+ layers: r => [Plan.layer(Chart.Line(points(r.readings), { x: p => p.week, y: p => p.v }), { breach: { below: 92 } })],
1446
+ }),
1447
+ // A running total — a month shows where it CLOSED.
1448
+ Plan.series.chart(Measure, {
1449
+ key: "cum", title: "Cumulative",
1450
+ match: r => r.series.equal("cum"),
1451
+ label: r => r.label, id: true, value: r => r.value,
1452
+ layers: r => [Plan.layer(Chart.Area(points(r.readings), { x: p => p.week, y: p => p.v }), { fold: "last" })],
1453
+ }),
1454
+ // Amounts — the two halls' columns stacked by one series id,
1455
+ // each month their weeks' SUM, on a two-line gutter (label
1456
+ // over sub).
1457
+ Plan.series.chart(Measure, {
1458
+ key: "stacked", title: "Stacked",
1459
+ match: r => r.series.equal("stacked"),
1460
+ label: r => r.label, id: true, stacked: true, sub: r => r.sub,
1461
+ layers: r => [
1462
+ Plan.layer(Chart.Column(points(r.readings), { x: p => p.week, y: p => p.v }), { series: "H1" }),
1463
+ Plan.layer(Chart.Column(points(r.second), { x: p => p.week, y: p => p.v }), { series: "H2" }),
1464
+ ],
1465
+ }),
1466
+ // A scatter draws every reading, folded or not.
1467
+ Plan.series.chart(Measure, {
1468
+ key: "ppm", title: "Ppm",
1469
+ match: r => r.series.equal("ppm"),
1470
+ label: r => r.label, id: true, value: r => r.value,
1471
+ layers: r => [Chart.Scatter(points(r.readings), { x: p => p.week, y: p => p.v })],
1472
+ }),
1473
+ // A line and every annotation kind, at expanded density.
1474
+ Plan.series.chart(Measure, {
1475
+ key: "refs", title: "Refs",
1476
+ match: r => r.series.equal("refs"),
1477
+ label: r => r.label, id: true,
1478
+ height: "expanded",
1479
+ layers: r => [
1480
+ Plan.layer(Chart.Line(points(r.readings), { x: p => p.week, y: p => p.v }), { breach: { below: 92 } }),
1481
+ Chart.refLine({ y: 100, label: "TARGET 100" }),
1482
+ Chart.refBand({ x: [week(29n), week(31n)], label: "CRUNCH" }),
1483
+ Chart.refDot({ x: week(30n), y: 88.9, label: "LOW" }),
1484
+ ],
1485
+ }),
1486
+ ]),
1487
+ // The composed dual-axis chart; its axes take Chart.Root's
1488
+ // vocabulary — domain / tickValues. Output columns scale left,
1489
+ // folded by their MEAN, a week's average, so the fixed 0–60
1490
+ // scale holds at every resolution; the on-time line and its
1491
+ // band scale right.
1492
+ Plan.series.section(Measure, { key: "quality", title: "Quality", meta: "1 row" }, [
1493
+ Plan.series.chart(Measure, {
1494
+ key: "dual", title: "Dual",
1495
+ match: r => r.series.equal("dual"),
1496
+ label: r => r.label, id: true,
1497
+ height: Plan.fixed("120px"),
1498
+ left: { domain: [0, 60], tickValues: [0, 25, 50] },
1499
+ right: { domain: [80, 105], tickValues: [85, 95, 105] },
1500
+ swatches: [{ color: "ink.3", label: "out" }, { color: "brand.d", label: "on-time · rh" }],
1501
+ layers: r => [
1502
+ Plan.layer(Chart.Column(points(r.readings), { x: p => p.week, y: p => p.v }), { fold: "mean" }),
1503
+ Plan.layer(Chart.Line(points(r.second), { x: p => p.week, y: p => p.v }), { axis: "right" }),
1504
+ Plan.layer(Chart.Band(band(r.lo, r.hi), { x: p => p.week, low: p => p.lo, high: p => p.hi }), { axis: "right" }),
1505
+ ],
1506
+ }),
1507
+ ]),
1508
+ Plan.series.section(Measure, { key: "load", title: "Load", meta: "5 rows" }, [
1509
+ // A hall's presses nest under it, and its row is their
1510
+ // per-bucket MEAN — painted on `scale`, the scale a parent's
1511
+ // DERIVED cells take (#824).
1512
+ Plan.series.heat(Measure, {
1513
+ key: "depth", title: "Depth",
1514
+ match: r => r.series.equal("depth"),
1515
+ label: r => r.label, id: true,
1516
+ cells: r => Plan.heatCells(heat(r.readings), { min: 0, max: 100, warnAt: 95 }),
1517
+ children: r => r.children, aggregate: "mean",
1518
+ scale: { min: 0, max: 100, warnAt: 95 },
1519
+ }),
1520
+ // The same kind of reading, folded by its MAX — the peak a
1521
+ // month hit, printed through the declared format.
1522
+ Plan.series.heat(Measure, {
1523
+ key: "peak", title: "Peak load",
1524
+ match: r => r.series.equal("peak"),
1525
+ label: r => r.label,
1526
+ cells: r => Plan.heatCells(heat(r.readings), { min: 0, max: 100, fold: "max", format: whole }),
1527
+ }),
1528
+ // A fraction of each week booked — a month's is its weeks'
1529
+ // mean, pale only when every one of them is planned.
1530
+ Plan.series.heat(Measure, {
1531
+ key: "booked", title: "Booked",
1532
+ match: r => r.series.equal("booked"),
1533
+ label: r => r.label, sub: r => r.sub,
1534
+ cells: r => Plan.weightCells(booked(r.readings)),
1535
+ }),
1536
+ ]),
1537
+ Plan.series.section(Measure, { key: "finishing", title: "Finishing", meta: "1 row" }, [
1538
+ // A month of several weeks sums each fill, and draws no
1539
+ // in-bar label: a label described one week.
1540
+ Plan.series.heat(Measure, {
1541
+ key: "segments", title: "Segments",
1542
+ match: r => r.series.equal("segments"),
1543
+ label: r => r.label, sub: r => r.sub,
1544
+ cells: r => Plan.segmentCells(r.segments),
1545
+ }),
1546
+ ]),
1547
+ Plan.series.section(Measure, { key: "deliveries", title: "Deliveries", meta: "21 rows" }, [
1548
+ // Each order nests under its contract, each contract under its
1549
+ // top (`children`, to any depth), and every parent sums its
1550
+ // children.
1551
+ Plan.series.table(Measure, {
1552
+ key: "orders", title: "Orders",
1553
+ match: r => r.series.equal("orders"),
1554
+ label: r => r.label,
1555
+ cells: r => Plan.tableCells(weekly(r.readings)),
1556
+ children: r => r.children, aggregate: "sum",
1557
+ format: whole,
1558
+ }),
1559
+ Plan.series.table(Measure, {
1560
+ key: "net", title: "Net",
1561
+ match: r => r.series.equal("net"),
1562
+ label: r => r.label, emphasis: "footer",
1563
+ cells: r => Plan.tableCells(weekly(r.readings)),
1564
+ format: whole,
1565
+ }),
1566
+ // Per-POSITION style declared ONCE, in the CONFIG — a strong
1567
+ // rolled-up actual beside its muted, always-signed plan Δ.
1568
+ Plan.series.table(Measure, {
1569
+ key: "actplan", title: "Actual vs plan",
1570
+ match: r => r.series.equal("actplan"),
1571
+ label: r => r.label, stacked: true, sub: r => r.sub,
1572
+ series: r => [
1573
+ Plan.tableSeries({ strong: true, rollup: true, cells: Plan.tableCells(weekly(r.readings)) }),
1574
+ Plan.tableSeries({
1575
+ tone: "muted",
1576
+ format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
1577
+ cells: Plan.tableCells(weekly(r.second)),
1578
+ }),
1579
+ ],
1580
+ format: whole,
1581
+ }),
1582
+ // The VERTICAL split stacks the positions; the row grows.
1583
+ Plan.series.table(Measure, {
1584
+ key: "inout", title: "Inout",
1585
+ match: r => r.series.equal("inout"),
1586
+ label: r => r.label, split: "vertical",
1587
+ series: r => [
1588
+ Plan.tableSeries({ cells: Plan.tableCells(weekly(r.readings)) }),
1589
+ Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(weekly(r.second)) }),
1590
+ ],
1591
+ format: whole,
1592
+ }),
1593
+ // HORIZONTAL on a ONE-line gutter — the other half of the pair
1594
+ // above: the split is a cell-layout choice and the gutter a
1595
+ // label choice, so neither implies the other.
1596
+ Plan.series.table(Measure, {
1597
+ key: "sidebyside", title: "Side by side",
1598
+ match: r => r.series.equal("sidebyside"),
1599
+ label: r => r.label, split: "horizontal",
1600
+ series: r => [
1601
+ Plan.tableSeries({ strong: true, cells: Plan.tableCells(weekly(r.readings)) }),
1602
+ Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(weekly(r.second)) }),
1603
+ ],
1604
+ format: whole,
1605
+ }),
1606
+ // A MULTI-VALUE series under a subtotal parent — every position
1607
+ // rolls up, so the parent shows an act subtotal beside a Δ
1608
+ // subtotal instead of collapsing to one number and looking
1609
+ // complete. Flag a position `rollup: true` to narrow it back to
1610
+ // that one.
1611
+ Plan.series.table(Measure, {
1612
+ key: "lot", title: "Lot",
1613
+ match: r => r.series.equal("lot"),
1614
+ label: r => r.label,
1615
+ series: r => [
1616
+ Plan.tableSeries({ strong: true, cells: Plan.tableCells(weekly(r.readings)) }),
1617
+ Plan.tableSeries({
1618
+ tone: "muted",
1619
+ format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
1620
+ cells: Plan.tableCells(weekly(r.second)),
1621
+ }),
1622
+ ],
1623
+ children: r => r.children, aggregate: "sum",
1624
+ format: whole,
1625
+ }),
1626
+ // NESTED and VERTICAL — the subtotal stacks its positions the
1627
+ // way its members do. The parent's positions carry no values
1628
+ // of their own (they are derived), so its height comes from
1629
+ // its members' count: a parent that read its own empty cells
1630
+ // as one line would render as two.
1631
+ Plan.series.table(Measure, {
1632
+ key: "stack", title: "Stack",
1633
+ match: r => r.series.equal("stack"),
1634
+ label: r => r.label, split: "vertical",
1635
+ series: r => [
1636
+ Plan.tableSeries({ strong: true, cells: Plan.tableCells(weekly(r.readings)) }),
1637
+ Plan.tableSeries({ tone: "muted", cells: Plan.tableCells(weekly(r.second)) }),
1638
+ ],
1639
+ children: r => r.children, aggregate: "sum",
1640
+ format: whole,
1641
+ }),
1642
+ // VERTICAL on a TWO-line gutter — the remaining combination,
1643
+ // and the one that grows the row in BOTH directions at once.
1644
+ Plan.series.table(Measure, {
1645
+ key: "overunder", title: "Over / under",
1646
+ match: r => r.series.equal("overunder"),
1647
+ label: r => r.label, split: "vertical", stacked: true, sub: r => r.sub,
1648
+ series: r => [
1649
+ Plan.tableSeries({ strong: true, rollup: true, cells: Plan.tableCells(weekly(r.readings)) }),
1650
+ Plan.tableSeries({
1651
+ tone: "muted",
1652
+ format: Format.Number({ maximumFractionDigits: 0n, signDisplay: "always" }),
1653
+ cells: Plan.tableCells(weekly(r.second)),
1654
+ }),
1655
+ ],
1656
+ format: whole,
1657
+ }),
1658
+ // A balance — a month shows where it CLOSED, its last week.
1659
+ Plan.series.table(Measure, {
1660
+ key: "stock", title: "Paper stock",
1661
+ match: r => r.series.equal("stock"),
1662
+ label: r => r.label,
1663
+ cells: r => Plan.tableCells(weekly(r.readings)), fold: "last",
1664
+ format: whole,
1665
+ }),
1666
+ ]),
1667
+ ], ArrayType(Plan.Types.Series(Measure)));
1668
+ // The weeks the canvas shows — the rows its slice narrows.
1669
+ const weeks = $.let(East.Array.generate(12n, MeasureWeek, (_$2, i) => ({ week: week(i.add(23n)) })));
1670
+ // The resolution is the slice's, MONTH to start, and the window
1671
+ // its range. A slice range is CLOSED — both ends inclusive — so
1672
+ // the twelve weeks W23–W34 end the millisecond before W35.
1673
+ const slice = $.let(Slice.bind("ex.plan.measures", weeks, {
1674
+ fields: { week: { label: "Week", format: { date: "MMM D" } } },
1675
+ range: "week",
1676
+ initial: { week: { from: week(23n), to: week(35n).addMilliseconds(-1n) } },
1677
+ view: { resolution: some(variant("month", null)) },
1678
+ }));
1679
+ const axis = $.const(Plan.axis({
1680
+ window: { min: week(23n), max: week(35n) },
1681
+ resolution: "month", resolutions: ["month", "week"], now: week(27n),
1682
+ }));
1683
+ return (_jsx(Plan, { axis: axis, data: measures, series: series, slice: { slice, affordances: ["range", "resolution", "brush"] } }));
1684
+ } }));
1685
+ }),
1686
+ inputs: [],
1687
+ });
1688
+ // ============================================================================
1689
+ // planRowDrop — `data`'s rows edited in place: cards dropped from the Plan's
1690
+ // library panel, runs moved and resized, one checked Save (#880, #825, #1259)
1691
+ // ============================================================================
1692
+ /** A job, its label already composed. */
1693
+ export const DropJob = StructType({
1694
+ key: StringType, label: StringType,
1695
+ start: DateTimeType, end: DateTimeType, state: EventStateType,
1696
+ });
1697
+ /** A van drop. */
1698
+ export const DropAlloc = StructType({ key: StringType, at: DateTimeType, state: EventStateType });
1699
+ /** A crew shift, its label already composed. */
1700
+ export const DropShift = StructType({
1701
+ key: StringType, from: DateTimeType, to: DateTimeType, label: StringType, state: EventStateType,
1702
+ });
1703
+ /** A press a hall holds — its jobs; its name is its key. */
1704
+ export const DropPress = StructType({ jobs: ArrayType(DropJob) });
1705
+ /** One row of the ops record — `series` names the series that claims it;
1706
+ * `readings` are fortnightly from W27; a hall holds its presses
1707
+ * (`presses`), the hierarchy the data's own. */
1708
+ export const DropOpsRow = StructType({
1709
+ series: StringType, label: StringType,
1710
+ jobs: ArrayType(DropJob),
1711
+ readings: ArrayType(FloatType),
1712
+ allocs: ArrayType(DropAlloc),
1713
+ nums: ArrayType(Plan.Types.TableCell),
1714
+ shifts: ArrayType(DropShift),
1715
+ marks: ArrayType(Plan.Types.EventMark),
1716
+ presses: DictType(StringType, DropPress),
1717
+ });
1718
+ /**
1719
+ * The ops RECORD every gesture drafts and Save commits to — its initial state
1720
+ * the genesis commit. The droppable and inert kinds interleave, and Hall 3 is
1721
+ * an entry holding its two presses, H3-P11 already running four jobs, so one
1722
+ * more is refused.
1723
+ */
1724
+ export const planDropOps = e3.record("plan_drop_ops", DictType(StringType, DropOpsRow), new Map([
1725
+ ["util", { series: "util", label: "Util %", jobs: [], readings: [46.0, 58.0, 66.0, 72.0, 84.0, 96.0],
1726
+ allocs: [], nums: [], shifts: [], marks: [], presses: new Map() }],
1727
+ ["p03", { series: "press", label: "H1-P03", readings: [], allocs: [], nums: [], shifts: [], marks: [], presses: new Map(),
1728
+ jobs: [{ key: "j4642", label: "RUN · J-4642", start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), state: variant("in-progress", null) }] }],
1729
+ ["p04", { series: "press", label: "H1-P04", readings: [], allocs: [], nums: [], shifts: [], marks: [], presses: new Map(),
1730
+ jobs: [{ key: "j4624", label: "RUN · J-4624", start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-20T00:00:00Z"), state: variant("confirmed", null) }] }],
1731
+ ["load", { series: "load", label: "H2 load", jobs: [], readings: [46.0, 58.0, 66.0, 72.0, 84.0, 96.0],
1732
+ allocs: [], nums: [], shifts: [], marks: [], presses: new Map() }],
1733
+ ["van1", { series: "van", label: "Van 1", jobs: [], readings: [], nums: [], shifts: [], marks: [], presses: new Map(),
1734
+ allocs: [{ key: "a1", at: new Date("2026-07-13T00:00:00Z"), state: variant("confirmed", null) }] }],
1735
+ ["desp", { series: "table", label: "Delivered · k", jobs: [], readings: [], allocs: [], shifts: [], marks: [], presses: new Map(),
1736
+ nums: [
1737
+ { at: variant("time", new Date("2026-07-06T00:00:00Z")), value: some(128.0), text: none, tone: none },
1738
+ { at: variant("time", new Date("2026-07-27T00:00:00Z")), value: some(-96.0), text: none, tone: none },
1739
+ ] }],
1740
+ ["crewA", { series: "crew", label: "Crew A", jobs: [], readings: [], allocs: [], nums: [], marks: [], presses: new Map(),
1741
+ shifts: [{ key: "s1", from: new Date("2026-06-29T00:00:00Z"), to: new Date("2026-07-13T00:00:00Z"), label: "80h", state: variant("confirmed", null) }] }],
1742
+ ["ms", { series: "strm", label: "Milestones", jobs: [], readings: [], allocs: [], nums: [], shifts: [], presses: new Map(),
1743
+ marks: [{ key: "k", at: variant("time", new Date("2026-07-13T00:00:00Z")), kind: variant("milestone", null), icon: none, label: some("KICKOFF") }] }],
1744
+ // A HALL, holding its presses: a gesture on one of them drafts the hall,
1745
+ // the entry they ride in. H3-P11 already runs four jobs.
1746
+ ["hall3", { series: "hall", label: "Hall 3", jobs: [], readings: [], allocs: [], nums: [], shifts: [], marks: [],
1747
+ presses: new Map([
1748
+ ["H3-P11", { jobs: [
1749
+ { key: "j4897", label: "RUN · J-4897", start: new Date("2026-06-29T00:00:00Z"), end: new Date("2026-07-13T00:00:00Z"), state: variant("confirmed", null) },
1750
+ { key: "j4903", label: "RUN · J-4903", start: new Date("2026-07-20T00:00:00Z"), end: new Date("2026-08-17T00:00:00Z"), state: variant("confirmed", null) },
1751
+ { key: "j4911", label: "RUN · J-4911", start: new Date("2026-08-17T00:00:00Z"), end: new Date("2026-08-31T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
1752
+ { key: "j4918", label: "RUN · J-4918", start: new Date("2026-08-31T00:00:00Z"), end: new Date("2026-09-14T00:00:00Z"), state: variant("proposed", variant("recommended", null)) },
1753
+ ] }],
1754
+ ["H3-P12", { jobs: [
1755
+ { key: "j4925", label: "RUN · J-4925", start: new Date("2026-07-06T00:00:00Z"), end: new Date("2026-07-27T00:00:00Z"), state: variant("confirmed", null) },
1756
+ ] }],
1757
+ ]) }],
1758
+ ]));
1759
+ /** The ops record's patch door — every Save commits through it. */
1760
+ export const planDropOpsPatch = e3.mutation.patch(planDropOps);
1761
+ /** A card in the palette — a thing a row of its family takes. */
1762
+ export const DropCard = StructType({ name: StringType, family: StringType, note: StringType });
1763
+ /** The palette's cards, by key — a card's key is what a drop names it by. */
1764
+ export const planDropCards = e3.input("plan_drop_cards", DictType(StringType, DropCard), variant("value", new Map([
1765
+ ["job-poster", { name: "Poster run", family: "job", note: "job · presses" }],
1766
+ ["job-leaflet", { name: "Leaflet run", family: "job", note: "job · presses" }],
1767
+ ["dlv-van", { name: "Van 3", family: "delivery", note: "delivery · vans" }],
1768
+ ["shf-night", { name: "Night shift", family: "shift", note: "shift · crews" }],
1769
+ ["mst-check", { name: "Press check", family: "milestone", note: "milestone · streams" }],
1770
+ // Of a family no row takes, so every row refuses it — the ⊘ stage
1771
+ // everywhere, which is what a card with nowhere to go looks like.
1772
+ ["pallet", { name: "PALLET", family: "other", note: "fits nowhere" }],
1773
+ ])));
1774
+ /**
1775
+ * `data`'s rows edited in place (#880) — the Plan as a drag TARGET, a
1776
+ * heterogeneous one, which is what makes it different from every other target
1777
+ * in the grammar, and its own runs moved and resized (#825).
1778
+ *
1779
+ * The palette is the Plan's own library panel (#1259): `Plan.library.tab`
1780
+ * lists the cards, grouped by family, and a card drags from it onto the rows
1781
+ * that take one — by the pointer, or from the keyboard: Space picks a card up,
1782
+ * the arrows carry it from row to row and along a row's buckets, Space drops
1783
+ * it and Escape cancels, and every step is said to a screen reader. The Plan
1784
+ * takes its own panel's cards with no `id` or `sources`.
1785
+ *
1786
+ * Roster, Board and Blend have ONE kind of cell, so "can you drop here" is a
1787
+ * question about the cell's contents. A Plan's rows are nine different things,
1788
+ * so the question is answered at three levels:
1789
+ *
1790
+ * 1. **Structurally, by series.** A card lands only on a row whose series
1791
+ * declares `edit` — WHERE it lands (`items`, the entry's list the row's
1792
+ * elements come from) and HOW it becomes one (`create`, from the drop: the
1793
+ * card, the row and the bucket's instant). Only the kinds holding discrete
1794
+ * scheduled objects can — `span` (runs), `buckets` (tiles), `events`
1795
+ * (marks), `cards` (chips). A `chart` / `heat` / `table` row renders
1796
+ * DERIVED values, and section headers and group strips are wayfinding:
1797
+ * they register no cell and never light up during a drag.
1798
+ * 2. **By policy, with `canDrop`.** Of the rows that can receive, this canvas
1799
+ * admits only the matching FAMILY: a job goes on a press, a delivery on
1800
+ * a van, a shift on a crew, a milestone on a stream. The `PALLET` card
1801
+ * is of a family no row takes and is therefore refused everywhere — the ⊘
1802
+ * stage on every row, which is what a card with nowhere to go should look
1803
+ * like.
1804
+ * 3. **As a draft.** A drop is a gesture of the editing session: the entry is
1805
+ * drafted with the new item in its list and its rows derived again —
1806
+ * drawn at once with the pending mark, undone with ⌘Z — and Save writes
1807
+ * every draft as ONE checked batch, one commit through the record's patch
1808
+ * door (`Record.onApply`).
1809
+ *
1810
+ * A press's run moves along its press or onto another press and resizes by
1811
+ * either end: its series names the job's key and the instant fields a move
1812
+ * writes (`key`, `start`, `end`). It moves in whole weeks, or in days with
1813
+ * Shift held; from the keyboard, Space on a focused run picks it up: ←/→ move
1814
+ * it a week, Shift+←/→ its end, Alt+←/→ its start, ↑/↓ carry it to another
1815
+ * press, and Space drops it.
1816
+ *
1817
+ * Hall 3 is an entry of its own, holding its presses (`Plan.children`): its
1818
+ * row rolls their runs up and takes no drop, and every gesture on one of its
1819
+ * presses lands on the press row, one level down, yet drafts the HALL — the
1820
+ * source's top-level entry, which the whole subtree rides in. A job moved
1821
+ * between a Hall 1 press and a Hall 3 one leaves one entry and joins the
1822
+ * other, as one gesture over both.
1823
+ *
1824
+ * `ready` is the author's check over a drafted entry: a press holding more
1825
+ * than four jobs — an entry of its own, or one a hall holds — is refused, by
1826
+ * name, and Save waits until it is fixed. H3-P11 already holds four, so a job
1827
+ * dropped or moved onto it holds Save until one leaves. `onPatch` hears each
1828
+ * gesture as it is made, and the footer says the last one.
1829
+ *
1830
+ * The Plan is bounded shorter than its rows, so the Hall 3 presses start below
1831
+ * the fold: a card held at the canvas's bottom edge scrolls them there (#608).
1832
+ */
1833
+ export const planRowDrop = example({
1834
+ keywords: [
1835
+ "Plan", "library", "panel", "Plan.library.tab", "palette", "DnD", "drag", "drop", "canDrop",
1836
+ "edit", "items", "create", "add", "target", "surface", "cell", "slot", "row kind", "selective", "veto",
1837
+ "invalid", "span", "buckets", "events", "cards", "chart", "heat", "table", "section", "row id", "row text",
1838
+ "droppable", "inert", "bucket instant", "editing", "session", "onApply", "onPatch", "ready",
1839
+ "Readiness", "Editing.Types.Readiness", "draft", "drafts", "transaction", "Save", "undo", "redo",
1840
+ "discard", "history", "pending", "Plan.Types.PatchEvent", "Reactive", "State", "re-derive", "#880",
1841
+ "move", "resize", "run", "key", "start", "end", "Shift", "snap", "cross-row", "Plan.Types.Move", "#825",
1842
+ "nested", "children", "Plan.children", "top-level entry", "rollup",
1843
+ "auto-scroll", "keyboard", "Space", "screen reader", "announcements", "#608", "footer", "#1259",
1844
+ "Record", "Record.bind", "Record.onApply", "e3.record", "patch", "commit", "Data.bindPaged", "Data.bind",
1845
+ ],
1846
+ description: "The Plan's editing session over an e3 record — cards dragged from the Plan's library panel land only on a series that declares `edit`, `canDrop` admits only the matching family, runs move and resize, a gesture on a hall's press drafts the hall, `ready` refuses a crowded press, and every gesture is a draft saved as one checked batch, one commit through the record's patch door",
1847
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
1848
+ // The source is a RECORD: the canvas pages it, and each Save is
1849
+ // one commit through its patch door.
1850
+ const ops = $.let(Data.bindPaged(planDropOps));
1851
+ const record = $.let(Record.bind(planDropOps, [planDropOpsPatch]));
1852
+ // The palette the cards come from, by key.
1853
+ const cards = $.let(Data.bind(planDropCards));
1854
+ const palette = $.let(cards.read());
1855
+ // Monday of ISO week n, 2026 — window W27–W38 (half-open), now W31.
1856
+ const week = $.const(East.function([IntegerType], DateTimeType, ($, n) => {
1857
+ const w1 = $.const(new Date("2025-12-29T00:00:00Z"), DateTimeType);
1858
+ return w1.addWeeks(n.subtract(1n));
1859
+ }));
1860
+ const MeasureRow = StructType({ week: DateTimeType, pct: FloatType });
1861
+ // A row's fortnightly readings from W27, as a chart's points and as
1862
+ // heat cells printing their values.
1863
+ const points = $.const(East.function([ArrayType(FloatType)], ArrayType(MeasureRow), ($, readings) => East.Array.generate(readings.size(), MeasureRow, (_$, i) => ({ week: week(i.multiply(2n).add(27n)), pct: readings.get(i) }))));
1864
+ const cells = $.const(East.function([ArrayType(FloatType)], ArrayType(Plan.Types.HeatCell), ($, readings) => East.Array.generate(readings.size(), Plan.Types.HeatCell, (_$, i) => ({
1865
+ at: Plan.at.time(week(i.multiply(2n).add(27n))),
1866
+ value: some(readings.get(i)),
1867
+ label: some(East.Float.printFixed(readings.get(i), 0n)),
1868
+ }))));
1869
+ // Everything a drop creates is a PROPOSAL — it is a suggestion the
1870
+ // host has not committed, and the lifecycle is how the canvas says so.
1871
+ const ADDED = variant("proposed", variant("added", null));
1872
+ // ── The policy table the host owns ───────────────────────────
1873
+ // Which FAMILY of card each row will take. A drop cell names its
1874
+ // row by the canonical TEXT of the row's id — the series that made
1875
+ // it and the path of keys to it — so the table is keyed by that
1876
+ // text. A row absent from this map takes nothing — which is how
1877
+ // the inert kinds would behave even if they did register a cell.
1878
+ const rowAccepts = $.const(new Map([
1879
+ [East.print(Plan.ref("press", "p03")), "job"],
1880
+ [East.print(Plan.ref("press", "p04")), "job"],
1881
+ [East.print(Plan.ref("gpress", "hall3", "H3-P11")), "job"],
1882
+ [East.print(Plan.ref("gpress", "hall3", "H3-P12")), "job"],
1883
+ [East.print(Plan.ref("van", "van1")), "delivery"],
1884
+ [East.print(Plan.ref("crew", "crewA")), "shift"],
1885
+ [East.print(Plan.ref("strm", "ms")), "milestone"],
1886
+ ]), DictType(StringType, StringType));
1887
+ // ── The drop veto ────────────────────────────────────────────
1888
+ // Consulted with the candidate event the pointer's CURRENT bucket
1889
+ // would produce, so the ⊘ appears while dragging rather than after,
1890
+ // and once more before the drop becomes a draft. A card is vetted
1891
+ // by its family; a run moved or resized is the canvas's own, which
1892
+ // lands only on a row of its item type, so the veto lets it through.
1893
+ const canDrop = $.const(East.function([DragEventType], BooleanType, ($, event) => {
1894
+ const yes = $.const(true, BooleanType);
1895
+ return event.match({
1896
+ add: ($, add) => {
1897
+ const row = $.let(add.into.row);
1898
+ const card = $.let(add.from.key);
1899
+ return rowAccepts.has(row)
1900
+ .and(_$ => palette.has(card))
1901
+ .and(_$ => rowAccepts.get(row).equal(palette.get(card).family));
1902
+ },
1903
+ }, _$ => yes);
1904
+ }));
1905
+ // ── The session ──────────────────────────────────────────────
1906
+ // Every gesture is a DRAFT of the entry it touched — a hall's
1907
+ // press drafts the hall — drawn at once, marked pending, and the
1908
+ // history item undoes, redoes, discards and saves it. `ready`
1909
+ // checks each drafted entry: a press holding more than four jobs
1910
+ // — the entry itself, or one of a hall's presses — is refused, by
1911
+ // name.
1912
+ const ready = $.const(East.function([DropOpsRow, StringType], Editing.Types.Readiness, ($, row, _key) => {
1913
+ const crowded = $.let(row.presses.filter((_$, p) => p.jobs.size().greater(4n)).toArray((_$, p, name) => ({
1914
+ field: "jobs", message: East.str `${name} holds ${East.print(p.jobs.size())} jobs — at most 4`,
1915
+ })));
1916
+ $.if(row.jobs.size().greater(4n), ($) => {
1917
+ $(crowded.pushLast({
1918
+ field: "jobs", message: East.str `${row.label} holds ${East.print(row.jobs.size())} jobs — at most 4`,
1919
+ }));
1920
+ });
1921
+ $.if(crowded.size().greater(0n), ($) => {
1922
+ $.return(East.value(variant("invalid", crowded), Editing.Types.Readiness));
1923
+ });
1924
+ return East.value(variant("ready", null), Editing.Types.Readiness);
1925
+ }));
1926
+ // Every gesture, as it is made — a drop, a move, an undo — into a
1927
+ // log the viewer keeps, which the footer says.
1928
+ const lastBind = $.let(State.bind([StringType], "ex.plan.lastdrop", "none yet"));
1929
+ const onPatch = $.const(East.function([Plan.Types.PatchEvent(DropOpsRow)], NullType, ($, event) => {
1930
+ $(lastBind.write(East.str `${event.origin.getTag()} · ${event.label}`));
1931
+ }));
1932
+ const last = $.let(lastBind.read());
1933
+ const axis = $.const(Plan.axis({
1934
+ window: { min: week(27n), max: week(39n) }, resolution: "week", now: week(31n),
1935
+ }));
1936
+ return (_jsx(Plan, { axis: axis, data: ops,
1937
+ // The palette — the Plan's own library panel, its cards
1938
+ // grouped by family. A card drags onto the rows that take
1939
+ // one; `canDrop` is the policy. A drop becomes a draft of
1940
+ // the session — without `editing` no card lands, since
1941
+ // nothing could hold it.
1942
+ library: [Plan.library.tab(palette, {
1943
+ name: "Palette", icon: "palette",
1944
+ label: c => c.name, meta: c => some(c.note), group: c => c.family,
1945
+ })], canDrop: canDrop, editing: { onApply: Record.onApply(record, { keyed: true }), onPatch, ready }, series: [
1946
+ // INERT — a chart plots a derived series, so there
1947
+ // is nothing a card could become here.
1948
+ Plan.series.chart(DropOpsRow, {
1949
+ key: "util", title: "Utilisation",
1950
+ match: r => r.series.equal("util"),
1951
+ label: r => r.label, id: true, height: "spark",
1952
+ layers: r => [Chart.Line(points(r.readings), { x: p => p.week, y: p => p.pct })],
1953
+ }),
1954
+ // RECEIVES and MOVES — runs are discrete scheduled
1955
+ // objects. A dropped job joins the press's `jobs`, a
1956
+ // fortnight long from the bucket the pointer named;
1957
+ // a run moves and resizes — the job found by `key`,
1958
+ // the run's own key, and a move writing `start` and
1959
+ // `end`. Keys stay unique on a press: a drop or a move
1960
+ // that would repeat one is refused, so a new job's key
1961
+ // names its card and place.
1962
+ Plan.series.span(DropOpsRow, {
1963
+ key: "press", title: "Press jobs",
1964
+ match: r => r.series.equal("press"),
1965
+ label: r => r.label, id: true,
1966
+ runs: r => r.jobs.map((_$, j) => Plan.run({
1967
+ key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
1968
+ })),
1969
+ edit: {
1970
+ items: "jobs",
1971
+ key: "key", start: "start", end: "end",
1972
+ create: (drop, r) => ({
1973
+ key: East.str `drop-${drop.from.key}-${East.print(r.jobs.size())}`,
1974
+ label: palette.get(drop.from.key).name,
1975
+ start: drop.at.unwrap("time"), end: drop.at.unwrap("time").addWeeks(2n),
1976
+ state: ADDED,
1977
+ }),
1978
+ },
1979
+ }),
1980
+ // INERT — an intensity field has no object to add to.
1981
+ Plan.series.heat(DropOpsRow, {
1982
+ key: "load", title: "Hall load",
1983
+ match: r => r.series.equal("load"),
1984
+ label: r => r.label,
1985
+ cells: r => Plan.heatCells(cells(r.readings), { min: 0, max: 100 }),
1986
+ }),
1987
+ // RECEIVES — a dropped delivery becomes a tile in
1988
+ // the bucket under the pointer.
1989
+ Plan.series.buckets(DropOpsRow, {
1990
+ key: "van", title: "Van drops",
1991
+ match: r => r.series.equal("van"),
1992
+ label: r => r.label,
1993
+ events: r => r.allocs.map((_$, a) => Plan.event({ key: a.key, at: a.at, state: a.state })),
1994
+ edit: {
1995
+ items: "allocs",
1996
+ create: (drop, r) => ({
1997
+ key: East.str `drop-${drop.from.key}-${East.print(r.allocs.size())}`,
1998
+ at: drop.at.unwrap("time"), state: ADDED,
1999
+ }),
2000
+ },
2001
+ }),
2002
+ // INERT — the cells are computed numbers.
2003
+ Plan.series.table(DropOpsRow, {
2004
+ key: "table", title: "Delivered sheets",
2005
+ match: r => r.series.equal("table"),
2006
+ label: r => r.label,
2007
+ cells: r => r.nums,
2008
+ format: Format.Number({ maximumFractionDigits: 0n }),
2009
+ }),
2010
+ // RECEIVES — a dropped shift becomes a chip.
2011
+ Plan.series.cards(DropOpsRow, {
2012
+ key: "crew", title: "Crew shifts",
2013
+ match: r => r.series.equal("crew"),
2014
+ label: r => r.label,
2015
+ chips: r => r.shifts.map((_$, s) => Plan.chip({
2016
+ key: s.key, from: s.from, to: s.to, label: s.label, state: s.state,
2017
+ })),
2018
+ edit: {
2019
+ items: "shifts",
2020
+ create: (drop, r) => ({
2021
+ key: East.str `drop-${drop.from.key}-${East.print(r.shifts.size())}`,
2022
+ from: drop.at.unwrap("time"), to: drop.at.unwrap("time").addWeeks(2n),
2023
+ label: palette.get(drop.from.key).name, state: ADDED,
2024
+ }),
2025
+ },
2026
+ }),
2027
+ // RECEIVES — a dropped milestone becomes a mark at
2028
+ // the instant, the one kind with no duration.
2029
+ Plan.series.events(DropOpsRow, {
2030
+ key: "strm", title: "Milestones",
2031
+ match: r => r.series.equal("strm"),
2032
+ label: r => r.label, id: true,
2033
+ marks: r => r.marks,
2034
+ edit: {
2035
+ items: "marks",
2036
+ create: (drop, r) => ({
2037
+ key: East.str `drop-${drop.from.key}-${East.print(r.marks.size())}`,
2038
+ at: drop.at, kind: variant("milestone", null), icon: none,
2039
+ label: some(palette.get(drop.from.key).name),
2040
+ }),
2041
+ },
2042
+ }),
2043
+ // A SECTION's header is inert. Under it, a HALL: an
2044
+ // entry holding its presses, stepped down into
2045
+ // through a plain field — which is what lets a
2046
+ // gesture on a press write back into its hall. The
2047
+ // hall's row rolls their runs up and takes no drop; a
2048
+ // press of it receives, and its runs move — onto the
2049
+ // Hall 1 presses too.
2050
+ Plan.series.section(DropOpsRow, { key: "hall-block", title: "Halls", meta: "nested" }, [
2051
+ Plan.series.span(DropOpsRow, {
2052
+ key: "halls", title: "Halls",
2053
+ match: r => r.series.equal("hall"),
2054
+ label: r => r.label,
2055
+ runs: _r => [], rollup: "union",
2056
+ children: Plan.children(r => r.presses, [
2057
+ Plan.series.span(DropPress, {
2058
+ key: "gpress", title: "Hall presses",
2059
+ label: (_p, k) => k, id: true,
2060
+ runs: p => p.jobs.map((_$, j) => Plan.run({
2061
+ key: j.key, start: j.start, end: j.end, label: j.label, state: j.state,
2062
+ })),
2063
+ edit: {
2064
+ items: "jobs",
2065
+ key: "key", start: "start", end: "end",
2066
+ create: (drop, p) => ({
2067
+ key: East.str `drop-${drop.from.key}-${East.print(p.jobs.size())}`,
2068
+ label: palette.get(drop.from.key).name,
2069
+ start: drop.at.unwrap("time"), end: drop.at.unwrap("time").addWeeks(2n),
2070
+ state: ADDED,
2071
+ }),
2072
+ },
2073
+ }),
2074
+ ]),
2075
+ }),
2076
+ ]),
2077
+ ], footer: [{ text: East.str `LAST GESTURE · ${last}`, end: true }],
2078
+ // Shorter than its rows: the last ones start below the
2079
+ // fold, and a card held at the bottom edge scrolls there.
2080
+ style: { height: "360px" } }));
2081
+ } }))),
2082
+ inputs: [],
2083
+ });
2084
+ // ============================================================================
2085
+ // planNumberAxis — the `number` axis (#631): day 1..8 with AM/PM lanes
2086
+ // ============================================================================
2087
+ /** An order on the horizon — its day (an integer day index) and its hall. */
2088
+ export const NumberHorizonRow = StructType({ key: StringType, day: IntegerType, hall: StringType });
2089
+ /** How many orders the horizon holds; {@link planNumberHorizon} makes them. */
2090
+ export const planNumberHorizonCount = e3.input("plan_number_horizon_count", IntegerType, variant("value", 24n));
2091
+ /** The horizon's orders, generated from their count — two a day from day 1,
2092
+ * the halls in turn: twelve days behind an eight-day window. */
2093
+ export const generateNumberHorizon = East.function([IntegerType], ArrayType(NumberHorizonRow), (_$, count) => East.Array.generate(count, NumberHorizonRow, (_$2, i) => ({
2094
+ key: East.str `o${East.print(i.add(1n))}`,
2095
+ day: i.divide(2n).add(1n),
2096
+ hall: i.remainder(2n).equal(0n).ifElse(() => "Hall 1", () => "Hall 2"),
2097
+ })));
2098
+ /** The task that generates the horizon. */
2099
+ export const planNumberHorizon = e3.task("plan_number_horizon", [planNumberHorizonCount], generateNumberHorizon);
2100
+ /** A van drop — its day (a plain number), lane and lifecycle state. */
2101
+ export const NumberAlloc = StructType({ key: StringType, day: FloatType, lane: StringType, state: EventStateType });
2102
+ /** A job — its days. */
2103
+ export const NumberJob = StructType({ key: StringType, label: StringType, start: FloatType, end: FloatType, state: EventStateType });
2104
+ /** A crew shift — its days and hours. */
2105
+ export const NumberShift = StructType({ key: StringType, from: FloatType, to: FloatType, hours: FloatType, state: EventStateType });
2106
+ /** A milestone — its day. */
2107
+ export const NumberMark = StructType({ key: StringType, day: FloatType, label: StringType });
2108
+ /** A chart point — a day and a value. */
2109
+ export const NumberPoint = StructType({ day: FloatType, sheets: FloatType });
2110
+ /** A table reading — a day and its value. */
2111
+ export const NumberReading = StructType({ at: FloatType, value: OptionType(FloatType) });
2112
+ /** RAW rows — every instant is a plain number (a day index). */
2113
+ export const NumberOpsRow = StructType({
2114
+ series: StringType, label: StringType, value: OptionType(StringType), sub: OptionType(StringType),
2115
+ allocations: ArrayType(NumberAlloc), jobs: ArrayType(NumberJob), shifts: ArrayType(NumberShift),
2116
+ marks: ArrayType(NumberMark), points: ArrayType(NumberPoint), sheets: ArrayType(NumberReading),
2117
+ cells: ArrayType(Plan.Types.HeatCell),
2118
+ });
2119
+ /** The rows — the keys are the entries' identities; the series list is the layout. */
2120
+ export const planNumberOps = e3.input("plan_number_ops", DictType(StringType, NumberOpsRow), variant("value", new Map([
2121
+ ["van1", { series: "van", label: "Van 1", value: none, sub: some("drops/day"),
2122
+ jobs: [], shifts: [], marks: [], points: [], sheets: [], cells: [],
2123
+ allocations: [
2124
+ { key: "a1", day: 1.0, lane: "am", state: variant("confirmed", null) },
2125
+ { key: "a2", day: 1.0, lane: "pm", state: variant("confirmed", null) },
2126
+ { key: "a3", day: 2.0, lane: "am", state: variant("confirmed", null) },
2127
+ { key: "a4", day: 3.0, lane: "pm", state: variant("proposed", variant("recommended", null)) },
2128
+ { key: "a5", day: 5.0, lane: "am", state: variant("proposed", variant("recommended", null)) },
2129
+ { key: "a6", day: 6.0, lane: "pm", state: variant("confirmed", null) },
2130
+ { key: "a7", day: 8.0, lane: "am", state: variant("proposed", variant("recommended", null)) },
2131
+ ] }],
2132
+ ["van2", { series: "van", label: "Van 2", value: none, sub: some("drops/day"),
2133
+ jobs: [], shifts: [], marks: [], points: [], sheets: [], cells: [],
2134
+ allocations: [
2135
+ { key: "b1", day: 2.0, lane: "pm", state: variant("confirmed", null) },
2136
+ { key: "b2", day: 4.0, lane: "am", state: variant("confirmed", null) },
2137
+ { key: "b3", day: 4.0, lane: "pm", state: variant("proposed", variant("recommended", null)) },
2138
+ { key: "b4", day: 7.0, lane: "am", state: variant("proposed", variant("recommended", null)) },
2139
+ ] }],
2140
+ ["p03", { series: "press", label: "H1-P03", value: some("12k/h"), sub: none,
2141
+ allocations: [], shifts: [], marks: [], points: [], sheets: [], cells: [],
2142
+ jobs: [
2143
+ { key: "mr", label: "MAKEREADY", start: 1.0, end: 2.0, state: variant("actual", null) },
2144
+ { key: "j4642", label: "RUN · J-4642", start: 2.0, end: 5.0, state: variant("in-progress", null) },
2145
+ { key: "j4663", label: "RUN · J-4663", start: 6.0, end: 8.0, state: variant("proposed", variant("recommended", null)) },
2146
+ ] }],
2147
+ ["p04", { series: "press", label: "H1-P04", value: some("8k/h"), sub: none,
2148
+ allocations: [], shifts: [], marks: [], points: [], sheets: [], cells: [],
2149
+ jobs: [
2150
+ { key: "j4624", label: "RUN · J-4624", start: 1.0, end: 4.0, state: variant("actual", null) },
2151
+ { key: "proof", label: "PROOF", start: 4.0, end: 5.0, state: variant("confirmed", null) },
2152
+ { key: "j4693", label: "RUN · J-4693", start: 5.0, end: 9.0, state: variant("proposed", variant("recommended", null)) },
2153
+ ] }],
2154
+ // Heat cells as STORED records — the instant spelled explicitly.
2155
+ ["load", { series: "load", label: "Hall load", value: none, sub: none,
2156
+ allocations: [], jobs: [], shifts: [], marks: [], points: [], sheets: [],
2157
+ cells: [
2158
+ { at: variant("number", 1.0), value: some(40.0), label: some("40") },
2159
+ { at: variant("number", 2.0), value: some(69.0), label: some("69") },
2160
+ { at: variant("number", 3.0), value: some(43.0), label: some("43") },
2161
+ { at: variant("number", 4.0), value: some(72.0), label: some("72") },
2162
+ { at: variant("number", 5.0), value: some(46.0), label: some("46") },
2163
+ { at: variant("number", 6.0), value: some(75.0), label: some("75") },
2164
+ { at: variant("number", 7.0), value: some(49.0), label: some("49") },
2165
+ { at: variant("number", 8.0), value: some(78.0), label: some("78") },
2166
+ ] }],
2167
+ ["out", { series: "out", label: "Printed · k", value: some("612 k sheets"), sub: none,
2168
+ allocations: [], jobs: [], shifts: [], marks: [], sheets: [], cells: [],
2169
+ points: [
2170
+ { day: 1.0, sheets: 60.0 }, { day: 2.0, sheets: 77.0 }, { day: 3.0, sheets: 94.0 }, { day: 4.0, sheets: 71.0 },
2171
+ { day: 5.0, sheets: 88.0 }, { day: 6.0, sheets: 65.0 }, { day: 7.0, sheets: 82.0 }, { day: 8.0, sheets: 99.0 },
2172
+ ] }],
2173
+ ["desp", { series: "table", label: "Delivered · k", value: none, sub: none,
2174
+ allocations: [], jobs: [], shifts: [], marks: [], points: [], cells: [],
2175
+ sheets: [
2176
+ { at: 1.0, value: some(80.0) }, { at: 2.0, value: some(103.0) }, { at: 3.0, value: some(126.0) }, { at: 4.0, value: some(149.0) },
2177
+ { at: 5.0, value: some(102.0) }, { at: 6.0, value: some(125.0) }, { at: 7.0, value: some(148.0) }, { at: 8.0, value: some(101.0) },
2178
+ ] }],
2179
+ ["crewA", { series: "crew", label: "Crew A", value: none, sub: none,
2180
+ allocations: [], jobs: [], marks: [], points: [], sheets: [], cells: [],
2181
+ shifts: [
2182
+ { key: "s1", from: 1.0, to: 3.0, hours: 24.0, state: variant("confirmed", null) },
2183
+ { key: "s2", from: 3.0, to: 6.0, hours: 36.0, state: variant("confirmed", null) },
2184
+ { key: "s3", from: 6.0, to: 8.0, hours: 24.0, state: variant("proposed", variant("recommended", null)) },
2185
+ ] }],
2186
+ ["ms", { series: "ms", label: "Milestones", value: none, sub: none,
2187
+ allocations: [], jobs: [], shifts: [], points: [], sheets: [], cells: [],
2188
+ marks: [
2189
+ { key: "kick", day: 2.0, label: "KICKOFF" },
2190
+ { key: "rel", day: 6.0, label: "REL" },
2191
+ ] }],
2192
+ ])));
2193
+ /**
2194
+ * The `number` axis — the retired Planner's `plannerPoint` canvas on the
2195
+ * Plan: eight days at step 1, AM/PM lanes in the bucket rows, `now` at day
2196
+ * 5. Every kind positions on the one numeric scale, and every instant in the
2197
+ * raw data is a plain number: a `FloatType` field wraps to the `number` arm
2198
+ * through the element builders (`Plan.run({ start: j.start })`), a chart
2199
+ * layer's numeric x accessor lands its columns on the same arm, `Plan.tableCells`
2200
+ * reads a numeric `at`, and the heat cells stored as RECORDS spell it out with
2201
+ * the `number` arm. The slice's range field is an integer day, so the horizon
2202
+ * brush and the range chip ride the slice's `integer` arm exactly as they ride
2203
+ * `datetime` on a time axis — a closed range, so the eight days `[1, 9)` read
2204
+ * `1–8`; there is no resolution segment — `step` is the declaration.
2205
+ */
2206
+ export const planNumberAxis = example({
2207
+ keywords: [
2208
+ "Plan", "axis", "number", "numeric", "step", "Plan.axis.number", "Plan.at", "instant",
2209
+ "day", "AM", "PM", "lanes", "buckets", "Planner", "plannerPoint", "brush", "integer", "range", "inclusive",
2210
+ "format", "now", "typed axis", "#631", "raw", "Data.bind", "bound", "e3.input", "e3.task",
2211
+ ],
2212
+ description: "The number axis over rows bound from e3 — day 1..8 at step 1 with AM/PM lanes (the retired Planner's plannerPoint), every row kind on the numeric scale, the horizon brush over the slice's integer range, read inclusive",
2213
+ fn: East.function([], UIComponentType, (_$) => {
2214
+ return (_jsx(Reactive, { children: $ => {
2215
+ const ops = $.let(Data.bind(planNumberOps));
2216
+ // The slice's horizon — twelve days of orders behind an eight-day
2217
+ // window, which a task generates.
2218
+ const horizon = $.let(Data.bind(planNumberHorizon));
2219
+ const series = $.const([
2220
+ Plan.series.section(NumberOpsRow, { key: "vans-local", title: "Deliveries · Local", meta: "2 rows" }, [
2221
+ Plan.series.buckets(NumberOpsRow, {
2222
+ key: "van", title: "Vans",
2223
+ match: r => r.series.equal("van"),
2224
+ label: r => r.label, sub: r => r.sub,
2225
+ // The Planner's AM/PM lanes — sub-slots of each day column.
2226
+ lanes: _r => [Plan.lane({ key: "am", label: "AM" }), Plan.lane({ key: "pm", label: "PM" })],
2227
+ // `a.day` is a FloatType field — the builder wraps it to the number arm.
2228
+ events: r => r.allocations.map((_$, a) => Plan.event({ key: a.key, at: a.day, lane: a.lane, state: a.state })),
2229
+ }),
2230
+ ]),
2231
+ Plan.series.span(NumberOpsRow, {
2232
+ key: "press", title: "Presses",
2233
+ match: r => r.series.equal("press"),
2234
+ label: r => r.label, id: true, value: r => r.value,
2235
+ runs: r => r.jobs.map((_$, j) => Plan.run({ key: j.key, start: j.start, end: j.end, label: j.label, state: j.state })),
2236
+ }),
2237
+ Plan.series.heat(NumberOpsRow, {
2238
+ key: "load", title: "Hall load",
2239
+ match: r => r.series.equal("load"),
2240
+ label: r => r.label,
2241
+ cells: r => Plan.heatCells(r.cells, { min: 0, max: 100, warnAt: 90 }),
2242
+ }),
2243
+ Plan.series.chart(NumberOpsRow, {
2244
+ key: "out", title: "Output",
2245
+ match: r => r.series.equal("out"),
2246
+ label: r => r.label, id: true, value: r => r.value, height: "expanded",
2247
+ // A numeric x accessor lands the columns on the number arm.
2248
+ layers: r => [Chart.Column(r.points, { x: p => p.day, y: p => p.sheets })],
2249
+ }),
2250
+ Plan.series.table(NumberOpsRow, {
2251
+ key: "table", title: "Deliveries",
2252
+ match: r => r.series.equal("table"),
2253
+ label: r => r.label,
2254
+ // A `{ at: FloatType, value }` record wraps by its field type.
2255
+ cells: r => Plan.tableCells(r.sheets),
2256
+ format: Format.Number({ maximumFractionDigits: 0n }),
2257
+ }),
2258
+ Plan.series.cards(NumberOpsRow, {
2259
+ key: "crew", title: "Crews",
2260
+ match: r => r.series.equal("crew"),
2261
+ label: r => r.label,
2262
+ chips: r => r.shifts.map((_$, s) => Plan.chip({
2263
+ key: s.key, from: s.from, to: s.to,
2264
+ label: East.str `${East.Float.printFixed(s.hours, 0n)}h`, state: s.state,
2265
+ })),
2266
+ }),
2267
+ Plan.series.events(NumberOpsRow, {
2268
+ key: "ms", title: "Milestones",
2269
+ match: r => r.series.equal("ms"),
2270
+ label: r => r.label, id: true,
2271
+ marks: r => r.marks.map((_$, m) => Plan.mark({ key: m.key, at: m.day, kind: "milestone", label: m.label })),
2272
+ }),
2273
+ ], ArrayType(Plan.Types.Series(NumberOpsRow)));
2274
+ // The horizon's range field is an INTEGER day, so the brush, the
2275
+ // range chip and the window keys write the slice's `integer` arm:
2276
+ // a closed range, both ends inclusive — the eight days `[1, 9)`
2277
+ // are `1–8`.
2278
+ const slice = $.let(Slice.bind("ex.plan.number", horizon.read(), {
2279
+ fields: { day: { label: "Day" }, hall: { label: "Hall" } },
2280
+ range: "day", initial: { day: { from: 1n, to: 8n } },
2281
+ }));
2282
+ // The declaration: `[1, 9)` ÷ 1 = eight day columns, the divider at 5.
2283
+ const axis = $.const(Plan.axis.number({
2284
+ window: { min: 1, max: 9 }, step: 1, now: 5, format: Chart.format.number(),
2285
+ }));
2286
+ return (_jsx(Plan, { axis: axis, data: ops, series: series, slice: { slice, affordances: ["filter", "range", "brush", "summary"] }, footer: [{ text: "8 STEPS · NOW 5" }] }));
2287
+ } }));
2288
+ }),
2289
+ inputs: [],
2290
+ });
2291
+ // ============================================================================
2292
+ // planOrdinalAxis — the `ordinal` axis (#631): workflow phases
2293
+ // ============================================================================
2294
+ /** A job — the phases it spans, by name. */
2295
+ export const OrdinalJob = StructType({ key: StringType, label: StringType, start: StringType, end: StringType, state: EventStateType });
2296
+ /** A bindery slot — the phase it sits in. */
2297
+ export const OrdinalAlloc = StructType({ key: StringType, phase: StringType, state: EventStateType });
2298
+ /** A crew shift — the phases it covers. */
2299
+ export const OrdinalShift = StructType({ key: StringType, from: StringType, to: StringType, label: StringType, state: EventStateType });
2300
+ /** A gate — its phase, and whether it holds the work. */
2301
+ export const OrdinalMark = StructType({ key: StringType, phase: StringType, label: StringType, exception: BooleanType });
2302
+ /** A chart point — a phase and a count. */
2303
+ export const OrdinalPoint = StructType({ phase: StringType, n: FloatType });
2304
+ /** A table reading — a phase and its value. */
2305
+ export const OrdinalReading = StructType({ at: StringType, value: OptionType(FloatType) });
2306
+ /** RAW rows — every instant is a phase NAME. */
2307
+ export const OrdinalOrderRow = StructType({
2308
+ series: StringType, label: StringType, value: OptionType(StringType), sub: OptionType(StringType),
2309
+ jobs: ArrayType(OrdinalJob), allocations: ArrayType(OrdinalAlloc), shifts: ArrayType(OrdinalShift),
2310
+ marks: ArrayType(OrdinalMark), points: ArrayType(OrdinalPoint), counts: ArrayType(OrdinalReading),
2311
+ cells: ArrayType(Plan.Types.HeatCell),
2312
+ });
2313
+ /** The jobs, bindery slots, loads and gates of a six-phase workflow. */
2314
+ export const planOrdinalOrders = e3.input("plan_ordinal_orders", DictType(StringType, OrdinalOrderRow), variant("value", new Map([
2315
+ ["j6188", { series: "job", label: "J-6188", value: some("96 k sheets"), sub: none,
2316
+ allocations: [], shifts: [], marks: [], points: [], counts: [], cells: [],
2317
+ jobs: [
2318
+ { key: "plates", label: "PLATES", start: "PREPRESS", end: "PLATES", state: variant("actual", null) },
2319
+ { key: "print", label: "PRINT · J-4642", start: "PRINT", end: "FINISH", state: variant("in-progress", null) },
2320
+ { key: "deliver", label: "BIND + DELIVER", start: "BIND", end: "DELIVER", state: variant("proposed", variant("recommended", null)) },
2321
+ ] }],
2322
+ ["j6204", { series: "job", label: "J-6204", value: some("54 k sheets"), sub: none,
2323
+ allocations: [], shifts: [], marks: [], points: [], counts: [], cells: [],
2324
+ jobs: [
2325
+ { key: "prepress", label: "PREPRESS", start: "PREPRESS", end: "PREPRESS", state: variant("actual", null) },
2326
+ { key: "print", label: "PRINT · J-4663", start: "PLATES", end: "BIND", state: variant("proposed", variant("recommended", null)) },
2327
+ ] }],
2328
+ ["bindery", { series: "bindery", label: "Bindery 2", value: none, sub: some("slots"),
2329
+ jobs: [], shifts: [], marks: [], points: [], counts: [], cells: [],
2330
+ allocations: [
2331
+ { key: "a1", phase: "PLATES", state: variant("confirmed", null) }, { key: "a2", phase: "PRINT", state: variant("confirmed", null) },
2332
+ { key: "a3", phase: "PRINT", state: variant("proposed", variant("recommended", null)) }, { key: "a4", phase: "BIND", state: variant("proposed", variant("recommended", null)) },
2333
+ ] }],
2334
+ // Heat cells as STORED records — the phase spelled on the `ordinal` arm.
2335
+ ["load", { series: "load", label: "Phase load", value: none, sub: none,
2336
+ jobs: [], allocations: [], shifts: [], marks: [], points: [], counts: [],
2337
+ cells: [
2338
+ { at: variant("ordinal", "PREPRESS"), value: some(35.0), label: some("35") },
2339
+ { at: variant("ordinal", "PLATES"), value: some(66.0), label: some("66") },
2340
+ { at: variant("ordinal", "PRINT"), value: some(37.0), label: some("37") },
2341
+ { at: variant("ordinal", "FINISH"), value: some(68.0), label: some("68") },
2342
+ { at: variant("ordinal", "BIND"), value: some(39.0), label: some("39") },
2343
+ { at: variant("ordinal", "DELIVER"), value: some(70.0), label: some("70") },
2344
+ ] }],
2345
+ ["wip", { series: "wip", label: "WIP · jobs", value: some("31"), sub: none,
2346
+ jobs: [], allocations: [], shifts: [], marks: [], counts: [], cells: [],
2347
+ points: [
2348
+ { phase: "PREPRESS", n: 4.0 }, { phase: "PLATES", n: 11.0 }, { phase: "PRINT", n: 18.0 },
2349
+ { phase: "FINISH", n: 5.0 }, { phase: "BIND", n: 12.0 }, { phase: "DELIVER", n: 19.0 },
2350
+ ] }],
2351
+ ["count", { series: "count", label: "Jobs in phase", value: none, sub: none,
2352
+ jobs: [], allocations: [], shifts: [], marks: [], points: [], cells: [],
2353
+ counts: [
2354
+ { at: "PREPRESS", value: some(12.0) }, { at: "PLATES", value: some(23.0) }, { at: "PRINT", value: some(34.0) },
2355
+ { at: "FINISH", value: some(15.0) }, { at: "BIND", value: none }, { at: "DELIVER", value: some(37.0) },
2356
+ ] }],
2357
+ ["crew", { series: "crew", label: "Crew B", value: none, sub: none,
2358
+ jobs: [], allocations: [], marks: [], points: [], counts: [], cells: [],
2359
+ shifts: [
2360
+ { key: "s1", from: "PREPRESS", to: "PLATES", label: "prepress crew", state: variant("confirmed", null) },
2361
+ { key: "s2", from: "PRINT", to: "DELIVER", label: "+ bindery crew", state: variant("proposed", variant("recommended", null)) },
2362
+ ] }],
2363
+ ["gates", { series: "gates", label: "Gates", value: none, sub: none,
2364
+ jobs: [], allocations: [], shifts: [], points: [], counts: [], cells: [],
2365
+ marks: [
2366
+ { key: "g1", phase: "FINISH", label: "HOLD", exception: true },
2367
+ { key: "g2", phase: "DELIVER", label: "RELEASE", exception: false },
2368
+ ] }],
2369
+ ])));
2370
+ /**
2371
+ * The `ordinal` axis — a workflow of six phases, each a bucket, in declared
2372
+ * order. Instants are the phase VALUES: a run's `start` / `end` are phase
2373
+ * names (an interval covers `[start, end]` in phase order — on an ordinal
2374
+ * axis the end names the LAST bucket covered, since values are buckets, not
2375
+ * edges), a tile sits in a phase, a chart's string x accessor lands its
2376
+ * columns on the phase arm, `Plan.tableCells` reads a string `at`, and a
2377
+ * record written as data spells its phase on the `ordinal` arm. There is no
2378
+ * slice range for an ordinal axis — the list IS the window, so the horizon
2379
+ * brush does not mount and the window keys idle; `now` names a phase.
2380
+ */
2381
+ export const planOrdinalAxis = example({
2382
+ keywords: [
2383
+ "Plan", "axis", "ordinal", "phase", "phases", "workflow", "stage", "Plan.axis.ordinal",
2384
+ "Plan.at", "instant", "values", "list", "span", "buckets", "heat", "chart", "table",
2385
+ "cards", "events", "Planner", "typed axis", "#631", "raw", "Data.bind", "bound", "e3.input",
2386
+ ],
2387
+ description: "The ordinal axis over rows bound from e3 — six workflow phases as the buckets, in declared order; jobs span phases, tiles and cells sit in them, a string x accessor lands a chart on them",
2388
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
2389
+ const orders = $.let(Data.bind(planOrdinalOrders));
2390
+ const PHASES = $.const(["PREPRESS", "PLATES", "PRINT", "FINISH", "BIND", "DELIVER"], ArrayType(StringType));
2391
+ const EXCEPTION = $.const(variant("exception", null), Plan.Types.EventMarkKind);
2392
+ const MILESTONE = $.const(variant("milestone", null), Plan.Types.EventMarkKind);
2393
+ const series = $.const([
2394
+ Plan.series.section(OrdinalOrderRow, { key: "job-block", title: "Jobs", meta: "2 rows" }, [
2395
+ Plan.series.span(OrdinalOrderRow, {
2396
+ key: "job", title: "Jobs",
2397
+ match: r => r.series.equal("job"),
2398
+ label: r => r.label, id: true, value: r => r.value,
2399
+ // `j.start` / `j.end` are StringType fields — the builder wraps them to the ordinal arm.
2400
+ runs: r => r.jobs.map((_$, j) => Plan.run({ key: j.key, start: j.start, end: j.end, label: j.label, state: j.state })),
2401
+ }),
2402
+ ]),
2403
+ Plan.series.buckets(OrdinalOrderRow, {
2404
+ key: "bindery", title: "Bindery",
2405
+ match: r => r.series.equal("bindery"),
2406
+ label: r => r.label, sub: r => r.sub,
2407
+ events: r => r.allocations.map((_$, a) => Plan.event({ key: a.key, at: a.phase, state: a.state })),
2408
+ }),
2409
+ Plan.series.heat(OrdinalOrderRow, {
2410
+ key: "load", title: "Phase load",
2411
+ match: r => r.series.equal("load"),
2412
+ label: r => r.label,
2413
+ cells: r => Plan.heatCells(r.cells, { min: 0, max: 100, warnAt: 90 }),
2414
+ }),
2415
+ Plan.series.chart(OrdinalOrderRow, {
2416
+ key: "wip", title: "WIP",
2417
+ match: r => r.series.equal("wip"),
2418
+ label: r => r.label, id: true, value: r => r.value, height: "expanded",
2419
+ // A string x accessor lands the columns on the ordinal arm.
2420
+ layers: r => [Chart.Column(r.points, { x: p => p.phase, y: p => p.n })],
2421
+ }),
2422
+ Plan.series.table(OrdinalOrderRow, {
2423
+ key: "count", title: "Counts",
2424
+ match: r => r.series.equal("count"),
2425
+ label: r => r.label,
2426
+ cells: r => Plan.tableCells(r.counts),
2427
+ format: Format.Number({ maximumFractionDigits: 0n }),
2428
+ }),
2429
+ Plan.series.cards(OrdinalOrderRow, {
2430
+ key: "crew", title: "Crews",
2431
+ match: r => r.series.equal("crew"),
2432
+ label: r => r.label,
2433
+ chips: r => r.shifts.map((_$, s) => Plan.chip({ key: s.key, from: s.from, to: s.to, label: s.label, state: s.state })),
2434
+ }),
2435
+ Plan.series.events(OrdinalOrderRow, {
2436
+ key: "gates", title: "Gates",
2437
+ match: r => r.series.equal("gates"),
2438
+ label: r => r.label, id: true,
2439
+ marks: r => r.marks.map((_$, m) => Plan.mark({
2440
+ key: m.key, at: m.phase, label: m.label,
2441
+ kind: m.exception.ifElse(() => EXCEPTION, () => MILESTONE),
2442
+ })),
2443
+ }),
2444
+ ], ArrayType(Plan.Types.Series(OrdinalOrderRow)));
2445
+ // The declaration: the list IS the axis — one bucket per phase, `now` at PRINT.
2446
+ const axis = $.const(Plan.axis.ordinal({ values: PHASES, now: "PRINT" }));
2447
+ return (_jsx(Plan, { axis: axis, data: orders, series: series, footer: [{ text: "6 PHASES · NOW PRINT" }] }));
2448
+ } }))),
2449
+ inputs: [],
2450
+ });
2451
+ // ============================================================================
2452
+ // slicePlanChrome — a Plan with the `slice` chrome option
2453
+ // Exercises: the rail on the timeline canvas (filter · search · range
2454
+ // over the row type's datetime field) plus the horizon brush.
2455
+ // ============================================================================
2456
+ /** A task on the plan — its owner and its window. */
2457
+ export const SliceJob = StructType({ task: StringType, owner: StringType, start: DateTimeType, end: DateTimeType });
2458
+ /** The plan's tasks — the rows the slice narrows. */
2459
+ export const planSliceJobs = e3.input("plan_slice_jobs", ArrayType(SliceJob), variant("value", [
2460
+ { task: "Planning", owner: "Team A", start: new Date("2024-01-01T00:00:00Z"), end: new Date("2024-01-15T00:00:00Z") },
2461
+ { task: "Design", owner: "Team B", start: new Date("2024-01-10T00:00:00Z"), end: new Date("2024-02-01T00:00:00Z") },
2462
+ { task: "Development", owner: "Team C", start: new Date("2024-01-20T00:00:00Z"), end: new Date("2024-03-15T00:00:00Z") },
2463
+ { task: "Testing", owner: "Team A", start: new Date("2024-03-01T00:00:00Z"), end: new Date("2024-03-30T00:00:00Z") },
2464
+ ]));
2465
+ export const slicePlanChrome = example({
2466
+ keywords: ["Slice", "Plan", "slice", "chrome", "filter", "search", "range", "brush", "timeline", "Data.bind", "bound", "e3.input"],
2467
+ description: "Ops plan — Plan with the `slice` chrome option over tasks bound from e3: a header rail (`filter`, `search`, `range`) plus the `brush` affordance — drag a window on the horizon strip to set the slice's range; rows fed explicitly via `Slice.rows` and re-keyed into the canvas's keyed collection",
2468
+ fn: East.function([], UIComponentType, (_$) => {
2469
+ return (_jsx(Reactive, { children: $ => {
2470
+ const tasks = $.let(Data.bind(planSliceJobs));
2471
+ const slice = $.let(Slice.bind("ex.slice.plan.chrome", tasks.read(), {
2472
+ fields: { task: { label: "Task" }, owner: { label: "Owner" }, start: { label: "Start" } },
2473
+ search: ["task", "owner"], range: "start",
2474
+ initial: { owner: ["Team A"] },
2475
+ }));
2476
+ // The narrowed rows re-key into the canvas's keyed collection
2477
+ // (#568) — the task name is the row identity here.
2478
+ const narrowed = $.let(Slice.rows(slice));
2479
+ const jobs = $.let(narrowed.toDict((_$, j) => j.task, (_$, j) => j));
2480
+ const series = $.const([
2481
+ Plan.series.span(SliceJob, {
2482
+ key: "jobs", title: "Jobs",
2483
+ label: r => r.task, id: true,
2484
+ sub: r => some(r.owner),
2485
+ runs: (r, k) => [Plan.run({
2486
+ key: k, start: r.start, end: r.end,
2487
+ label: r.task, state: variant("confirmed", null),
2488
+ })],
2489
+ }),
2490
+ ], ArrayType(Plan.Types.Series(SliceJob)));
2491
+ const axis = $.const(Plan.axis({
2492
+ window: { min: new Date("2024-01-01"), max: new Date("2024-04-01") },
2493
+ resolution: "week", now: new Date("2024-01-20"),
2494
+ }));
2495
+ return (_jsx(Plan, { axis: axis, data: jobs, series: series, slice: { slice, affordances: ["filter", "search", "range", "brush"] } }));
2496
+ } }));
2497
+ }),
2498
+ inputs: [],
2499
+ });
2500
+ //# sourceMappingURL=plan.examples.js.map