@k2b/ui 0.49.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/dist/base.css +40 -2
  2. package/dist/browser/actions/ContextMenu.js +3 -0
  3. package/dist/browser/actions/ContextMenu.js.map +1 -1
  4. package/dist/browser/charts/core/area.js +190 -0
  5. package/dist/browser/charts/core/area.js.map +1 -0
  6. package/dist/browser/charts/core/calendar.js +186 -0
  7. package/dist/browser/charts/core/calendar.js.map +1 -0
  8. package/dist/browser/charts/core/chart-map-data.js +9 -0
  9. package/dist/browser/charts/core/chart-map-data.js.map +1 -0
  10. package/dist/browser/charts/core/index.js +12 -0
  11. package/dist/browser/charts/core/index.js.map +1 -0
  12. package/dist/browser/charts/core/inspection.js +9 -0
  13. package/dist/browser/charts/core/inspection.js.map +1 -0
  14. package/dist/browser/charts/core/registry.js +28 -0
  15. package/dist/browser/charts/core/registry.js.map +1 -0
  16. package/dist/browser/charts/core/render.js +103 -0
  17. package/dist/browser/charts/core/render.js.map +1 -0
  18. package/dist/browser/charts/core/snapshot.js +65 -0
  19. package/dist/browser/charts/core/snapshot.js.map +1 -0
  20. package/dist/browser/charts/core/svg.js +2542 -0
  21. package/dist/browser/charts/core/svg.js.map +1 -0
  22. package/dist/browser/charts/core/termination.test-fixture.js +162 -0
  23. package/dist/browser/charts/core/termination.test-fixture.js.map +1 -0
  24. package/dist/browser/charts/core/text.js +13 -0
  25. package/dist/browser/charts/core/text.js.map +1 -0
  26. package/dist/browser/charts/core/types.js +8 -0
  27. package/dist/browser/charts/core/types.js.map +1 -0
  28. package/dist/browser/charts.js +4 -2
  29. package/dist/browser/charts.js.map +1 -1
  30. package/dist/browser/content/BalanceSummary.js +106 -0
  31. package/dist/browser/content/BalanceSummary.js.map +1 -0
  32. package/dist/browser/content/Chart.js +134 -115
  33. package/dist/browser/content/Chart.js.map +1 -1
  34. package/dist/browser/content/ChartExplorer.js +366 -122
  35. package/dist/browser/content/ChartExplorer.js.map +1 -1
  36. package/dist/browser/content/ChartSnapshotView.js +16 -9
  37. package/dist/browser/content/ChartSnapshotView.js.map +1 -1
  38. package/dist/browser/content/DataTable.js +676 -293
  39. package/dist/browser/content/DataTable.js.map +1 -1
  40. package/dist/browser/content/DataTableTools.js +119 -0
  41. package/dist/browser/content/DataTableTools.js.map +1 -0
  42. package/dist/browser/content/FlowAmount.js +82 -0
  43. package/dist/browser/content/FlowAmount.js.map +1 -0
  44. package/dist/browser/content/balance-summary-messages.js +26 -0
  45. package/dist/browser/content/balance-summary-messages.js.map +1 -0
  46. package/dist/browser/content/chart-bar.js +6 -6
  47. package/dist/browser/content/chart-bar.js.map +1 -1
  48. package/dist/browser/content/chart-calendar.js +43 -0
  49. package/dist/browser/content/chart-calendar.js.map +1 -0
  50. package/dist/browser/content/chart-inspection-core.js +164 -37
  51. package/dist/browser/content/chart-inspection-core.js.map +1 -1
  52. package/dist/browser/content/chart-inspection.js.map +1 -1
  53. package/dist/browser/content/chart-line-gaps.js +2 -2
  54. package/dist/browser/content/chart-line-gaps.js.map +1 -1
  55. package/dist/browser/content/chart-map-viewport.js.map +1 -1
  56. package/dist/browser/content/chart-message-catalog.js +164 -0
  57. package/dist/browser/content/chart-message-catalog.js.map +1 -0
  58. package/dist/browser/content/chart-messages.js +2 -138
  59. package/dist/browser/content/chart-messages.js.map +1 -1
  60. package/dist/browser/content/chart-snapshot.js +1 -62
  61. package/dist/browser/content/chart-snapshot.js.map +1 -1
  62. package/dist/browser/content/chart-state-timeline.js +1 -1
  63. package/dist/browser/content/chart-state-timeline.js.map +1 -1
  64. package/dist/browser/content/chart-svg.js +2 -1
  65. package/dist/browser/content/chart-svg.js.map +1 -1
  66. package/dist/browser/content/chart-time-axis.js +139 -0
  67. package/dist/browser/content/chart-time-axis.js.map +1 -0
  68. package/dist/browser/content/data-table-columns.js +160 -0
  69. package/dist/browser/content/data-table-columns.js.map +1 -0
  70. package/dist/browser/content/data-table-messages.js +38 -2
  71. package/dist/browser/content/data-table-messages.js.map +1 -1
  72. package/dist/browser/content/flow-amount-messages.js +26 -0
  73. package/dist/browser/content/flow-amount-messages.js.map +1 -0
  74. package/dist/browser/content/index.js +4 -0
  75. package/dist/browser/content/index.js.map +1 -1
  76. package/dist/browser/inputs/DatePicker.js +5 -4
  77. package/dist/browser/inputs/DatePicker.js.map +1 -1
  78. package/dist/browser/inputs/SelectChip.js +50 -17
  79. package/dist/browser/inputs/SelectChip.js.map +1 -1
  80. package/dist/browser/inputs/date-picker.js +13 -0
  81. package/dist/browser/inputs/date-picker.js.map +1 -1
  82. package/dist/browser/intl/Format.js +31 -8
  83. package/dist/browser/intl/Format.js.map +1 -1
  84. package/dist/browser/layout/AppWorkspace.js +7 -1
  85. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  86. package/dist/browser/surfaces/BudgetList.js +281 -0
  87. package/dist/browser/surfaces/BudgetList.js.map +1 -0
  88. package/dist/browser/surfaces/ProgressBar.js +134 -21
  89. package/dist/browser/surfaces/ProgressBar.js.map +1 -1
  90. package/dist/browser/surfaces/StatCell.js +320 -144
  91. package/dist/browser/surfaces/StatCell.js.map +1 -1
  92. package/dist/browser/surfaces/StatGrid.js +75 -57
  93. package/dist/browser/surfaces/StatGrid.js.map +1 -1
  94. package/dist/browser/surfaces/budget-messages.js +64 -0
  95. package/dist/browser/surfaces/budget-messages.js.map +1 -0
  96. package/dist/browser/surfaces/delta-badge.js +65 -0
  97. package/dist/browser/surfaces/delta-badge.js.map +1 -0
  98. package/dist/browser/surfaces/index.js +2 -1
  99. package/dist/browser/surfaces/index.js.map +1 -1
  100. package/dist/browser/surfaces/person-color.js +2 -2
  101. package/dist/browser/surfaces/person-color.js.map +1 -1
  102. package/dist/browser/surfaces/stat-cell-messages.js +41 -0
  103. package/dist/browser/surfaces/stat-cell-messages.js.map +1 -0
  104. package/dist/browser/widgets/Dashboard.js +137 -0
  105. package/dist/browser/widgets/Dashboard.js.map +1 -0
  106. package/dist/browser/widgets/index.js +1 -0
  107. package/dist/browser/widgets/index.js.map +1 -1
  108. package/dist/ssr/actions/ContextMenu.js +3 -0
  109. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  110. package/dist/ssr/charts/core/area.js +190 -0
  111. package/dist/ssr/charts/core/area.js.map +1 -0
  112. package/dist/ssr/charts/core/calendar.js +186 -0
  113. package/dist/ssr/charts/core/calendar.js.map +1 -0
  114. package/dist/ssr/charts/core/chart-map-data.js +9 -0
  115. package/dist/ssr/charts/core/chart-map-data.js.map +1 -0
  116. package/dist/ssr/charts/core/index.js +12 -0
  117. package/dist/ssr/charts/core/index.js.map +1 -0
  118. package/dist/ssr/charts/core/inspection.js +9 -0
  119. package/dist/ssr/charts/core/inspection.js.map +1 -0
  120. package/dist/ssr/charts/core/registry.js +28 -0
  121. package/dist/ssr/charts/core/registry.js.map +1 -0
  122. package/dist/ssr/charts/core/render.js +103 -0
  123. package/dist/ssr/charts/core/render.js.map +1 -0
  124. package/dist/ssr/charts/core/snapshot.js +65 -0
  125. package/dist/ssr/charts/core/snapshot.js.map +1 -0
  126. package/dist/ssr/charts/core/svg.js +2542 -0
  127. package/dist/ssr/charts/core/svg.js.map +1 -0
  128. package/dist/ssr/charts/core/termination.test-fixture.js +162 -0
  129. package/dist/ssr/charts/core/termination.test-fixture.js.map +1 -0
  130. package/dist/ssr/charts/core/text.js +13 -0
  131. package/dist/ssr/charts/core/text.js.map +1 -0
  132. package/dist/ssr/charts/core/types.js +8 -0
  133. package/dist/ssr/charts/core/types.js.map +1 -0
  134. package/dist/ssr/charts.js +4 -2
  135. package/dist/ssr/charts.js.map +1 -1
  136. package/dist/ssr/content/BalanceSummary.js +75 -0
  137. package/dist/ssr/content/BalanceSummary.js.map +1 -0
  138. package/dist/ssr/content/Chart.js +90 -95
  139. package/dist/ssr/content/Chart.js.map +1 -1
  140. package/dist/ssr/content/ChartExplorer.js +190 -46
  141. package/dist/ssr/content/ChartExplorer.js.map +1 -1
  142. package/dist/ssr/content/ChartSnapshotView.js +5 -1
  143. package/dist/ssr/content/ChartSnapshotView.js.map +1 -1
  144. package/dist/ssr/content/DataTable.js +287 -78
  145. package/dist/ssr/content/DataTable.js.map +1 -1
  146. package/dist/ssr/content/DataTableTools.js +106 -0
  147. package/dist/ssr/content/DataTableTools.js.map +1 -0
  148. package/dist/ssr/content/FlowAmount.js +58 -0
  149. package/dist/ssr/content/FlowAmount.js.map +1 -0
  150. package/dist/ssr/content/balance-summary-messages.js +26 -0
  151. package/dist/ssr/content/balance-summary-messages.js.map +1 -0
  152. package/dist/ssr/content/chart-bar.js +6 -6
  153. package/dist/ssr/content/chart-bar.js.map +1 -1
  154. package/dist/ssr/content/chart-calendar.js +43 -0
  155. package/dist/ssr/content/chart-calendar.js.map +1 -0
  156. package/dist/ssr/content/chart-inspection-core.js +164 -37
  157. package/dist/ssr/content/chart-inspection-core.js.map +1 -1
  158. package/dist/ssr/content/chart-inspection.js.map +1 -1
  159. package/dist/ssr/content/chart-line-gaps.js +2 -2
  160. package/dist/ssr/content/chart-line-gaps.js.map +1 -1
  161. package/dist/ssr/content/chart-map-viewport.js.map +1 -1
  162. package/dist/ssr/content/chart-message-catalog.js +164 -0
  163. package/dist/ssr/content/chart-message-catalog.js.map +1 -0
  164. package/dist/ssr/content/chart-messages.js +2 -138
  165. package/dist/ssr/content/chart-messages.js.map +1 -1
  166. package/dist/ssr/content/chart-snapshot.js +1 -62
  167. package/dist/ssr/content/chart-snapshot.js.map +1 -1
  168. package/dist/ssr/content/chart-state-timeline.js +1 -1
  169. package/dist/ssr/content/chart-state-timeline.js.map +1 -1
  170. package/dist/ssr/content/chart-svg.js +2 -1
  171. package/dist/ssr/content/chart-svg.js.map +1 -1
  172. package/dist/ssr/content/chart-time-axis.js +139 -0
  173. package/dist/ssr/content/chart-time-axis.js.map +1 -0
  174. package/dist/ssr/content/data-table-columns.js +160 -0
  175. package/dist/ssr/content/data-table-columns.js.map +1 -0
  176. package/dist/ssr/content/data-table-messages.js +38 -2
  177. package/dist/ssr/content/data-table-messages.js.map +1 -1
  178. package/dist/ssr/content/flow-amount-messages.js +26 -0
  179. package/dist/ssr/content/flow-amount-messages.js.map +1 -0
  180. package/dist/ssr/content/index.js +4 -0
  181. package/dist/ssr/content/index.js.map +1 -1
  182. package/dist/ssr/inputs/DatePicker.js +5 -4
  183. package/dist/ssr/inputs/DatePicker.js.map +1 -1
  184. package/dist/ssr/inputs/SelectChip.js +26 -9
  185. package/dist/ssr/inputs/SelectChip.js.map +1 -1
  186. package/dist/ssr/inputs/date-picker.js +13 -0
  187. package/dist/ssr/inputs/date-picker.js.map +1 -1
  188. package/dist/ssr/intl/Format.js +22 -2
  189. package/dist/ssr/intl/Format.js.map +1 -1
  190. package/dist/ssr/layout/AppWorkspace.js +7 -1
  191. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  192. package/dist/ssr/surfaces/BudgetList.js +170 -0
  193. package/dist/ssr/surfaces/BudgetList.js.map +1 -0
  194. package/dist/ssr/surfaces/ProgressBar.js +96 -5
  195. package/dist/ssr/surfaces/ProgressBar.js.map +1 -1
  196. package/dist/ssr/surfaces/StatCell.js +157 -51
  197. package/dist/ssr/surfaces/StatCell.js.map +1 -1
  198. package/dist/ssr/surfaces/StatGrid.js +24 -35
  199. package/dist/ssr/surfaces/StatGrid.js.map +1 -1
  200. package/dist/ssr/surfaces/budget-messages.js +64 -0
  201. package/dist/ssr/surfaces/budget-messages.js.map +1 -0
  202. package/dist/ssr/surfaces/delta-badge.js +41 -0
  203. package/dist/ssr/surfaces/delta-badge.js.map +1 -0
  204. package/dist/ssr/surfaces/index.js +2 -1
  205. package/dist/ssr/surfaces/index.js.map +1 -1
  206. package/dist/ssr/surfaces/person-color.js +2 -2
  207. package/dist/ssr/surfaces/person-color.js.map +1 -1
  208. package/dist/ssr/surfaces/stat-cell-messages.js +41 -0
  209. package/dist/ssr/surfaces/stat-cell-messages.js.map +1 -0
  210. package/dist/ssr/widgets/Dashboard.js +84 -0
  211. package/dist/ssr/widgets/Dashboard.js.map +1 -0
  212. package/dist/ssr/widgets/index.js +1 -0
  213. package/dist/ssr/widgets/index.js.map +1 -1
  214. package/dist/styles.css +1 -1
  215. package/dist/types/charts/core/area.d.ts +62 -0
  216. package/dist/types/charts/core/calendar.d.ts +57 -0
  217. package/dist/types/charts/core/chart-map-data.d.ts +3 -0
  218. package/dist/types/charts/core/index.d.ts +11 -0
  219. package/dist/types/charts/core/inspection.d.ts +16 -0
  220. package/dist/types/charts/core/registry.d.ts +27 -0
  221. package/dist/types/charts/core/render.d.ts +40 -0
  222. package/dist/types/charts/core/snapshot.d.ts +27 -0
  223. package/dist/types/charts/core/svg.d.ts +714 -0
  224. package/dist/types/charts/core/termination.test-fixture.d.ts +1 -0
  225. package/dist/types/charts/core/text.d.ts +5 -0
  226. package/dist/types/charts/core/types.d.ts +31 -0
  227. package/dist/types/charts.d.ts +4 -2
  228. package/dist/types/content/BalanceSummary.d.ts +29 -0
  229. package/dist/types/content/Chart.d.ts +26 -36
  230. package/dist/types/content/ChartExplorer.d.ts +43 -2
  231. package/dist/types/content/ChartSnapshotView.d.ts +1 -0
  232. package/dist/types/content/DataTable.d.ts +28 -0
  233. package/dist/types/content/DataTableTools.d.ts +44 -0
  234. package/dist/types/content/FlowAmount.d.ts +28 -0
  235. package/dist/types/content/balance-summary-messages.d.ts +11 -0
  236. package/dist/types/content/chart-bar.d.ts +1 -1
  237. package/dist/types/content/chart-calendar.d.ts +4 -0
  238. package/dist/types/content/chart-inspection-core.d.ts +21 -16
  239. package/dist/types/content/chart-inspection.d.ts +5 -1
  240. package/dist/types/content/chart-map-viewport.d.ts +1 -1
  241. package/dist/types/content/chart-message-catalog.d.ts +84 -0
  242. package/dist/types/content/chart-messages.d.ts +2 -33
  243. package/dist/types/content/chart-snapshot.d.ts +1 -26
  244. package/dist/types/content/chart-svg.d.ts +1 -1
  245. package/dist/types/content/chart-time-axis.d.ts +26 -0
  246. package/dist/types/content/data-table-columns.d.ts +72 -0
  247. package/dist/types/content/data-table-messages.d.ts +18 -0
  248. package/dist/types/content/flow-amount-messages.d.ts +11 -0
  249. package/dist/types/content/index.d.ts +10 -2
  250. package/dist/types/inputs/SelectChip.d.ts +2 -0
  251. package/dist/types/inputs/date-picker.d.ts +6 -0
  252. package/dist/types/intl/Format.d.ts +5 -0
  253. package/dist/types/layout/AppWorkspace.d.ts +5 -0
  254. package/dist/types/surfaces/BudgetList.d.ts +42 -0
  255. package/dist/types/surfaces/ProgressBar.d.ts +38 -2
  256. package/dist/types/surfaces/StatCell.d.ts +35 -2
  257. package/dist/types/surfaces/StatGrid.d.ts +3 -1
  258. package/dist/types/surfaces/budget-messages.d.ts +30 -0
  259. package/dist/types/surfaces/delta-badge.d.ts +10 -0
  260. package/dist/types/surfaces/index.d.ts +4 -3
  261. package/dist/types/surfaces/person-color.d.ts +2 -2
  262. package/dist/types/surfaces/stat-cell-messages.d.ts +19 -0
  263. package/dist/types/widgets/Dashboard.d.ts +42 -0
  264. package/dist/types/widgets/index.d.ts +1 -0
  265. package/package.json +7 -1
@@ -0,0 +1,170 @@
1
+ import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
2
+ import { ssr as _$ssr } from "solid-js/web";
3
+ import { createComponent as _$createComponent } from "solid-js/web";
4
+ import { escape as _$escape } from "solid-js/web";
5
+ var _tmpl$ = ["<ul class=\"k2b-budget__list\">", "</ul>"],
6
+ _tmpl$2 = ["<li><span class=\"k2b-budget__swatch\" data-pattern=\"hatched\"></span>", "</li>"],
7
+ _tmpl$3 = ["<li><span class=\"k2b-budget__swatch\" data-pattern=\"over\"></span>", "</li>"],
8
+ _tmpl$4 = ["<ul class=\"k2b-budget__legend\" aria-hidden=\"true\"><li><span class=\"k2b-budget__swatch\" data-pattern=\"solid\"></span>", "</li>", "", "</ul>"],
9
+ _tmpl$5 = ["<div class=\"", "\">", "</div>"],
10
+ _tmpl$6 = ["<p class=\"k2b-budget__empty\">", "</p>"],
11
+ _tmpl$7 = ["<span class=\"k2b-budget__label\">", "</span>"],
12
+ _tmpl$8 = ["<span class=\"k2b-budget__amount\">", " <span>", "</span></span>"],
13
+ _tmpl$9 = ["<i", " aria-hidden=\"true\"></i>"],
14
+ _tmpl$0 = ["<span class=\"k2b-budget__hint\">", "", "</span>"],
15
+ _tmpl$1 = ["<li class=\"k2b-budget__row\"", ">", "</li>"],
16
+ _tmpl$10 = ["<span>", " <b>", "</b></span>"],
17
+ _tmpl$11 = ["<span class=\"k2b-budget__tip\"><b>", "</b><span>", " <b>", "</b></span>", "<span>", " <b>", "</b></span><span>", " <b>", "</b></span></span>"];
18
+ import { For, Show } from "solid-js";
19
+ import { Tooltip } from "../feedback/Tooltip";
20
+ import { useLocale } from "../intl/locale";
21
+ import { useBudgetMessages } from "./budget-messages";
22
+ import { budgetOverrun, budgetRatio, ProgressBar } from "./ProgressBar";
23
+
24
+ /** One budget: `used` is booked, `expected` comes on top; over the budget is `used + expected - limit`. */
25
+
26
+ /** "near" from 80 % of the budget, "over" beyond it. */
27
+
28
+ /** The figures of one budget row; the share counts booked and expected together. */
29
+ export const budgetState = item => {
30
+ const used = Number.isFinite(item.used) ? Math.max(0, item.used) : 0;
31
+ const expected = item.expected !== undefined && Number.isFinite(item.expected) ? Math.max(0, item.expected) : 0;
32
+ const limit = Number.isFinite(item.limit) ? Math.max(0, item.limit) : 0;
33
+ const total = used + expected;
34
+ const over = budgetOverrun(total, limit);
35
+ const ratio = budgetRatio(total, limit);
36
+ // The same float tolerance as the overrun: 0.7 + 0.1 of 1 is exactly 80 %.
37
+ const level = over > 0 ? "over" : ratio >= 0.8 - 1e-9 ? "near" : "ok";
38
+ return {
39
+ used,
40
+ expected,
41
+ limit,
42
+ total,
43
+ over,
44
+ free: over > 0 ? 0 : Math.max(0, limit - total),
45
+ ratio,
46
+ level
47
+ };
48
+ };
49
+
50
+ /**
51
+ * Budgets as rows: label and "spent of budget", a bar with booked (solid), expected (hatched) and over the budget
52
+ * (red, with a mark at the limit), and a hint that turns amber from 80 % and red beyond the budget.
53
+ */
54
+ export function BudgetList(props) {
55
+ const locale = useLocale();
56
+ const t = useBudgetMessages(() => props.locale);
57
+ const money = value => new Intl.NumberFormat(props.locale ?? locale(), {
58
+ style: "currency",
59
+ currency: props.currency,
60
+ minimumFractionDigits: props.decimals ?? 0,
61
+ maximumFractionDigits: props.decimals ?? 0
62
+ }).format(value);
63
+ const states = () => props.items.map(item => ({
64
+ item,
65
+ state: budgetState(item)
66
+ }));
67
+ const anyExpected = () => states().some(({
68
+ state
69
+ }) => state.expected > 0);
70
+ const anyOver = () => states().some(({
71
+ state
72
+ }) => state.over > 0);
73
+ return _$ssr(_tmpl$5, `k2b-budget ${_$escape(props.class ?? "", true)}`, _$escape(_$createComponent(Show, {
74
+ get when() {
75
+ return props.items.length;
76
+ },
77
+ get fallback() {
78
+ return _$ssr(_tmpl$6, _$escape(props.empty));
79
+ },
80
+ get children() {
81
+ return [_$ssr(_tmpl$, _$escape(_$createComponent(For, {
82
+ get each() {
83
+ return states();
84
+ },
85
+ children: ({
86
+ item,
87
+ state
88
+ }) => _$ssr(_tmpl$1, _$ssrAttribute("data-level", _$escape(state.level, true), false), _$escape(_$createComponent(Tooltip.Anchor, {
89
+ "class": "k2b-budget__anchor",
90
+ tabIndex: 0,
91
+ get content() {
92
+ return _$ssr(_tmpl$11, _$escape(item.label), _$escape(t().used), _$escape(money(state.used)), _$escape(_$createComponent(Show, {
93
+ get when() {
94
+ return state.expected > 0;
95
+ },
96
+ get children() {
97
+ return _$ssr(_tmpl$10, _$escape(t().expected), _$escape(money(state.expected)));
98
+ }
99
+ })), _$escape(t().budget), _$escape(money(state.limit)), state.over > 0 ? _$escape(t().overBy) : _$escape(t().left), _$escape(money(state.over > 0 ? state.over : state.free)));
100
+ },
101
+ get children() {
102
+ return [_$ssr(_tmpl$7, _$escape(item.label)), _$ssr(_tmpl$8, _$escape(money(state.total)), _$escape(t().of({
103
+ limit: money(state.limit)
104
+ }))), _$createComponent(ProgressBar, {
105
+ "class": "k2b-budget__bar",
106
+ size: "sm",
107
+ get label() {
108
+ return item.label;
109
+ },
110
+ get segments() {
111
+ return [{
112
+ value: state.used
113
+ }, {
114
+ value: state.expected,
115
+ pattern: "hatched"
116
+ }];
117
+ },
118
+ get limit() {
119
+ return state.limit;
120
+ },
121
+ get valueText() {
122
+ return t().valueText({
123
+ used: money(state.used),
124
+ expected: state.expected > 0 ? money(state.expected) : undefined,
125
+ limit: money(state.limit)
126
+ });
127
+ }
128
+ }), _$ssr(_tmpl$0, _$escape(_$createComponent(Show, {
129
+ get when() {
130
+ return state.level !== "ok";
131
+ },
132
+ get children() {
133
+ return _$ssr(_tmpl$9, _$ssrAttribute("class", state.level === "over" ? "ti ti-alert-circle" : "ti ti-alert-triangle", false));
134
+ }
135
+ })), state.level === "over" ? _$escape(t().over({
136
+ amount: money(state.over)
137
+ })) : state.level === "near" ? _$escape(t().near({
138
+ amount: money(state.free)
139
+ })) : _$escape(t().free({
140
+ amount: money(state.free)
141
+ })))];
142
+ }
143
+ })))
144
+ }))), _$createComponent(Show, {
145
+ get when() {
146
+ return props.legend !== false;
147
+ },
148
+ get children() {
149
+ return _$ssr(_tmpl$4, _$escape(t().used), _$escape(_$createComponent(Show, {
150
+ get when() {
151
+ return anyExpected();
152
+ },
153
+ get children() {
154
+ return _$ssr(_tmpl$2, _$escape(t().expected));
155
+ }
156
+ })), _$escape(_$createComponent(Show, {
157
+ get when() {
158
+ return anyOver();
159
+ },
160
+ get children() {
161
+ return _$ssr(_tmpl$3, _$escape(t().overLegend));
162
+ }
163
+ })));
164
+ }
165
+ })];
166
+ }
167
+ })));
168
+ }
169
+ export default BudgetList;
170
+ //# sourceMappingURL=BudgetList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["surfaces/BudgetList.tsx"],"sourcesContent":["import { For, type JSX, Show } from \"solid-js\";\nimport { Tooltip } from \"../feedback/Tooltip\";\nimport { useLocale } from \"../intl/locale\";\nimport { useBudgetMessages } from \"./budget-messages\";\nimport { budgetOverrun, budgetRatio, ProgressBar } from \"./ProgressBar\";\n\n/** One budget: `used` is booked, `expected` comes on top; over the budget is `used + expected - limit`. */\nexport type BudgetItem = { id?: string; label: string; used: number; expected?: number; limit: number };\n\nexport type BudgetListProps = {\n items: readonly BudgetItem[];\n /** ISO 4217 currency code, e.g. `\"EUR\"`. */\n currency: string;\n /** Explicit locale override; defaults to the inherited render locale. */\n locale?: string;\n /** Fixed fraction digits. Defaults to 0. */\n decimals?: number;\n /** The sentence shown when there is no budget. */\n empty?: string;\n /** Hides the legend under the rows when the host explains the colours elsewhere. */\n legend?: boolean;\n class?: string;\n};\n\n/** \"near\" from 80 % of the budget, \"over\" beyond it. */\nexport type BudgetLevel = \"ok\" | \"near\" | \"over\";\n\n/** The figures of one budget row; the share counts booked and expected together. */\nexport const budgetState = (item: Pick<BudgetItem, \"used\" | \"expected\" | \"limit\">) => {\n const used = Number.isFinite(item.used) ? Math.max(0, item.used) : 0;\n const expected = item.expected !== undefined && Number.isFinite(item.expected) ? Math.max(0, item.expected) : 0;\n const limit = Number.isFinite(item.limit) ? Math.max(0, item.limit) : 0;\n const total = used + expected;\n const over = budgetOverrun(total, limit);\n const ratio = budgetRatio(total, limit);\n // The same float tolerance as the overrun: 0.7 + 0.1 of 1 is exactly 80 %.\n const level: BudgetLevel = over > 0 ? \"over\" : ratio >= 0.8 - 1e-9 ? \"near\" : \"ok\";\n return { used, expected, limit, total, over, free: over > 0 ? 0 : Math.max(0, limit - total), ratio, level };\n};\n\n/**\n * Budgets as rows: label and \"spent of budget\", a bar with booked (solid), expected (hatched) and over the budget\n * (red, with a mark at the limit), and a hint that turns amber from 80 % and red beyond the budget.\n */\nexport function BudgetList(props: BudgetListProps): JSX.Element {\n const locale = useLocale();\n const t = useBudgetMessages(() => props.locale);\n const money = (value: number) =>\n new Intl.NumberFormat(props.locale ?? locale(), {\n style: \"currency\",\n currency: props.currency,\n minimumFractionDigits: props.decimals ?? 0,\n maximumFractionDigits: props.decimals ?? 0,\n }).format(value);\n const states = () => props.items.map((item) => ({ item, state: budgetState(item) }));\n const anyExpected = () => states().some(({ state }) => state.expected > 0);\n const anyOver = () => states().some(({ state }) => state.over > 0);\n return (\n <div class={`k2b-budget ${props.class ?? \"\"}`}>\n <Show when={props.items.length} fallback={<p class=\"k2b-budget__empty\">{props.empty}</p>}>\n <ul class=\"k2b-budget__list\">\n <For each={states()}>\n {({ item, state }) => (\n <li class=\"k2b-budget__row\" data-level={state.level}>\n <Tooltip.Anchor\n class=\"k2b-budget__anchor\"\n tabIndex={0}\n content={\n <span class=\"k2b-budget__tip\">\n <b>{item.label}</b>\n <span>\n {t().used} <b>{money(state.used)}</b>\n </span>\n <Show when={state.expected > 0}>\n <span>\n {t().expected} <b>{money(state.expected)}</b>\n </span>\n </Show>\n <span>\n {t().budget} <b>{money(state.limit)}</b>\n </span>\n <span>\n {state.over > 0 ? t().overBy : t().left} <b>{money(state.over > 0 ? state.over : state.free)}</b>\n </span>\n </span>\n }\n >\n <span class=\"k2b-budget__label\">{item.label}</span>\n <span class=\"k2b-budget__amount\">\n {money(state.total)} <span>{t().of({ limit: money(state.limit) })}</span>\n </span>\n <ProgressBar\n class=\"k2b-budget__bar\"\n size=\"sm\"\n label={item.label}\n segments={[{ value: state.used }, { value: state.expected, pattern: \"hatched\" }]}\n limit={state.limit}\n valueText={t().valueText({\n used: money(state.used),\n expected: state.expected > 0 ? money(state.expected) : undefined,\n limit: money(state.limit),\n })}\n />\n <span class=\"k2b-budget__hint\">\n <Show when={state.level !== \"ok\"}>\n <i class={state.level === \"over\" ? \"ti ti-alert-circle\" : \"ti ti-alert-triangle\"} aria-hidden=\"true\" />\n </Show>\n {state.level === \"over\"\n ? t().over({ amount: money(state.over) })\n : state.level === \"near\"\n ? t().near({ amount: money(state.free) })\n : t().free({ amount: money(state.free) })}\n </span>\n </Tooltip.Anchor>\n </li>\n )}\n </For>\n </ul>\n <Show when={props.legend !== false}>\n <ul class=\"k2b-budget__legend\" aria-hidden=\"true\">\n <li>\n <span class=\"k2b-budget__swatch\" data-pattern=\"solid\" />\n {t().used}\n </li>\n <Show when={anyExpected()}>\n <li>\n <span class=\"k2b-budget__swatch\" data-pattern=\"hatched\" />\n {t().expected}\n </li>\n </Show>\n <Show when={anyOver()}>\n <li>\n <span class=\"k2b-budget__swatch\" data-pattern=\"over\" />\n {t().overLegend}\n </li>\n </Show>\n </ul>\n </Show>\n </Show>\n </div>\n );\n}\n\nexport default BudgetList;\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,SAAS,GAAG,EAAY,IAAI,QAAQ,UAAU;AAC9C,SAAS,OAAO,QAAQ,qBAAqB;AAC7C,SAAS,SAAS,QAAQ,gBAAgB;AAC1C,SAAS,iBAAiB,QAAQ,mBAAmB;AACrD,SAAS,aAAa,EAAE,WAAW,EAAE,WAAW,QAAQ,eAAe;;AAEvE;;AAkBA;;AAGA;AACA,OAAO,MAAM,WAAW,GAAI,IAAqD,IAAK;EACpF,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;EACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;EAC/G,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;EACvE,MAAM,KAAK,GAAG,IAAI,GAAG,QAAQ;EAC7B,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC;EACxC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC;EACvC;EACA,MAAM,KAAkB,GAAG,IAAI,GAAG,CAAC,GAAG,MAAM,GAAG,KAAK,IAAI,GAAG,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;EAClF,OAAO;IAAE,IAAI;IAAE,QAAQ;IAAE,KAAK;IAAE,KAAK;IAAE,IAAI;IAAE,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAAE,KAAK;IAAE;EAAM,CAAC;AAC9G,CAAC;;AAED;AACA;AACA;AACA;AACA,OAAO,SAAS,UAAU,CAAC,KAAsB,EAAe;EAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;EAC1B,MAAM,CAAC,GAAG,iBAAiB,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC;EAC/C,MAAM,KAAK,GAAI,KAAa,IAC1B,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,EAAE;IAC9C,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ;IACxB,qBAAqB,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC;IAC1C,qBAAqB,EAAE,KAAK,CAAC,QAAQ,IAAI;EAC3C,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;EAClB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,CAAE,IAAI,KAAM;IAAE,IAAI;IAAE,KAAK,EAAE,WAAW,CAAC,IAAI;EAAE,CAAC,CAAC,CAAC;EACpF,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAAE;EAAM,CAAC,KAAK,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC;EAC1E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAAE;EAAM,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;EAClE,sBACc,uBAAc,KAAK,CAAC,KAAK,IAAI,EAAE,SAAE,6BAC1C,IAAI;IAAA,IAAC,IAAI;MAAA,OAAE,KAAK,CAAC,KAAK,CAAC,MAAM;IAAA;IAAA,IAAE,QAAQ;MAAA,+BAAgC,KAAK,CAAC,KAAK;IAAA;IAAA;MAAA,iDAE9E,GAAG;QAAA,IAAC,IAAI;UAAA,OAAE,MAAM,CAAC,CAAC;QAAA;QAAA,UAChB,CAAC;UAAE,IAAI;UAAE;QAAM,CAAC,0DACyB,KAAK,CAAC,KAAK,4CAChD,OAAO,CAAC,MAAM;UAAA;UAEb,QAAQ,EAAE,CAAC;UAAA,IACX,OAAO;YAAA,gCAEC,IAAI,CAAC,KAAK,YAEX,CAAC,CAAC,CAAC,CAAC,IAAI,YAAM,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,8BAEjC,IAAI;cAAA,IAAC,IAAI;gBAAA,OAAE,KAAK,CAAC,QAAQ,GAAG,CAAC;cAAA;cAAA;gBAAA,gCAEzB,CAAC,CAAC,CAAC,CAAC,QAAQ,YAAM,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC;cAAA;YAAA,cAIzC,CAAC,CAAC,CAAC,CAAC,MAAM,YAAM,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAGlC,KAAK,CAAC,IAAI,GAAG,CAAC,YAAG,CAAC,CAAC,CAAC,CAAC,MAAM,aAAG,CAAC,CAAC,CAAC,CAAC,IAAI,YAAM,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;UAAA;UAAA;YAAA,gCAKjE,IAAI,CAAC,KAAK,4BAExC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,YAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;cAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK;YAAE,CAAC,CAAC,sBAElE,WAAW;cAAA;cAEV,IAAI;cAAA,IACJ,KAAK;gBAAA,OAAE,IAAI,CAAC,KAAK;cAAA;cAAA,IACjB,QAAQ;gBAAA,OAAE,CAAC;kBAAE,KAAK,EAAE,KAAK,CAAC;gBAAK,CAAC,EAAE;kBAAE,KAAK,EAAE,KAAK,CAAC,QAAQ;kBAAE,OAAO,EAAE;gBAAU,CAAC,CAAC;cAAA;cAAA,IAChF,KAAK;gBAAA,OAAE,KAAK,CAAC,KAAK;cAAA;cAAA,IAClB,SAAS;gBAAA,OAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;kBACvB,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;kBACvB,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,SAAS;kBAChE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK;gBAC1B,CAAC,CAAC;cAAA;YAAA,8CAGD,IAAI;cAAA,IAAC,IAAI;gBAAA,OAAE,KAAK,CAAC,KAAK,KAAK,IAAI;cAAA;cAAA;gBAAA,8CACpB,KAAK,CAAC,KAAK,KAAK,MAAM,GAAG,oBAAoB,GAAG,sBAAsB;cAAA;YAAA,KAEjF,KAAK,CAAC,KAAK,KAAK,MAAM,YACnB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;cAAE,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;YAAE,CAAC,CAAC,IACvC,KAAK,CAAC,KAAK,KAAK,MAAM,YACpB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;cAAE,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;YAAE,CAAC,CAAC,aACvC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;cAAE,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;YAAE,CAAC,CAAC;UAAA;QAAA;MAIpD,wBAGJ,IAAI;QAAA,IAAC,IAAI;UAAA,OAAE,KAAK,CAAC,MAAM,KAAK,KAAK;QAAA;QAAA;UAAA,+BAI3B,CAAC,CAAC,CAAC,CAAC,IAAI,8BAEV,IAAI;YAAA,IAAC,IAAI;cAAA,OAAE,WAAW,CAAC,CAAC;YAAA;YAAA;cAAA,+BAGpB,CAAC,CAAC,CAAC,CAAC,QAAQ;YAAA;UAAA,gCAGhB,IAAI;YAAA,IAAC,IAAI;cAAA,OAAE,OAAO,CAAC,CAAC;YAAA;YAAA;cAAA,+BAGhB,CAAC,CAAC,CAAC,CAAC,UAAU;YAAA;UAAA;QAAA;MAAA;IAAA;EAAA;AAQ/B;AAEA,eAAe,UAAU","ignoreList":[]}
@@ -1,13 +1,104 @@
1
- import { ssr as _$ssr } from "solid-js/web";
2
1
  import { ssrStyle as _$ssrStyle } from "solid-js/web";
2
+ import { ssr as _$ssr } from "solid-js/web";
3
+ import { createComponent as _$createComponent } from "solid-js/web";
3
4
  import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
4
5
  import { escape as _$escape } from "solid-js/web";
5
- var _tmpl$ = ["<div class=\"", "\"", "><div class=\"k2b-progress__track\" role=\"progressbar\"", " aria-valuemin=\"0\" aria-valuemax=\"100\"", "><div class=\"k2b-progress__value\" style=\"", "\"></div></div>", "</div>"],
6
- _tmpl$2 = ["<span class=\"k2b-progress__percent\">", "%</span>"];
6
+ var _tmpl$ = ["<div class=\"", "\"", "><div class=\"k2b-progress__track\" role=\"progressbar\"", " aria-valuemin=\"0\" aria-valuemax=\"100\"", ">", "</div>", "</div>"],
7
+ _tmpl$2 = ["<div class=\"k2b-progress__value\" style=\"", "\"></div>"],
8
+ _tmpl$3 = ["<span class=\"k2b-progress__limit\" aria-hidden=\"true\" style=\"", "\"></span>"],
9
+ _tmpl$4 = ["<div class=\"k2b-progress__segment\"", " style=\"", "\"></div>"],
10
+ _tmpl$5 = ["<span class=\"k2b-progress__percent\">", "</span>"];
11
+ import { For, Show } from "solid-js";
12
+
13
+ /** One part of a segmented bar, in the unit of `limit`. `"hatched"` marks an expected, not yet booked part. */
14
+
15
+ /**
16
+ * Sum minus limit, without float residue: `0.1 + 0.2` against `0.3` is not over. Amounts within a billionth of the
17
+ * larger of both count as equal.
18
+ */
19
+ export const budgetOverrun = (total, limit) => {
20
+ const over = total - limit;
21
+ return over > Math.max(1, Math.abs(total), Math.abs(limit)) * 1e-9 ? over : 0;
22
+ };
23
+
24
+ /** Sum over limit; an empty budget with spending is infinitely full. */
25
+ export const budgetRatio = (total, limit) => limit > 0 ? total / limit : total > 0 ? Number.POSITIVE_INFINITY : 0;
26
+
27
+ /**
28
+ * The drawn parts of a segmented bar, each as a percent of the track. The track spans the larger of the limit and the
29
+ * sum, so an overrun stays visible: segments fill up to the limit in order, the rest is one "over" part, and `mark`
30
+ * places the limit line (only when the sum goes beyond it). Without `limit` the segments fill the track; a limit of 0,
31
+ * below 0 or not finite is an empty budget, so any spending is over it.
32
+ */
33
+ export const progressBarParts = (segments, limit) => {
34
+ const values = segments.map(segment => ({
35
+ pattern: segment.pattern ?? "solid",
36
+ value: Number.isFinite(segment.value) ? Math.max(0, segment.value) : 0
37
+ }));
38
+ const total = values.reduce((sum, segment) => sum + segment.value, 0);
39
+ const budget = limit === undefined ? total : Number.isFinite(limit) ? Math.max(0, limit) : 0;
40
+ const scale = Math.max(budget, total);
41
+ if (scale <= 0) return {
42
+ parts: [],
43
+ total,
44
+ over: 0,
45
+ mark: null,
46
+ ratio: 0
47
+ };
48
+ const parts = [];
49
+ let filled = 0;
50
+ for (const segment of values) {
51
+ const inside = Math.max(0, Math.min(segment.value, budget - filled));
52
+ if (inside > 0) parts.push({
53
+ pattern: segment.pattern,
54
+ width: inside / scale * 100
55
+ });
56
+ filled += segment.value;
57
+ }
58
+ const over = budgetOverrun(total, budget);
59
+ if (over > 0) parts.push({
60
+ pattern: "over",
61
+ width: over / scale * 100
62
+ });
63
+ return {
64
+ parts,
65
+ total,
66
+ over,
67
+ mark: over > 0 ? budget / scale * 100 : null,
68
+ ratio: budgetRatio(total, budget)
69
+ };
70
+ };
7
71
  const clamp = value => Number.isFinite(value) ? Math.max(0, Math.min(100, Math.round(value))) : 0;
8
72
  export function ProgressBar(props) {
9
- const percent = () => clamp(props.value);
10
- return _$ssr(_tmpl$, `k2b-progress ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-size", _$escape(props.size ?? "md", true), false) + _$ssrAttribute("data-tone", _$escape(props.tone ?? "info", true), false), _$ssrAttribute("aria-label", _$escape(props.label, true), false), _$ssrAttribute("aria-valuenow", _$escape(percent(), true), false), _$ssrStyle(`width: ${percent()}%`), props.showValue ? _$ssr(_tmpl$2, _$escape(percent())) : _$escape(null));
73
+ const segmented = () => props.segments ? progressBarParts(props.segments, props.limit) : null;
74
+ // With segments the share always comes from them; `value` only drives the plain bar.
75
+ const percent = () => {
76
+ const bar = segmented();
77
+ if (!bar) return clamp(props.value ?? 0);
78
+ return Number.isFinite(bar.ratio) ? Math.max(0, Math.round(bar.ratio * 100)) : null;
79
+ };
80
+ const shown = () => percent() ?? 100;
81
+ return _$ssr(_tmpl$, `k2b-progress ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-size", _$escape(props.size ?? "md", true), false) + _$ssrAttribute("data-tone", _$escape(props.tone ?? "info", true), false) + _$ssrAttribute("data-segmented", segmented() ? "" : _$escape(undefined, true), false), _$ssrAttribute("aria-label", _$escape(props.label, true), false), _$ssrAttribute("aria-valuenow", _$escape(Math.min(100, shown()), true), false) + _$ssrAttribute("aria-valuetext", _$escape(props.valueText ?? (segmented() ? percent() === null ? "∞" : `${percent()}%` : undefined), true), false), _$escape(_$createComponent(Show, {
82
+ get when() {
83
+ return segmented();
84
+ },
85
+ get fallback() {
86
+ return _$ssr(_tmpl$2, _$ssrStyle(`width: ${shown()}%`));
87
+ },
88
+ children: bar => [_$createComponent(For, {
89
+ get each() {
90
+ return bar().parts;
91
+ },
92
+ children: part => _$ssr(_tmpl$4, _$ssrAttribute("data-pattern", _$escape(part.pattern, true), false), _$ssrStyle(`width: ${part.width.toFixed(3)}%`))
93
+ }), _$createComponent(Show, {
94
+ get when() {
95
+ return bar().mark !== null;
96
+ },
97
+ get children() {
98
+ return _$ssr(_tmpl$3, _$ssrStyle(`left: ${(bar().mark ?? 0).toFixed(3)}%`));
99
+ }
100
+ })]
101
+ })), props.showValue ? _$ssr(_tmpl$5, percent() === null ? "∞" : `${_$escape(percent())}%`) : _$escape(null));
11
102
  }
12
103
  export default ProgressBar;
13
104
  //# sourceMappingURL=ProgressBar.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["surfaces/ProgressBar.tsx"],"sourcesContent":["import type { JSX } from \"solid-js\";\nimport type { IntentTone } from \"../semantics\";\n\nexport type ProgressBarTone = Extract<IntentTone, \"info\" | \"success\" | \"warning\" | \"danger\">;\n\nexport type ProgressBarProps = {\n value: number;\n size?: \"xs\" | \"sm\" | \"md\";\n tone?: ProgressBarTone;\n showValue?: boolean;\n label: string;\n class?: string;\n};\n\nconst clamp = (value: number) => (Number.isFinite(value) ? Math.max(0, Math.min(100, Math.round(value))) : 0);\n\nexport function ProgressBar(props: ProgressBarProps): JSX.Element {\n const percent = () => clamp(props.value);\n return (\n <div class={`k2b-progress ${props.class ?? \"\"}`} data-size={props.size ?? \"md\"} data-tone={props.tone ?? \"info\"}>\n <div\n class=\"k2b-progress__track\"\n role=\"progressbar\"\n aria-label={props.label}\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n aria-valuenow={percent()}\n >\n <div class=\"k2b-progress__value\" style={`width: ${percent()}%`} />\n </div>\n {props.showValue ? <span class=\"k2b-progress__percent\">{percent()}%</span> : null}\n </div>\n );\n}\n\nexport default ProgressBar;\n"],"mappings":";;;;;;AAcA,MAAM,KAAK,GAAI,KAAa,IAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAE;AAE7G,OAAO,SAAS,WAAW,CAAC,KAAuB,EAAe;EAChE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;EACxC,qBACc,yBAAgB,KAAK,CAAC,KAAK,IAAI,EAAE,SAAE,uCAAa,KAAK,CAAC,IAAI,IAAI,IAAI,uDAAa,KAAK,CAAC,IAAI,IAAI,MAAM,uDAI/F,KAAK,CAAC,KAAK,0DAGR,OAAO,CAAC,CAAC,4BAEgB,UAAU,OAAO,CAAC,CAAC,GAAG,GAE/D,KAAK,CAAC,SAAS,2BAAwC,OAAO,CAAC,CAAC,cAAY,IAAI;AAGvF;AAEA,eAAe,WAAW","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["surfaces/ProgressBar.tsx"],"sourcesContent":["import { For, type JSX, Show } from \"solid-js\";\nimport type { IntentTone } from \"../semantics\";\n\nexport type ProgressBarTone = Extract<IntentTone, \"info\" | \"success\" | \"warning\" | \"danger\">;\n\n/** One part of a segmented bar, in the unit of `limit`. `\"hatched\"` marks an expected, not yet booked part. */\nexport type ProgressBarSegment = { value: number; pattern?: \"solid\" | \"hatched\" };\n\nexport type ProgressBarProps = {\n /** Percent from 0 to 100. With `segments`, the share of `limit` the segments fill. */\n value?: number;\n size?: \"xs\" | \"sm\" | \"md\";\n tone?: ProgressBarTone;\n showValue?: boolean;\n label: string;\n /** Parts drawn in order, such as booked and expected spending. Negative and non-finite values count as 0. */\n segments?: readonly ProgressBarSegment[];\n /** The budget the segments fill. What goes beyond it is drawn red, with a mark at the limit. */\n limit?: number;\n /** Spoken value, such as \"1,240 € of 1,500 €\"; defaults to the percent. */\n valueText?: string;\n class?: string;\n};\n\nexport type ProgressBarPart = { pattern: \"solid\" | \"hatched\" | \"over\"; width: number };\n\n/**\n * Sum minus limit, without float residue: `0.1 + 0.2` against `0.3` is not over. Amounts within a billionth of the\n * larger of both count as equal.\n */\nexport const budgetOverrun = (total: number, limit: number): number => {\n const over = total - limit;\n return over > Math.max(1, Math.abs(total), Math.abs(limit)) * 1e-9 ? over : 0;\n};\n\n/** Sum over limit; an empty budget with spending is infinitely full. */\nexport const budgetRatio = (total: number, limit: number): number => (limit > 0 ? total / limit : total > 0 ? Number.POSITIVE_INFINITY : 0);\n\n/**\n * The drawn parts of a segmented bar, each as a percent of the track. The track spans the larger of the limit and the\n * sum, so an overrun stays visible: segments fill up to the limit in order, the rest is one \"over\" part, and `mark`\n * places the limit line (only when the sum goes beyond it). Without `limit` the segments fill the track; a limit of 0,\n * below 0 or not finite is an empty budget, so any spending is over it.\n */\nexport const progressBarParts = (\n segments: readonly ProgressBarSegment[],\n limit?: number,\n): { parts: ProgressBarPart[]; total: number; over: number; mark: number | null; ratio: number } => {\n const values = segments.map((segment) => ({\n pattern: segment.pattern ?? \"solid\",\n value: Number.isFinite(segment.value) ? Math.max(0, segment.value) : 0,\n }));\n const total = values.reduce((sum, segment) => sum + segment.value, 0);\n const budget = limit === undefined ? total : Number.isFinite(limit) ? Math.max(0, limit) : 0;\n const scale = Math.max(budget, total);\n if (scale <= 0) return { parts: [], total, over: 0, mark: null, ratio: 0 };\n const parts: ProgressBarPart[] = [];\n let filled = 0;\n for (const segment of values) {\n const inside = Math.max(0, Math.min(segment.value, budget - filled));\n if (inside > 0) parts.push({ pattern: segment.pattern, width: (inside / scale) * 100 });\n filled += segment.value;\n }\n const over = budgetOverrun(total, budget);\n if (over > 0) parts.push({ pattern: \"over\", width: (over / scale) * 100 });\n return { parts, total, over, mark: over > 0 ? (budget / scale) * 100 : null, ratio: budgetRatio(total, budget) };\n};\n\nconst clamp = (value: number) => (Number.isFinite(value) ? Math.max(0, Math.min(100, Math.round(value))) : 0);\n\nexport function ProgressBar(props: ProgressBarProps): JSX.Element {\n const segmented = () => (props.segments ? progressBarParts(props.segments, props.limit) : null);\n // With segments the share always comes from them; `value` only drives the plain bar.\n const percent = () => {\n const bar = segmented();\n if (!bar) return clamp(props.value ?? 0);\n return Number.isFinite(bar.ratio) ? Math.max(0, Math.round(bar.ratio * 100)) : null;\n };\n const shown = () => percent() ?? 100;\n return (\n <div\n class={`k2b-progress ${props.class ?? \"\"}`}\n data-size={props.size ?? \"md\"}\n data-tone={props.tone ?? \"info\"}\n data-segmented={segmented() ? \"\" : undefined}\n >\n <div\n class=\"k2b-progress__track\"\n role=\"progressbar\"\n aria-label={props.label}\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n aria-valuenow={Math.min(100, shown())}\n aria-valuetext={props.valueText ?? (segmented() ? (percent() === null ? \"∞\" : `${percent()}%`) : undefined)}\n >\n <Show when={segmented()} fallback={<div class=\"k2b-progress__value\" style={`width: ${shown()}%`} />}>\n {(bar) => (\n <>\n <For each={bar().parts}>\n {(part) => <div class=\"k2b-progress__segment\" data-pattern={part.pattern} style={`width: ${part.width.toFixed(3)}%`} />}\n </For>\n <Show when={bar().mark !== null}>\n <span class=\"k2b-progress__limit\" aria-hidden=\"true\" style={`left: ${(bar().mark ?? 0).toFixed(3)}%`} />\n </Show>\n </>\n )}\n </Show>\n </div>\n {props.showValue ? <span class=\"k2b-progress__percent\">{percent() === null ? \"∞\" : `${percent()}%`}</span> : null}\n </div>\n );\n}\n\nexport default ProgressBar;\n"],"mappings":"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","ignoreList":[]}
@@ -1,25 +1,46 @@
1
- import { createComponent as _$createComponent } from "solid-js/web";
2
1
  import { memo as _$memo } from "solid-js/web";
3
2
  import { ssr as _$ssr } from "solid-js/web";
4
3
  import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
5
4
  import { escape as _$escape } from "solid-js/web";
6
- var _tmpl$ = ["<span class=\"k2b-stat-cell__label\"", ">", "</span>"],
7
- _tmpl$2 = ["<span class=\"", "\"", ">", "</span>"],
8
- _tmpl$3 = ["<div class=\"k2b-stat-cell__support\">", "", "</div>"],
9
- _tmpl$4 = ["<span class=\"k2b-stat-cell__sub\">", "</span>"],
10
- _tmpl$5 = ["<i class=\"", "\"", " aria-hidden=\"true\"></i>"],
11
- _tmpl$6 = ["<span class=\"k2b-stat-cell__accent\"", "><i", " aria-hidden=\"true\"></i>", "</span>"],
12
- _tmpl$7 = ["<div class=\"k2b-stat-cell\"", ">", "</div>"],
13
- _tmpl$8 = ["<a", " class=\"k2b-stat-cell k2b-stat-cell--link\"", "><i class=\"ti ti-external-link k2b-stat-cell__link-icon\" aria-hidden=\"true\"></i>", "</a>"];
14
- import { Show } from "solid-js";
5
+ import { createComponent as _$createComponent } from "solid-js/web";
6
+ var _tmpl$ = ["<div class=\"k2b-stat-cell__head\">", "<span class=\"k2b-stat-cell__label\"", ">", "</span></div>"],
7
+ _tmpl$2 = "<span class=\"k2b-stat-cell__skeleton k2b-skeleton\" data-part=\"value\" aria-hidden=\"true\"></span>",
8
+ _tmpl$3 = ["<span class=\"k2b-sr-only\">", "</span>"],
9
+ _tmpl$4 = ["<span class=\"k2b-stat-cell__error\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i>", "</span>"],
10
+ _tmpl$5 = ["<div class=\"k2b-stat-cell__main\">", "</div>"],
11
+ _tmpl$6 = "<span class=\"k2b-stat-cell__skeleton k2b-skeleton\" data-part=\"support\" aria-hidden=\"true\"></span>",
12
+ _tmpl$7 = ["<div class=\"k2b-stat-cell__support\">", "</div>"],
13
+ _tmpl$8 = ["<span class=\"k2b-stat-cell__icon k2b-icon-tile\"", " aria-hidden=\"true\"><i", "></i></span>"],
14
+ _tmpl$9 = ["<span class=\"", "\"", ">", "</span>"],
15
+ _tmpl$0 = ["<span class=\"k2b-stat-cell__sub\">", " ", "</span>"],
16
+ _tmpl$1 = ["<span class=\"k2b-stat-cell__sub\">", "</span>"],
17
+ _tmpl$10 = ["<i class=\"", "\"", " aria-hidden=\"true\"></i>"],
18
+ _tmpl$11 = ["<span class=\"k2b-stat-cell__accent\"", "><i", " aria-hidden=\"true\"></i>", "</span>"],
19
+ _tmpl$12 = ["<span class=\"k2b-stat-cell__stale\"><i class=\"ti ti-clock-exclamation\" aria-hidden=\"true\"></i>", "</span>"],
20
+ _tmpl$13 = ["<button type=\"button\" class=\"k2b-stat-cell__retry\">", "</button>"],
21
+ _tmpl$14 = ["<a", " class=\"k2b-stat-cell k2b-stat-cell--link\"", "><i class=\"ti ti-external-link k2b-stat-cell__link-icon\" aria-hidden=\"true\"></i>", "</a>"],
22
+ _tmpl$15 = ["<div class=\"k2b-stat-cell\"", ">", "</div>"];
23
+ import { Match, Show, Switch } from "solid-js";
15
24
  import Chart from "../content/Chart";
16
25
  import { useLocale } from "../intl/locale";
26
+ import { DeltaBadge } from "./delta-badge";
17
27
  import { useStatGridSize, useStatGridSurface } from "./StatGrid";
28
+ import { useStatCellMessages } from "./stat-cell-messages";
29
+
30
+ /**
31
+ * The change against a comparison period. `unit: "ratio"` (default) reads `0.082` as `+8.2 %`; `"points"` is the
32
+ * difference of two shares in ratio units, so `0.031` reads `+3.1 pts`. Only with `better` does the badge carry a tone
33
+ * and say "better" or "worse"; without it the change stays unrated.
34
+ */
35
+
36
+ /** The value the change compares against, shown under the figure, such as "Last month 44,560". */
37
+
18
38
  function Body(props) {
19
39
  const gridSize = useStatGridSize();
20
40
  const size = () => props.size ?? gridSize;
21
41
  const accent = () => props.accent;
22
42
  const locale = useLocale();
43
+ const t = useStatCellMessages();
23
44
  const defaultTrendTooltip = ({
24
45
  datum
25
46
  }) => {
@@ -33,71 +54,156 @@ function Body(props) {
33
54
  }]
34
55
  };
35
56
  };
36
- return [_$ssr(_tmpl$, _$ssrAttribute("data-size", _$escape(size(), true), false), _$escape(props.label)), _$ssr(_tmpl$2, `k2b-stat-cell__value ${_$escape(props.valueClass ?? "", true)}`, _$ssrAttribute("data-size", _$escape(size(), true), false) + _$ssrAttribute("title", _$escape(props.title, true), false), _$escape(props.value)), _$createComponent(Show, {
57
+ const compareValue = () => {
58
+ const value = props.compare?.value;
59
+ return typeof value === "number" ? new Intl.NumberFormat(locale()).format(value) : value;
60
+ };
61
+ const empty = () => props.value === null || props.value === undefined || props.value === "";
62
+ return [_$ssr(_tmpl$, _$escape(_$createComponent(Show, {
37
63
  get when() {
38
- return _$memo(() => !!props.trend)() && props.trend.length > 1;
64
+ return props.icon;
65
+ },
66
+ children: icon => _$ssr(_tmpl$8, _$ssrAttribute("data-tone", _$escape(props.tone ?? "zinc", true), false), _$ssrAttribute("class", _$escape(icon(), true), false))
67
+ })), _$ssrAttribute("data-size", _$escape(size(), true), false), _$escape(props.label)), _$ssr(_tmpl$5, _$escape(_$createComponent(Switch, {
68
+ get fallback() {
69
+ return [_$ssr(_tmpl$9, `k2b-stat-cell__value ${_$escape(props.valueClass ?? "", true)}`, _$ssrAttribute("data-size", _$escape(size(), true), false) + _$ssrAttribute("data-empty", _$escape(empty() || undefined, true), false) + _$ssrAttribute("title", _$escape(props.title, true), false), empty() ? "–" : _$escape(props.value)), _$createComponent(Show, {
70
+ get when() {
71
+ return _$memo(() => !!!empty())() && props.delta;
72
+ },
73
+ children: delta => _$createComponent(DeltaBadge, {
74
+ get delta() {
75
+ return delta();
76
+ },
77
+ get compare() {
78
+ return props.compare;
79
+ }
80
+ })
81
+ })];
39
82
  },
40
83
  get children() {
41
- return _$createComponent(Chart, {
42
- kind: "sparkline",
43
- get labels() {
44
- return {
45
- interactiveChart: props.label
46
- };
84
+ return [_$createComponent(Match, {
85
+ get when() {
86
+ return props.status === "loading";
47
87
  },
48
- get tooltip() {
49
- return props.trendTooltip ?? defaultTrendTooltip;
88
+ get children() {
89
+ return [_$ssr(_tmpl$2), _$ssr(_tmpl$3, _$escape(t().loading))];
90
+ }
91
+ }), _$createComponent(Match, {
92
+ get when() {
93
+ return props.status === "error";
50
94
  },
51
- "class": "k2b-stat-cell__trend",
52
- style: {
53
- height: "32px"
95
+ get children() {
96
+ return _$ssr(_tmpl$4, _$escape(t().notLoaded));
97
+ }
98
+ })];
99
+ }
100
+ }))), _$ssr(_tmpl$7, _$escape(_$createComponent(Switch, {
101
+ get fallback() {
102
+ return _$createComponent(Show, {
103
+ get when() {
104
+ return props.stale;
54
105
  },
55
- get data() {
56
- return Array.from(props.trend ?? []);
106
+ get fallback() {
107
+ return [_$createComponent(Show, {
108
+ get when() {
109
+ return props.compare;
110
+ },
111
+ children: compare => _$ssr(_tmpl$0, _$escape(compare().label), _$escape(compareValue()))
112
+ }), _$createComponent(Show, {
113
+ get when() {
114
+ return props.sub;
115
+ },
116
+ children: sub => _$ssr(_tmpl$1, _$escape(sub()))
117
+ }), _$createComponent(Show, {
118
+ get when() {
119
+ return accent();
120
+ },
121
+ children: value => _$createComponent(Show, {
122
+ get when() {
123
+ return value().text;
124
+ },
125
+ get fallback() {
126
+ return _$ssr(_tmpl$10, `${_$escape(value().icon, true)} k2b-stat-cell__accent-icon`, _$ssrAttribute("data-tone", _$escape(value().tone, true), false));
127
+ },
128
+ children: text => _$ssr(_tmpl$11, _$ssrAttribute("data-tone", _$escape(value().tone, true), false), _$ssrAttribute("class", _$escape(value().icon, true), false), _$escape(text()))
129
+ })
130
+ }), _$createComponent(Show, {
131
+ get when() {
132
+ return _$memo(() => !!(!empty() && props.trend))() && props.trend.length > 1;
133
+ },
134
+ get children() {
135
+ return _$createComponent(Chart, {
136
+ kind: "sparkline",
137
+ get labels() {
138
+ return {
139
+ interactiveChart: props.label
140
+ };
141
+ },
142
+ get tooltip() {
143
+ return props.trendTooltip ?? defaultTrendTooltip;
144
+ },
145
+ "class": "k2b-stat-cell__trend",
146
+ get data() {
147
+ return Array.from(props.trend ?? []);
148
+ },
149
+ showLast: true
150
+ });
151
+ }
152
+ })];
57
153
  },
58
- showLast: true,
59
- showMinMax: true
154
+ children: stale => [_$ssr(_tmpl$12, _$escape(stale())), _$createComponent(Show, {
155
+ get when() {
156
+ return props.onRetry;
157
+ },
158
+ children: retry => _$ssr(_tmpl$13, _$escape(t().reload))
159
+ })]
60
160
  });
61
- }
62
- }), _$createComponent(Show, {
63
- get when() {
64
- return props.sub || accent();
65
161
  },
66
162
  get children() {
67
- return _$ssr(_tmpl$3, _$escape(_$createComponent(Show, {
163
+ return [_$createComponent(Match, {
68
164
  get when() {
69
- return props.sub;
165
+ return props.status === "loading";
70
166
  },
71
- children: sub => _$ssr(_tmpl$4, _$escape(sub()))
72
- })), _$escape(_$createComponent(Show, {
167
+ get children() {
168
+ return _$ssr(_tmpl$6);
169
+ }
170
+ }), _$createComponent(Match, {
73
171
  get when() {
74
- return accent();
172
+ return props.status === "error";
75
173
  },
76
- children: value => _$createComponent(Show, {
77
- get when() {
78
- return value().text;
79
- },
80
- get fallback() {
81
- return _$ssr(_tmpl$5, `${_$escape(value().icon, true)} k2b-stat-cell__accent-icon`, _$ssrAttribute("data-tone", _$escape(value().tone, true), false));
82
- },
83
- children: text => _$ssr(_tmpl$6, _$ssrAttribute("data-tone", _$escape(value().tone, true), false), _$ssrAttribute("class", _$escape(value().icon, true), false), _$escape(text()))
84
- })
85
- })));
174
+ get children() {
175
+ return _$createComponent(Show, {
176
+ get when() {
177
+ return props.onRetry;
178
+ },
179
+ children: retry => _$ssr(_tmpl$13, _$escape(t().retry))
180
+ });
181
+ }
182
+ })];
86
183
  }
87
- })];
184
+ })))];
88
185
  }
186
+
187
+ /**
188
+ * One key figure as a framed tile: icon and label, the figure with its change, and a support line with the comparison
189
+ * and the trend. Each state (loading, error, stale, empty) keeps the tile's place and size.
190
+ */
89
191
  export function StatCell(props) {
90
192
  const gridSize = useStatGridSize();
91
193
  const size = () => props.size ?? gridSize;
92
194
  const surface = useStatGridSurface();
93
- return _$createComponent(Show, {
195
+ return _$createComponent(Show
196
+ // A tile that offers a retry is no link: the button must not sit inside it.
197
+ , {
94
198
  get when() {
95
- return props.href;
199
+ return _$memo(() => !!(props.href && !props.status))() && !(props.stale && props.onRetry);
96
200
  },
97
201
  get fallback() {
98
- return _$ssr(_tmpl$7, _$ssrAttribute("data-size", _$escape(size(), true), false) + _$ssrAttribute("data-surface", _$escape(surface, true), false) + _$ssrAttribute("data-wrap", _$escape(props.wrap || undefined, true), false), _$escape(_$createComponent(Body, props)));
202
+ return _$ssr(_tmpl$15, _$ssrAttribute("data-size", _$escape(size(), true), false) + _$ssrAttribute("data-surface", _$escape(surface, true), false) + _$ssrAttribute("data-wrap", _$escape(props.wrap || undefined, true), false) + _$ssrAttribute("data-status", _$escape(props.status, true), false) + _$ssrAttribute("aria-busy", _$escape(props.status === "loading" || undefined, true), false), _$escape(_$createComponent(Body, props)));
99
203
  },
100
- children: href => _$ssr(_tmpl$8, _$ssrAttribute("href", _$escape(href(), true), false), _$ssrAttribute("data-size", _$escape(size(), true), false) + _$ssrAttribute("data-surface", _$escape(surface, true), false) + _$ssrAttribute("data-wrap", _$escape(props.wrap || undefined, true), false), _$escape(_$createComponent(Body, props)))
204
+ get children() {
205
+ return _$ssr(_tmpl$14, _$ssrAttribute("href", _$escape(props.href, true), false), _$ssrAttribute("data-size", _$escape(size(), true), false) + _$ssrAttribute("data-surface", _$escape(surface, true), false) + _$ssrAttribute("data-wrap", _$escape(props.wrap || undefined, true), false), _$escape(_$createComponent(Body, props)));
206
+ }
101
207
  });
102
208
  }
103
209
  export default StatCell;