pricing-renderer 0.1.0 → 0.2.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 (175) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/README.md +142 -25
  3. package/THIRD_PARTY_NOTICES.md +84 -6
  4. package/custom-elements.json +233 -3
  5. package/dist/core/apply-selection.d.ts +8 -0
  6. package/dist/core/apply-selection.d.ts.map +1 -0
  7. package/dist/core/apply-selection.js +76 -0
  8. package/dist/core/apply-selection.js.map +1 -0
  9. package/dist/core/breakdown.d.ts +13 -0
  10. package/dist/core/breakdown.d.ts.map +1 -0
  11. package/dist/core/breakdown.js +56 -0
  12. package/dist/core/breakdown.js.map +1 -0
  13. package/dist/core/compare-sides.d.ts +16 -0
  14. package/dist/core/compare-sides.d.ts.map +1 -0
  15. package/dist/core/compare-sides.js +73 -0
  16. package/dist/core/compare-sides.js.map +1 -0
  17. package/dist/core/compare-values.d.ts +13 -0
  18. package/dist/core/compare-values.d.ts.map +1 -0
  19. package/dist/core/compare-values.js +100 -0
  20. package/dist/core/compare-values.js.map +1 -0
  21. package/dist/core/compare.d.ts +7 -0
  22. package/dist/core/compare.d.ts.map +1 -0
  23. package/dist/core/compare.js +301 -0
  24. package/dist/core/compare.js.map +1 -0
  25. package/dist/core/expression.js +2 -2
  26. package/dist/core/expression.js.map +1 -1
  27. package/dist/core/i18n.d.ts.map +1 -1
  28. package/dist/core/i18n.js +136 -2
  29. package/dist/core/i18n.js.map +1 -1
  30. package/dist/core/index.d.ts +9 -2
  31. package/dist/core/index.d.ts.map +1 -1
  32. package/dist/core/index.js +6 -1
  33. package/dist/core/index.js.map +1 -1
  34. package/dist/core/normalize.d.ts.map +1 -1
  35. package/dist/core/normalize.js +70 -9
  36. package/dist/core/normalize.js.map +1 -1
  37. package/dist/core/plan-relation.d.ts +8 -0
  38. package/dist/core/plan-relation.d.ts.map +1 -0
  39. package/dist/core/plan-relation.js +15 -0
  40. package/dist/core/plan-relation.js.map +1 -0
  41. package/dist/core/price-stabilizer.d.ts +3 -0
  42. package/dist/core/price-stabilizer.d.ts.map +1 -0
  43. package/dist/core/price-stabilizer.js +73 -0
  44. package/dist/core/price-stabilizer.js.map +1 -0
  45. package/dist/core/resolve.d.ts +7 -1
  46. package/dist/core/resolve.d.ts.map +1 -1
  47. package/dist/core/resolve.js +66 -21
  48. package/dist/core/resolve.js.map +1 -1
  49. package/dist/core/search-params.d.ts +15 -0
  50. package/dist/core/search-params.d.ts.map +1 -0
  51. package/dist/core/search-params.js +175 -0
  52. package/dist/core/search-params.js.map +1 -0
  53. package/dist/core/selection-change.d.ts +10 -0
  54. package/dist/core/selection-change.d.ts.map +1 -0
  55. package/dist/core/selection-change.js +124 -0
  56. package/dist/core/selection-change.js.map +1 -0
  57. package/dist/core/types.d.ts +209 -0
  58. package/dist/core/types.d.ts.map +1 -1
  59. package/dist/core/units.d.ts +13 -0
  60. package/dist/core/units.d.ts.map +1 -0
  61. package/dist/core/units.js +84 -0
  62. package/dist/core/units.js.map +1 -0
  63. package/dist/core/utils.d.ts.map +1 -1
  64. package/dist/core/utils.js +91 -32
  65. package/dist/core/utils.js.map +1 -1
  66. package/dist/core/view-model.d.ts +3 -1
  67. package/dist/core/view-model.d.ts.map +1 -1
  68. package/dist/core/view-model.js +113 -7
  69. package/dist/core/view-model.js.map +1 -1
  70. package/dist/element/change-review-define.d.ts +4 -0
  71. package/dist/element/change-review-define.d.ts.map +1 -0
  72. package/dist/element/change-review-define.js +17 -0
  73. package/dist/element/change-review-define.js.map +1 -0
  74. package/dist/element/change-review-element.d.ts +53 -0
  75. package/dist/element/change-review-element.d.ts.map +1 -0
  76. package/dist/element/change-review-element.js +104 -0
  77. package/dist/element/change-review-element.js.map +1 -0
  78. package/dist/element/change-review-events.d.ts +10 -0
  79. package/dist/element/change-review-events.d.ts.map +1 -0
  80. package/dist/element/change-review-events.js +2 -0
  81. package/dist/element/change-review-events.js.map +1 -0
  82. package/dist/element/change-review-renderers.d.ts +23 -0
  83. package/dist/element/change-review-renderers.d.ts.map +1 -0
  84. package/dist/element/change-review-renderers.js +391 -0
  85. package/dist/element/change-review-renderers.js.map +1 -0
  86. package/dist/element/cta.d.ts +45 -0
  87. package/dist/element/cta.d.ts.map +1 -0
  88. package/dist/element/cta.js +90 -0
  89. package/dist/element/cta.js.map +1 -0
  90. package/dist/element/current-selection.d.ts +13 -0
  91. package/dist/element/current-selection.d.ts.map +1 -0
  92. package/dist/element/current-selection.js +57 -0
  93. package/dist/element/current-selection.js.map +1 -0
  94. package/dist/element/define-change-review.d.ts +2 -0
  95. package/dist/element/define-change-review.d.ts.map +1 -0
  96. package/dist/element/define-change-review.js +7 -0
  97. package/dist/element/define-change-review.js.map +1 -0
  98. package/dist/element/events.d.ts +32 -6
  99. package/dist/element/events.d.ts.map +1 -1
  100. package/dist/element/hide-sections.d.ts +6 -0
  101. package/dist/element/hide-sections.d.ts.map +1 -0
  102. package/dist/element/hide-sections.js +26 -0
  103. package/dist/element/hide-sections.js.map +1 -0
  104. package/dist/element/index.d.ts +8 -1
  105. package/dist/element/index.d.ts.map +1 -1
  106. package/dist/element/index.js +3 -0
  107. package/dist/element/index.js.map +1 -1
  108. package/dist/element/pricing-renderer-element.d.ts +95 -7
  109. package/dist/element/pricing-renderer-element.d.ts.map +1 -1
  110. package/dist/element/pricing-renderer-element.js +563 -217
  111. package/dist/element/pricing-renderer-element.js.map +1 -1
  112. package/dist/element/renderers.d.ts +49 -4
  113. package/dist/element/renderers.d.ts.map +1 -1
  114. package/dist/element/renderers.js +795 -263
  115. package/dist/element/renderers.js.map +1 -1
  116. package/dist/element/same-input.d.ts +3 -0
  117. package/dist/element/same-input.d.ts.map +1 -0
  118. package/dist/element/same-input.js +56 -0
  119. package/dist/element/same-input.js.map +1 -0
  120. package/dist/react/headless.d.ts +9 -0
  121. package/dist/react/headless.d.ts.map +1 -0
  122. package/dist/react/headless.js +8 -0
  123. package/dist/react/headless.js.map +1 -0
  124. package/dist/react/index.d.ts +54 -4
  125. package/dist/react/index.d.ts.map +1 -1
  126. package/dist/react/index.js +156 -6
  127. package/dist/react/index.js.map +1 -1
  128. package/dist/react/provider.d.ts +17 -0
  129. package/dist/react/provider.d.ts.map +1 -0
  130. package/dist/react/provider.js +38 -0
  131. package/dist/react/provider.js.map +1 -0
  132. package/dist/react/selection-store.d.ts +51 -0
  133. package/dist/react/selection-store.d.ts.map +1 -0
  134. package/dist/react/selection-store.js +239 -0
  135. package/dist/react/selection-store.js.map +1 -0
  136. package/dist/react/slots-types.d.ts +38 -0
  137. package/dist/react/slots-types.d.ts.map +1 -0
  138. package/dist/react/slots-types.js +2 -0
  139. package/dist/react/slots-types.js.map +1 -0
  140. package/dist/react/slots.d.ts +40 -0
  141. package/dist/react/slots.d.ts.map +1 -0
  142. package/dist/react/slots.js +165 -0
  143. package/dist/react/slots.js.map +1 -0
  144. package/dist/react/use-pricing-selection.d.ts +10 -0
  145. package/dist/react/use-pricing-selection.d.ts.map +1 -0
  146. package/dist/react/use-pricing-selection.js +44 -0
  147. package/dist/react/use-pricing-selection.js.map +1 -0
  148. package/dist/react/use-pricing-source.d.ts +34 -0
  149. package/dist/react/use-pricing-source.d.ts.map +1 -0
  150. package/dist/react/use-pricing-source.js +186 -0
  151. package/dist/react/use-pricing-source.js.map +1 -0
  152. package/dist/react/use-renderer-state.d.ts +22 -0
  153. package/dist/react/use-renderer-state.d.ts.map +1 -0
  154. package/dist/react/use-renderer-state.js +112 -0
  155. package/dist/react/use-renderer-state.js.map +1 -0
  156. package/dist/space/index.d.ts +26 -0
  157. package/dist/space/index.d.ts.map +1 -0
  158. package/dist/space/index.js +71 -0
  159. package/dist/space/index.js.map +1 -0
  160. package/dist/styles/base.css +1030 -76
  161. package/dist/styles/theme-minimal.css +103 -0
  162. package/dist/styles/theme.css +22 -3
  163. package/dist/styles/tokens-shadcn.css +40 -0
  164. package/dist/yaml/index.d.ts +2 -0
  165. package/dist/yaml/index.d.ts.map +1 -1
  166. package/dist/yaml/index.js +1 -0
  167. package/dist/yaml/index.js.map +1 -1
  168. package/dist/yaml/load.d.ts.map +1 -1
  169. package/dist/yaml/load.js +106 -17
  170. package/dist/yaml/load.js.map +1 -1
  171. package/dist/yaml/source.d.ts +18 -0
  172. package/dist/yaml/source.d.ts.map +1 -0
  173. package/dist/yaml/source.js +46 -0
  174. package/dist/yaml/source.js.map +1 -0
  175. package/package.json +25 -13
@@ -0,0 +1,391 @@
1
+ // `html` is aliased so that the layout of the templates below stays as written.
2
+ import { html as tpl, nothing } from 'lit';
3
+ import { formatCurrency, translate, } from '../core/index.js';
4
+ import { formatUnit } from '../core/units.js';
5
+ // The parts of the review a host can hide. The content sections are deliberately absent: a review
6
+ // without its plan or its price is not a review.
7
+ export const REVIEW_SECTION_NAMES = [
8
+ // For a host whose own heading already names what is being compared.
9
+ 'title',
10
+ // The badge that reads a plan change as Upgrade or Downgrade. It follows the declaration order
11
+ // of `pricing.plans`, which is a ladder only inside a subscription: two configurations compared
12
+ // side by side have no such order, and the badge would assert one they do not have.
13
+ 'relation',
14
+ // Confirm and Back, for a diff that is not actionable.
15
+ 'actions',
16
+ ];
17
+ const relationKeys = {
18
+ upgrade: 'pricing.reviewRelationUpgrade',
19
+ downgrade: 'pricing.reviewRelationDowngrade',
20
+ change: 'pricing.reviewRelationChange',
21
+ current: 'pricing.reviewRelationCurrent',
22
+ };
23
+ const relationTones = {
24
+ upgrade: 'success',
25
+ downgrade: 'warning',
26
+ change: 'accent',
27
+ current: undefined,
28
+ };
29
+ const removalKeys = {
30
+ 'unavailable-for-plan': 'pricing.reviewRemovedUnavailable',
31
+ 'dependency-removed': 'pricing.reviewRemovedDependency',
32
+ 'not-in-version': 'pricing.reviewRemovedNotInVersion',
33
+ };
34
+ const differenceKeys = {
35
+ quote: 'pricing.reviewDifferenceQuote',
36
+ currency: 'pricing.reviewDifferenceCurrency',
37
+ };
38
+ const dash = '—';
39
+ // Intl rejects the underscore of a locale such as es_ES.
40
+ function intlLocale(locale) {
41
+ return locale.replace(/_/g, '-');
42
+ }
43
+ function formatNumber(value, locale, options) {
44
+ try {
45
+ return new Intl.NumberFormat(intlLocale(locale), options).format(value);
46
+ }
47
+ catch {
48
+ return String(value);
49
+ }
50
+ }
51
+ // Mirrors renderValue of the renderer, as text: no value and false are "Not included", unlimited
52
+ // is "Unlimited", numbers use Intl for the locale and carry the unit, text is kept, and arrays and
53
+ // objects are written as JSON.
54
+ export function formatComparedValue(value, messages, locale, unit) {
55
+ switch (value.kind) {
56
+ case 'none':
57
+ return translate(messages, 'pricing.notIncluded');
58
+ case 'unlimited':
59
+ return translate(messages, 'pricing.unlimited');
60
+ case 'boolean':
61
+ return translate(messages, value.value ? 'pricing.included' : 'pricing.notIncluded');
62
+ case 'number': {
63
+ const formatted = formatNumber(value.value, locale);
64
+ if (!unit)
65
+ return formatted;
66
+ return `${formatted} ${formatUnit(unit, value.value, intlLocale(locale))}`;
67
+ }
68
+ case 'text':
69
+ return value.value;
70
+ case 'json':
71
+ return JSON.stringify(value.value);
72
+ }
73
+ }
74
+ // The difference of two subtotals with its sign ("+€2", "-€5", "€0"), with the fraction digits of
75
+ // formatCurrency.
76
+ export function formatSignedCurrency(amount, currency, locale = 'en-US') {
77
+ const maximumFractionDigits = amount % 1 === 0 ? 0 : 2;
78
+ const base = { signDisplay: 'exceptZero', maximumFractionDigits };
79
+ try {
80
+ const options = { ...base, style: 'currency', currency };
81
+ return new Intl.NumberFormat(intlLocale(locale), options).format(amount);
82
+ }
83
+ catch {
84
+ return formatNumber(amount, locale, base);
85
+ }
86
+ }
87
+ function t(state, key, parameters) {
88
+ return translate(state.messages, key, parameters);
89
+ }
90
+ function arrow(state, before, after) {
91
+ return t(state, 'pricing.reviewChangeArrow', { before, after });
92
+ }
93
+ function badge(text, tone) {
94
+ return tpl `<span data-pr-part="change-badge" data-tone=${tone ?? nothing}>${text}</span>`;
95
+ }
96
+ function reviewSection(state, part, titleKey, body) {
97
+ const titleId = `${state.idPrefix}-${part}`;
98
+ return tpl `<section data-pr-part=${part} aria-labelledby=${titleId}>
99
+ <h3 id=${titleId}>${t(state, titleKey)}</h3>
100
+ ${body}
101
+ </section>`;
102
+ }
103
+ function changeRow(name, parts = {}) {
104
+ const { detail, badge: tag, reason, warning } = parts;
105
+ return tpl `<li data-pr-part="change-row">
106
+ <span class="pr-cr-row__name">${name}</span>
107
+ ${detail ? tpl `<span class="pr-cr-row__detail">${detail}</span>` : nothing}
108
+ ${tag ?? nothing}
109
+ ${reason ? tpl `<span class="pr-cr-row__reason">${reason}</span>` : nothing}
110
+ ${warning ? tpl `<p class="pr-cr-warning" role="note">${warning}</p>` : nothing}
111
+ </li>`;
112
+ }
113
+ function list(rows) {
114
+ return tpl `<ul>${rows}</ul>`;
115
+ }
116
+ function planName(plan) {
117
+ return plan.name ?? plan.id ?? dash;
118
+ }
119
+ function periodName(period) {
120
+ return period?.label ?? period?.id ?? dash;
121
+ }
122
+ function renderPlan(state) {
123
+ const { plan, relation, version } = state.comparison;
124
+ let name = planName(plan.after);
125
+ if (plan.changed)
126
+ name = arrow(state, planName(plan.before), planName(plan.after));
127
+ let banner = nothing;
128
+ if (version.changed) {
129
+ const text = t(state, 'pricing.reviewVersionChange', {
130
+ before: version.before ?? dash,
131
+ after: version.after ?? dash,
132
+ });
133
+ banner = tpl `<p class="pr-cr-banner">${text}</p>`;
134
+ }
135
+ const relationBadge = state.hiddenSections?.has('relation')
136
+ ? nothing
137
+ : badge(t(state, relationKeys[relation]), relationTones[relation]);
138
+ const body = tpl `<div data-pr-part="change-row">
139
+ <span class="pr-cr-row__name">${name}</span>
140
+ ${relationBadge}
141
+ </div>
142
+ ${banner}`;
143
+ return reviewSection(state, 'review-plan', 'pricing.reviewPlan', body);
144
+ }
145
+ // formatCurrency throws for a locale that Intl rejects; a plain number is the fallback then.
146
+ function formatSubtotal(amount, currency, locale) {
147
+ try {
148
+ return formatCurrency(amount, currency, intlLocale(locale));
149
+ }
150
+ catch {
151
+ return formatNumber(amount, locale, { maximumFractionDigits: amount % 1 === 0 ? 0 : 2 });
152
+ }
153
+ }
154
+ // A subtotal in its currency. A part that requires a quote is named, and a price that is only a
155
+ // quote reads "Contact sales".
156
+ function renderMoney(state, price) {
157
+ const quote = price.requiresQuote;
158
+ let amount = formatSubtotal(price.subtotal, price.currency, state.locale);
159
+ if (quote && price.subtotal === 0)
160
+ amount = t(state, 'pricing.contactSales');
161
+ let partial = nothing;
162
+ if (quote && price.subtotal !== 0) {
163
+ partial = tpl ` <small>${t(state, 'pricing.requiresQuote')}</small>`;
164
+ }
165
+ // What an amount is per: the plan's own unit, the same text the plan card shows. A real
166
+ // billing period is named too; the one invented for a pricing that declares none is not,
167
+ // since "Base" tells the reader nothing the author ever chose.
168
+ let period = nothing;
169
+ if (price.unit) {
170
+ period = tpl ` <small>${t(state, 'pricing.per', { unit: price.unit })}</small>`;
171
+ }
172
+ else if (price.billingPeriod && price.billingPeriod.synthesized !== true) {
173
+ period = tpl ` <small>${price.billingPeriod.label}</small>`;
174
+ }
175
+ return tpl `${amount}${partial}${period}`;
176
+ }
177
+ function renderDifference(state) {
178
+ const { price } = state.comparison;
179
+ if (price.difference === undefined)
180
+ return nothing;
181
+ const signed = formatSignedCurrency(price.difference, price.after.currency, state.locale);
182
+ // The reason the screen exists, so it is marked for the stylesheet and carries its direction.
183
+ // "same" is a real outcome: a plan swap can leave the bill untouched.
184
+ const direction = price.difference > 0 ? 'increase' : price.difference < 0 ? 'decrease' : 'same';
185
+ return tpl `<div data-pr-part="price-difference" data-direction=${direction}>
186
+ <dt>${t(state, 'pricing.reviewPriceDifference')}</dt>
187
+ <dd>${signed}</dd>
188
+ </div>`;
189
+ }
190
+ function renderPrice(state) {
191
+ const { price } = state.comparison;
192
+ let omitted = nothing;
193
+ if (price.differenceOmitted) {
194
+ const text = t(state, differenceKeys[price.differenceOmitted]);
195
+ omitted = tpl `<p class="pr-cr-note">${text}</p>`;
196
+ }
197
+ let billing = nothing;
198
+ if (price.billingPeriodChanged) {
199
+ const text = t(state, 'pricing.reviewBillingChange', {
200
+ before: periodName(price.before.billingPeriod),
201
+ after: periodName(price.after.billingPeriod),
202
+ });
203
+ billing = tpl `<p class="pr-cr-note">${text}</p>`;
204
+ }
205
+ const body = tpl `<dl class="pr-cr-price">
206
+ <div>
207
+ <dt>${t(state, 'pricing.reviewPriceBefore')}</dt>
208
+ <dd>${renderMoney(state, price.before)}</dd>
209
+ </div>
210
+ <div>
211
+ <dt>${t(state, 'pricing.reviewPriceAfter')}</dt>
212
+ <dd>${renderMoney(state, price.after)}</dd>
213
+ </div>
214
+ ${renderDifference(state)}
215
+ </dl>
216
+ ${omitted}
217
+ ${billing}`;
218
+ return reviewSection(state, 'review-price', 'pricing.reviewPrice', body);
219
+ }
220
+ function addOnRow(state, item, labelKey, tone, reason) {
221
+ const detail = item.quantity > 1 ? `× ${formatNumber(item.quantity, state.locale)}` : undefined;
222
+ return changeRow(item.name, { detail, badge: badge(t(state, labelKey), tone), reason });
223
+ }
224
+ function removalReason(state, item) {
225
+ const plan = planName(state.comparison.plan.after);
226
+ return t(state, removalKeys[item.reason], { plan });
227
+ }
228
+ function renderAddOns(state) {
229
+ const { addOns } = state.comparison;
230
+ const added = 'pricing.reviewAddOnAdded';
231
+ const removed = 'pricing.reviewAddOnRemoved';
232
+ const rows = [];
233
+ for (const item of addOns.added)
234
+ rows.push(addOnRow(state, item, added, 'success'));
235
+ for (const item of addOns.removedByUser)
236
+ rows.push(addOnRow(state, item, removed, 'warning'));
237
+ for (const item of addOns.removedByReconciliation) {
238
+ rows.push(addOnRow(state, item, removed, 'warning', removalReason(state, item)));
239
+ }
240
+ for (const item of addOns.quantityChanged) {
241
+ const before = formatNumber(item.before, state.locale);
242
+ const after = formatNumber(item.after, state.locale);
243
+ const detail = t(state, 'pricing.reviewQuantityChange', { before, after });
244
+ rows.push(changeRow(item.name, { detail }));
245
+ }
246
+ if (rows.length === 0)
247
+ return nothing;
248
+ return reviewSection(state, 'review-add-ons', 'pricing.reviewAddOns', list(rows));
249
+ }
250
+ function renderFeatures(state) {
251
+ const { features } = state.comparison;
252
+ const format = (value) => formatComparedValue(value, state.messages, state.locale);
253
+ const gained = badge(t(state, 'pricing.reviewFeatureGained'), 'success');
254
+ const lost = badge(t(state, 'pricing.reviewFeatureLost'), 'warning');
255
+ const changed = badge(t(state, 'pricing.reviewFeatureChanged'), 'accent');
256
+ const rows = [];
257
+ for (const item of features.gained)
258
+ rows.push(changeRow(item.name, { badge: gained }));
259
+ for (const item of features.lost)
260
+ rows.push(changeRow(item.name, { badge: lost }));
261
+ for (const item of features.changed) {
262
+ const detail = arrow(state, format(item.before), format(item.after));
263
+ rows.push(changeRow(item.name, { detail, badge: changed }));
264
+ }
265
+ if (rows.length === 0)
266
+ return nothing;
267
+ return reviewSection(state, 'review-features', 'pricing.reviewFeatures', list(rows));
268
+ }
269
+ function renderLimits(state) {
270
+ const rows = [];
271
+ for (const item of state.comparison.usageLimits.changed) {
272
+ const format = (value) => formatComparedValue(value, state.messages, state.locale, item.unit);
273
+ const moved = arrow(state, format(item.before), format(item.after));
274
+ // The delta is the part a reader scans for, and its sign is the whole message: more of a
275
+ // limit is a gain, less of one is what they are giving up. It carries its direction so the
276
+ // stylesheet can tone it without the reader parsing a minus sign.
277
+ let detail = moved;
278
+ if (item.delta !== undefined) {
279
+ const sign = formatNumber(item.delta, state.locale, { signDisplay: 'exceptZero' });
280
+ const unit = item.unit
281
+ ? ` ${formatUnit(item.unit, Math.abs(item.delta), intlLocale(state.locale))}`
282
+ : '';
283
+ const direction = item.delta > 0 ? 'increase' : item.delta < 0 ? 'decrease' : 'same';
284
+ detail = tpl `${moved} <span class="pr-cr-row__delta" data-direction=${direction}
285
+ >(${sign}${unit})</span
286
+ >`;
287
+ }
288
+ let warning;
289
+ if (item.exceedsCurrentUsage && item.currentUsage !== undefined) {
290
+ const usage = format({ kind: 'number', value: item.currentUsage });
291
+ warning = t(state, 'pricing.reviewLimitExceeded', { usage });
292
+ }
293
+ rows.push(changeRow(item.name, { detail, warning }));
294
+ }
295
+ if (rows.length === 0)
296
+ return nothing;
297
+ return reviewSection(state, 'review-limits', 'pricing.reviewLimits', list(rows));
298
+ }
299
+ function renderUnchanged(state) {
300
+ const { addOns, features, usageLimits } = state.comparison;
301
+ const counts = [addOns.unchanged, features.unchanged, usageLimits.unchanged];
302
+ if (counts.every((ids) => ids.length === 0))
303
+ return nothing;
304
+ // Add-ons are left out of this line: what a reader checks here is whether the entitlements
305
+ // they rely on survive the change, and an add-on they did not touch is not news. The section
306
+ // still appears when only add-ons are unchanged, because something did stay the same.
307
+ const [, featureCount = 0, limitCount = 0] = counts.map((ids) => ids.length);
308
+ const summary = t(state, 'pricing.reviewUnchangedSummary', {
309
+ features: formatNumber(featureCount, state.locale),
310
+ limits: formatNumber(limitCount, state.locale),
311
+ });
312
+ const body = tpl `<p>${summary}</p>`;
313
+ return reviewSection(state, 'review-unchanged', 'pricing.reviewUnchanged', body);
314
+ }
315
+ function renderNotices(state) {
316
+ if (state.notices.length === 0)
317
+ return nothing;
318
+ const items = state.notices.map((notice) => tpl `<li class="pr-cr-notice">${notice}</li>`);
319
+ return reviewSection(state, 'review-notices', 'pricing.reviewNotices', list(items));
320
+ }
321
+ function renderError(state) {
322
+ if (state.error === undefined)
323
+ return nothing;
324
+ return tpl `<div class="pr-cr-error" data-pr-part="review-error" role="alert">
325
+ <strong>${t(state, 'pricing.reviewErrorTitle')}</strong>
326
+ ${state.error}
327
+ </div>`;
328
+ }
329
+ function renderActions(state) {
330
+ if (state.hiddenSections?.has('actions'))
331
+ return nothing;
332
+ const status = state.pending ? t(state, 'pricing.reviewPending') : nothing;
333
+ const confirm = state.confirmLabel || t(state, 'pricing.reviewConfirm');
334
+ return tpl `<div data-pr-part="change-actions">
335
+ <p class="pr-cr-status" data-pr-part="review-status" role="status">${status}</p>
336
+ <button
337
+ class="pr-button pr-button--secondary"
338
+ data-pr-part="review-back"
339
+ type="button"
340
+ @click=${state.onBack}
341
+ >${t(state, 'pricing.reviewBack')}</button>
342
+ <button
343
+ class="pr-button pr-button--primary"
344
+ data-pr-part="review-confirm"
345
+ type="button"
346
+ aria-disabled=${state.blocked ? 'true' : nothing}
347
+ aria-busy=${state.pending ? 'true' : nothing}
348
+ @click=${state.onConfirm}
349
+ >${confirm}</button>
350
+ </div>`;
351
+ }
352
+ // The sections in the order of design.md, each only when it has content. Nothing changes: a note.
353
+ function renderBody(state) {
354
+ if (state.comparison.relation === 'current') {
355
+ const note = t(state, 'pricing.reviewNoChanges');
356
+ return tpl `<p class="pr-cr-notice" data-pr-part="review-no-changes">${note}</p>`;
357
+ }
358
+ return [
359
+ renderPlan(state),
360
+ renderPrice(state),
361
+ renderAddOns(state),
362
+ renderFeatures(state),
363
+ renderLimits(state),
364
+ renderUnchanged(state),
365
+ ];
366
+ }
367
+ // The root with its title, the body, the notices of the host, the alert region and the actions
368
+ // with the status region.
369
+ export function renderChangeReview(state) {
370
+ const titleId = `${state.idPrefix}-title`;
371
+ const title = t(state, 'pricing.reviewTitle');
372
+ // A host that hides the title labels the region with its own heading, so the reference goes with
373
+ // it: aria-labelledby pointing at an absent id leaves the section claiming a name it has not.
374
+ const untitled = state.hiddenSections?.has('title') === true;
375
+ const header = untitled
376
+ ? nothing
377
+ : tpl `<header class="pr-cr-header">
378
+ <h2 id=${titleId} data-pr-part="review-title" tabindex="-1">${title}</h2>
379
+ </header>`;
380
+ return tpl `<section
381
+ data-pr-part="change-review"
382
+ aria-labelledby=${untitled ? nothing : titleId}
383
+ >
384
+ ${header}
385
+ ${renderBody(state)}
386
+ ${renderNotices(state)}
387
+ ${renderError(state)}
388
+ ${renderActions(state)}
389
+ </section>`;
390
+ }
391
+ //# sourceMappingURL=change-review-renderers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-review-renderers.js","sourceRoot":"","sources":["../../src/element/change-review-renderers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { type NormalizedPlan, type PricingCta, type PricingViewModel, type SelectionRelation } from '../core/index.js';
2
+ export declare const REVIEW_CHANGE_ACTION_ID = "review-change";
3
+ export declare const CONTACT_SALES_ACTION_ID = "contact-sales";
4
+ /** What the CTAs need to know about the customer's contract and the host's pending work. */
5
+ export interface PricingActionContext {
6
+ /** The current plan id, only when the loaded pricing defines it. */
7
+ currentPlanId?: string;
8
+ pendingActionId?: string;
9
+ }
10
+ /** The summary action, computed by the element and rendered by `renderSummary`. */
11
+ export interface SummaryActionState {
12
+ actionId: typeof REVIEW_CHANGE_ACTION_ID | typeof CONTACT_SALES_ACTION_ID;
13
+ label: string;
14
+ relation: SelectionRelation;
15
+ disabled: boolean;
16
+ }
17
+ export interface PlanCtaView {
18
+ actionId: string;
19
+ /** What the button shows. Short: the card already names the plan. */
20
+ label: string;
21
+ /** The full phrase, naming the plan, for a reader who meets the button out of context. */
22
+ ariaLabel?: string;
23
+ /** Expanded and safe; absent for a button. */
24
+ href?: string;
25
+ /** The expanded `href` of the configured CTA, safe or not, for the `pricing-action` detail. */
26
+ eventHref?: string;
27
+ target?: '_self' | '_blank';
28
+ kind: 'primary' | 'secondary';
29
+ metadata?: PricingCta['metadata'];
30
+ /** Set only while the current plan is known. */
31
+ relation?: SelectionRelation;
32
+ /** The current plan's CTA: a button with the `disabled` attribute. */
33
+ disabled: boolean;
34
+ /** `aria-disabled`: any action of the host is pending. */
35
+ ariaDisabled: boolean;
36
+ /** This control is the pending action. */
37
+ pending: boolean;
38
+ }
39
+ export declare function isPendingActionId(pendingActionId: string | undefined, actionId: string): boolean;
40
+ export declare function expandCtaHref(href: string | undefined, planId: string): string | undefined;
41
+ /** The label, link and state of one plan CTA. */
42
+ export declare function planCtaView(viewModel: PricingViewModel, plan: NormalizedPlan, context: PricingActionContext): PlanCtaView;
43
+ /** The summary action for a known current plan and the relation of the selection in effect. */
44
+ export declare function summaryActionState(viewModel: PricingViewModel, relation: SelectionRelation): SummaryActionState;
45
+ //# sourceMappingURL=cta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cta.d.ts","sourceRoot":"","sources":["../../src/element/cta.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAG1B,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AACvD,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AAEvD,4FAA4F;AAC5F,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,uBAAuB,GAAG,OAAO,uBAAuB,CAAC;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,SAAS,GAAG,WAAW,CAAC;IAC9B,QAAQ,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;IAClC,gDAAgD;IAChD,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEhG;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1F;AAqBD,iDAAiD;AACjD,wBAAgB,WAAW,CACzB,SAAS,EAAE,gBAAgB,EAC3B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,oBAAoB,GAC5B,WAAW,CAqDb;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,EAAE,iBAAiB,GAC1B,kBAAkB,CAgBpB"}
@@ -0,0 +1,90 @@
1
+ import { formatCurrency, getPlanCta, isSafeLink, translate, } from '../core/index.js';
2
+ import { planRelation } from '../core/plan-relation.js';
3
+ export const REVIEW_CHANGE_ACTION_ID = 'review-change';
4
+ export const CONTACT_SALES_ACTION_ID = 'contact-sales';
5
+ export function isPendingActionId(pendingActionId, actionId) {
6
+ return pendingActionId !== undefined && pendingActionId === actionId;
7
+ }
8
+ // The plan id is encoded here, before the caller checks the safety of the result.
9
+ export function expandCtaHref(href, planId) {
10
+ return href?.split('{planId}').join(encodeURIComponent(planId));
11
+ }
12
+ function relationLabel(viewModel, plan, cta, relation) {
13
+ const price = viewModel.resolved.planPrices[plan.id];
14
+ if (price?.kind === 'label' || price?.kind === 'error') {
15
+ return cta?.labels?.contactSales ?? translate(viewModel.messages, 'pricing.contactSales');
16
+ }
17
+ if (relation === 'upgrade') {
18
+ return cta?.labels?.upgrade ?? translate(viewModel.messages, 'pricing.upgrade');
19
+ }
20
+ if (relation === 'downgrade') {
21
+ return cta?.labels?.downgrade ?? translate(viewModel.messages, 'pricing.downgrade');
22
+ }
23
+ return undefined;
24
+ }
25
+ /** The label, link and state of one plan CTA. */
26
+ export function planCtaView(viewModel, plan, context) {
27
+ const cta = getPlanCta(viewModel.presentation, plan.id);
28
+ const actionId = cta?.id ?? `choose-${plan.id}`;
29
+ const { currentPlanId, pendingActionId } = context;
30
+ const kind = cta?.kind === 'secondary' ? 'secondary' : 'primary';
31
+ // an unsafe expanded link renders as a button
32
+ const eventHref = expandCtaHref(cta?.href, plan.id) || undefined;
33
+ const href = isSafeLink(eventHref) ? eventHref : undefined;
34
+ const base = {
35
+ actionId,
36
+ kind,
37
+ ...(cta?.target ? { target: cta.target } : {}),
38
+ ...(cta?.metadata ? { metadata: cta.metadata } : {}),
39
+ ...(eventHref ? { eventHref } : {}),
40
+ ariaDisabled: pendingActionId !== undefined,
41
+ pending: isPendingActionId(pendingActionId, actionId),
42
+ };
43
+ const defaultLabel = () => cta?.label ?? translate(viewModel.messages, 'pricing.choosePlan', { plan: plan.name });
44
+ if (currentPlanId === undefined) {
45
+ return { ...base, label: defaultLabel(), ...(href ? { href } : {}), disabled: false };
46
+ }
47
+ const relation = planRelation(viewModel.pricing, currentPlanId, plan.id);
48
+ if (relation === 'current') {
49
+ return {
50
+ ...base,
51
+ label: cta?.labels?.current ?? translate(viewModel.messages, 'pricing.currentPlanCta'),
52
+ relation,
53
+ disabled: true,
54
+ };
55
+ }
56
+ const label = relationLabel(viewModel, plan, cta, relation) ?? defaultLabel();
57
+ // The visible text is short because the card names the plan right above it. A button read on
58
+ // its own still has to say which plan it changes to, so the full phrase lives in aria-label.
59
+ const longKey = relation === 'upgrade'
60
+ ? 'pricing.upgradeTo'
61
+ : relation === 'downgrade'
62
+ ? 'pricing.downgradeTo'
63
+ : undefined;
64
+ const ariaLabel = longKey
65
+ ? translate(viewModel.messages, longKey, { plan: plan.name })
66
+ : undefined;
67
+ return {
68
+ ...base,
69
+ label,
70
+ ...(ariaLabel && ariaLabel !== label ? { ariaLabel } : {}),
71
+ ...(href ? { href } : {}),
72
+ relation,
73
+ disabled: false,
74
+ };
75
+ }
76
+ /** The summary action for a known current plan and the relation of the selection in effect. */
77
+ export function summaryActionState(viewModel, relation) {
78
+ const quote = viewModel.resolved.requiresQuote;
79
+ return {
80
+ actionId: quote ? CONTACT_SALES_ACTION_ID : REVIEW_CHANGE_ACTION_ID,
81
+ label: quote
82
+ ? translate(viewModel.messages, 'pricing.contactSales')
83
+ : translate(viewModel.messages, 'pricing.reviewChangeCta', {
84
+ total: formatCurrency(viewModel.resolved.subtotal, viewModel.pricing.metadata.currency, viewModel.locale),
85
+ }),
86
+ relation,
87
+ disabled: relation === 'current',
88
+ };
89
+ }
90
+ //# sourceMappingURL=cta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cta.js","sourceRoot":"","sources":["../../src/element/cta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type NormalizedPricing, type PricingDiagnostic, type PricingSelection } from '../core/index.js';
2
+ /** What the element marks as current, derived from the loaded pricing. */
3
+ export interface CurrentContext {
4
+ /** The current plan id; always a plan id of the pricing. */
5
+ planId: string;
6
+ /** The add-ons still selected after reconciliation on the current plan. */
7
+ addOnIds: ReadonlySet<string>;
8
+ }
9
+ /** The current side of Decision 2 (steps 1 to 4), or `undefined` without a known current plan. */
10
+ export declare function deriveCurrentContext(pricing: NormalizedPricing, current: Partial<PricingSelection> | undefined): CurrentContext | undefined;
11
+ /** One warning per plan id or add-on id of the current selection that the pricing does not define. */
12
+ export declare function currentSelectionDiagnostics(pricing: NormalizedPricing, current: Partial<PricingSelection> | undefined): PricingDiagnostic[];
13
+ //# sourceMappingURL=current-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-selection.d.ts","sourceRoot":"","sources":["../../src/element/current-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAE1B,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC/B;AAKD,kGAAkG;AAGlG,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,GAC7C,cAAc,GAAG,SAAS,CAoB5B;AAED,sGAAsG;AACtG,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,GAC7C,iBAAiB,EAAE,CAuBrB"}
@@ -0,0 +1,57 @@
1
+ import { isRecord, mergeSelection, reconcileSelection, } from '../core/index.js';
2
+ const hasId = (items, id) => items.some((item) => item.id === id);
3
+ /** The current side of Decision 2 (steps 1 to 4), or `undefined` without a known current plan. */
4
+ // Without a known plan, reconciling would use the default plan, which is not the customer's, so no
5
+ // add-on is marked either. Unknown add-on ids are ignored.
6
+ export function deriveCurrentContext(pricing, current) {
7
+ if (!isRecord(current))
8
+ return undefined;
9
+ const planId = typeof current.planId === 'string' ? current.planId : undefined;
10
+ if (planId === undefined || !hasId(pricing.plans, planId))
11
+ return undefined;
12
+ const addOns = {};
13
+ if (isRecord(current.addOns)) {
14
+ for (const [id, entry] of Object.entries(current.addOns)) {
15
+ if (!hasId(pricing.addOns, id) || !isRecord(entry) || typeof entry.selected !== 'boolean') {
16
+ continue;
17
+ }
18
+ const quantity = typeof entry.quantity === 'number' ? entry.quantity : 0;
19
+ addOns[id] = { selected: entry.selected, quantity };
20
+ }
21
+ }
22
+ const { selection } = reconcileSelection(pricing, mergeSelection(pricing, { planId, addOns }));
23
+ const addOnIds = new Set();
24
+ for (const [id, entry] of Object.entries(selection.addOns)) {
25
+ if (entry.selected)
26
+ addOnIds.add(id);
27
+ }
28
+ return { planId, addOnIds };
29
+ }
30
+ /** One warning per plan id or add-on id of the current selection that the pricing does not define. */
31
+ export function currentSelectionDiagnostics(pricing, current) {
32
+ if (!isRecord(current))
33
+ return [];
34
+ const diagnostics = [];
35
+ if (typeof current.planId === 'string' && !hasId(pricing.plans, current.planId)) {
36
+ diagnostics.push({
37
+ code: 'PR_CURRENT_SELECTION_UNKNOWN_PLAN',
38
+ severity: 'warning',
39
+ message: `Unknown plan "${current.planId}" in currentSelection; no plan is marked as current.`,
40
+ path: 'currentSelection.planId',
41
+ });
42
+ }
43
+ if (isRecord(current.addOns)) {
44
+ for (const id of Object.keys(current.addOns)) {
45
+ if (hasId(pricing.addOns, id))
46
+ continue;
47
+ diagnostics.push({
48
+ code: 'PR_CURRENT_SELECTION_UNKNOWN_ADDON',
49
+ severity: 'warning',
50
+ message: `Unknown add-on "${id}" in currentSelection; it is ignored.`,
51
+ path: `currentSelection.addOns.${id}`,
52
+ });
53
+ }
54
+ }
55
+ return diagnostics;
56
+ }
57
+ //# sourceMappingURL=current-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-selection.js","sourceRoot":"","sources":["../../src/element/current-selection.ts"],"names":[],"mappings":"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"}