@sankhyatronics/sankhya-cms 0.1.18 → 0.3.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 (216) hide show
  1. package/custom-elements.json +11634 -3153
  2. package/dist/attention-panel/index.js +2 -2
  3. package/dist/bar-chart/index.js +2 -2
  4. package/dist/bento-grid/index.d.ts +33 -1
  5. package/dist/bento-grid/index.js +4 -3
  6. package/dist/booking/index.d.ts +309 -0
  7. package/dist/booking/index.js +20 -0
  8. package/dist/booking/index.js.map +1 -0
  9. package/dist/card/index.d.ts +3 -4
  10. package/dist/card/index.js +2 -2
  11. package/dist/carousel/index.d.ts +20 -6
  12. package/dist/carousel/index.js +3 -3
  13. package/dist/chip/index.js +2 -2
  14. package/dist/{chunk-JQY2G4QK.js → chunk-2GTR37PU.js} +2 -2
  15. package/dist/chunk-2GTR37PU.js.map +1 -0
  16. package/dist/{chunk-FLY7TNRT.js → chunk-2HMOX253.js} +128 -25
  17. package/dist/chunk-2HMOX253.js.map +1 -0
  18. package/dist/{chunk-DH53HZPB.js → chunk-37KFBKPD.js} +32 -4
  19. package/dist/chunk-37KFBKPD.js.map +1 -0
  20. package/dist/{chunk-ALI26WOB.js → chunk-4XPKKYVZ.js} +54 -15
  21. package/dist/chunk-4XPKKYVZ.js.map +1 -0
  22. package/dist/chunk-535QAZ6A.js +236 -0
  23. package/dist/chunk-535QAZ6A.js.map +1 -0
  24. package/dist/{chunk-AKJQ345O.js → chunk-5AWGZQWU.js} +2 -2
  25. package/dist/chunk-6OZKZG5J.js +300 -0
  26. package/dist/chunk-6OZKZG5J.js.map +1 -0
  27. package/dist/chunk-AYNT7G3J.js +805 -0
  28. package/dist/chunk-AYNT7G3J.js.map +1 -0
  29. package/dist/chunk-BEWX3E6P.js +32 -0
  30. package/dist/chunk-BEWX3E6P.js.map +1 -0
  31. package/dist/chunk-CZUGPKXV.js +275 -0
  32. package/dist/chunk-CZUGPKXV.js.map +1 -0
  33. package/dist/{chunk-LEXZKGQX.js → chunk-DJFWZOZM.js} +107 -11
  34. package/dist/chunk-DJFWZOZM.js.map +1 -0
  35. package/dist/chunk-DPANTC3O.js +1714 -0
  36. package/dist/chunk-DPANTC3O.js.map +1 -0
  37. package/dist/chunk-EKZFFY45.js +426 -0
  38. package/dist/chunk-EKZFFY45.js.map +1 -0
  39. package/dist/chunk-ER7MCPDV.js +7 -0
  40. package/dist/chunk-ER7MCPDV.js.map +1 -0
  41. package/dist/chunk-EW2RK5Y6.js +383 -0
  42. package/dist/chunk-EW2RK5Y6.js.map +1 -0
  43. package/dist/{chunk-BLQV2YMO.js → chunk-EZOKT7NJ.js} +2 -2
  44. package/dist/chunk-FF2LWEF3.js +386 -0
  45. package/dist/chunk-FF2LWEF3.js.map +1 -0
  46. package/dist/chunk-FOUK3NHR.js +7 -0
  47. package/dist/chunk-FOUK3NHR.js.map +1 -0
  48. package/dist/chunk-GB4NGCZI.js +181 -0
  49. package/dist/chunk-GB4NGCZI.js.map +1 -0
  50. package/dist/{chunk-7NLZND6Q.js → chunk-GOFSMD6W.js} +35 -11
  51. package/dist/chunk-GOFSMD6W.js.map +1 -0
  52. package/dist/{chunk-6D6ROU7X.js → chunk-IGMVF3KK.js} +2 -2
  53. package/dist/{chunk-INMSEYT6.js → chunk-IVPVYVWG.js} +2 -2
  54. package/dist/chunk-JURCF4OD.js +381 -0
  55. package/dist/chunk-JURCF4OD.js.map +1 -0
  56. package/dist/{chunk-FZ36KL73.js → chunk-KZ7PMFWS.js} +11 -1
  57. package/dist/chunk-KZ7PMFWS.js.map +1 -0
  58. package/dist/{chunk-EMCQBT3S.js → chunk-LO6TZBGD.js} +129 -35
  59. package/dist/chunk-LO6TZBGD.js.map +1 -0
  60. package/dist/chunk-MFRGAT6D.js +860 -0
  61. package/dist/chunk-MFRGAT6D.js.map +1 -0
  62. package/dist/chunk-MIBVQXRU.js +1098 -0
  63. package/dist/chunk-MIBVQXRU.js.map +1 -0
  64. package/dist/{chunk-35TX3AOA.js → chunk-NABSRUXG.js} +2 -2
  65. package/dist/{chunk-R5G6KFTJ.js → chunk-NXKDZWS7.js} +44 -6
  66. package/dist/chunk-NXKDZWS7.js.map +1 -0
  67. package/dist/{chunk-KMU6ABQF.js → chunk-NXUS2CH7.js} +14 -1
  68. package/dist/chunk-NXUS2CH7.js.map +1 -0
  69. package/dist/{chunk-GJO3RGOB.js → chunk-OCPGOLVF.js} +56 -11
  70. package/dist/chunk-OCPGOLVF.js.map +1 -0
  71. package/dist/chunk-OEOE6IFY.js +628 -0
  72. package/dist/chunk-OEOE6IFY.js.map +1 -0
  73. package/dist/{chunk-SMLR736E.js → chunk-PV6OCXXN.js} +2 -2
  74. package/dist/chunk-PV6OCXXN.js.map +1 -0
  75. package/dist/chunk-Q66CJO3P.js +534 -0
  76. package/dist/chunk-Q66CJO3P.js.map +1 -0
  77. package/dist/{chunk-MG2VTZA4.js → chunk-QS3ANOWN.js} +2 -2
  78. package/dist/{chunk-ZN4LIKVG.js → chunk-QSJ543V2.js} +2 -2
  79. package/dist/chunk-QSJ543V2.js.map +1 -0
  80. package/dist/{chunk-FML6AG2V.js → chunk-RANC3OI6.js} +2 -2
  81. package/dist/chunk-T4BIN37Q.js +32 -0
  82. package/dist/chunk-T4BIN37Q.js.map +1 -0
  83. package/dist/{chunk-YVTFQHVY.js → chunk-TEAZZPDQ.js} +2 -2
  84. package/dist/chunk-TJK55CJ2.js +49 -0
  85. package/dist/chunk-TJK55CJ2.js.map +1 -0
  86. package/dist/chunk-UKIVLF7S.js +32 -0
  87. package/dist/chunk-UKIVLF7S.js.map +1 -0
  88. package/dist/chunk-V4R7PYP5.js +905 -0
  89. package/dist/chunk-V4R7PYP5.js.map +1 -0
  90. package/dist/{chunk-AG2COC64.js → chunk-VVYQ2DDS.js} +2 -2
  91. package/dist/chunk-WAMOAAMN.js +16 -0
  92. package/dist/chunk-WAMOAAMN.js.map +1 -0
  93. package/dist/chunk-WAYHHL4J.js +1234 -0
  94. package/dist/chunk-WAYHHL4J.js.map +1 -0
  95. package/dist/{chunk-YQ42EBK5.js → chunk-WH6HE6SY.js} +10 -4
  96. package/dist/chunk-WH6HE6SY.js.map +1 -0
  97. package/dist/chunk-XP6DI3VY.js +180 -0
  98. package/dist/chunk-XP6DI3VY.js.map +1 -0
  99. package/dist/{chunk-5EBWMPJS.js → chunk-XS6LNTSH.js} +76 -14
  100. package/dist/chunk-XS6LNTSH.js.map +1 -0
  101. package/dist/{chunk-WT4OATGP.js → chunk-ZKFCDVB4.js} +16 -7
  102. package/dist/chunk-ZKFCDVB4.js.map +1 -0
  103. package/dist/cms-theme.css +29 -0
  104. package/dist/contact-form/index.d.ts +220 -0
  105. package/dist/contact-form/index.js +16 -0
  106. package/dist/contact-form/index.js.map +1 -0
  107. package/dist/content-block/index.d.ts +39 -4
  108. package/dist/content-block/index.js +8 -4
  109. package/dist/cookie-consent/index.d.ts +60 -12
  110. package/dist/cookie-consent/index.js +3 -2
  111. package/dist/date-tile/index.js +2 -2
  112. package/dist/dropdown/index.d.ts +29 -11
  113. package/dist/dropdown/index.js +4 -2
  114. package/dist/feature-split/index.d.ts +6 -1
  115. package/dist/feature-split/index.js +5 -4
  116. package/dist/features-section/index.d.ts +53 -9
  117. package/dist/features-section/index.js +6 -5
  118. package/dist/flow/index.d.ts +35 -0
  119. package/dist/flow/index.js +9 -0
  120. package/dist/flow/index.js.map +1 -0
  121. package/dist/footer/index.d.ts +13 -0
  122. package/dist/footer/index.js +2 -2
  123. package/dist/header/index.d.ts +135 -16
  124. package/dist/header/index.js +3 -2
  125. package/dist/hero/index.d.ts +92 -8
  126. package/dist/hero/index.js +6 -3
  127. package/dist/icon-button/index.d.ts +7 -7
  128. package/dist/icon-button/index.js +2 -2
  129. package/dist/index.d.ts +12 -3
  130. package/dist/index.js +121 -47
  131. package/dist/items-accordion/index.d.ts +9 -0
  132. package/dist/items-accordion/index.js +4 -3
  133. package/dist/kpi-card/index.js +2 -2
  134. package/dist/markdown/index.d.ts +19 -2
  135. package/dist/markdown/index.js +3 -2
  136. package/dist/mega-menu/index.d.ts +95 -7
  137. package/dist/mega-menu/index.js +7 -2
  138. package/dist/menu-grid/index.d.ts +2 -3
  139. package/dist/menu-grid/index.js +2 -2
  140. package/dist/menu-grid-item/index.d.ts +6 -8
  141. package/dist/menu-grid-item/index.js +2 -2
  142. package/dist/menu-item/index.d.ts +11 -7
  143. package/dist/menu-item/index.js +3 -2
  144. package/dist/plan-cards/index.d.ts +69 -0
  145. package/dist/plan-cards/index.js +13 -0
  146. package/dist/plan-cards/index.js.map +1 -0
  147. package/dist/product-nav/index.d.ts +127 -0
  148. package/dist/product-nav/index.js +11 -0
  149. package/dist/product-nav/index.js.map +1 -0
  150. package/dist/promo-banner/index.d.ts +42 -4
  151. package/dist/promo-banner/index.js +6 -3
  152. package/dist/quote-dialog/index.d.ts +174 -0
  153. package/dist/quote-dialog/index.js +22 -0
  154. package/dist/quote-dialog/index.js.map +1 -0
  155. package/dist/schema/index.d.ts +11 -0
  156. package/dist/schema/index.js +9 -0
  157. package/dist/schema/index.js.map +1 -1
  158. package/dist/screenshot-tour/index.d.ts +54 -0
  159. package/dist/screenshot-tour/index.js +9 -0
  160. package/dist/screenshot-tour/index.js.map +1 -0
  161. package/dist/section/index.d.ts +3 -0
  162. package/dist/section/index.js +3 -3
  163. package/dist/segmented-control/index.js +2 -2
  164. package/dist/select/index.js +2 -2
  165. package/dist/shared/styles.d.ts +7 -1
  166. package/dist/shared/styles.js +3 -1
  167. package/dist/stats/index.js +2 -2
  168. package/dist/steps/index.d.ts +45 -0
  169. package/dist/steps/index.js +11 -0
  170. package/dist/steps/index.js.map +1 -0
  171. package/dist/tag-strip/index.d.ts +33 -0
  172. package/dist/tag-strip/index.js +9 -0
  173. package/dist/tag-strip/index.js.map +1 -0
  174. package/dist/testimonials/index.d.ts +9 -1
  175. package/dist/testimonials/index.js +3 -3
  176. package/dist/tooltip/index.js +2 -2
  177. package/package.json +38 -2
  178. package/dist/chunk-5EBWMPJS.js.map +0 -1
  179. package/dist/chunk-76AVBQUA.js +0 -252
  180. package/dist/chunk-76AVBQUA.js.map +0 -1
  181. package/dist/chunk-7NLZND6Q.js.map +0 -1
  182. package/dist/chunk-ALI26WOB.js.map +0 -1
  183. package/dist/chunk-BMHMNXIA.js +0 -158
  184. package/dist/chunk-BMHMNXIA.js.map +0 -1
  185. package/dist/chunk-DH53HZPB.js.map +0 -1
  186. package/dist/chunk-EMCQBT3S.js.map +0 -1
  187. package/dist/chunk-FLY7TNRT.js.map +0 -1
  188. package/dist/chunk-FZ36KL73.js.map +0 -1
  189. package/dist/chunk-GJO3RGOB.js.map +0 -1
  190. package/dist/chunk-J6EO4R73.js +0 -633
  191. package/dist/chunk-J6EO4R73.js.map +0 -1
  192. package/dist/chunk-JKBWZLG4.js +0 -348
  193. package/dist/chunk-JKBWZLG4.js.map +0 -1
  194. package/dist/chunk-JQY2G4QK.js.map +0 -1
  195. package/dist/chunk-KMU6ABQF.js.map +0 -1
  196. package/dist/chunk-LEXZKGQX.js.map +0 -1
  197. package/dist/chunk-MSSH3HFK.js +0 -205
  198. package/dist/chunk-MSSH3HFK.js.map +0 -1
  199. package/dist/chunk-R5G6KFTJ.js.map +0 -1
  200. package/dist/chunk-SMLR736E.js.map +0 -1
  201. package/dist/chunk-VWS53WY4.js +0 -157
  202. package/dist/chunk-VWS53WY4.js.map +0 -1
  203. package/dist/chunk-WT4OATGP.js.map +0 -1
  204. package/dist/chunk-XWB4CKDU.js +0 -708
  205. package/dist/chunk-XWB4CKDU.js.map +0 -1
  206. package/dist/chunk-YQ42EBK5.js.map +0 -1
  207. package/dist/chunk-ZN4LIKVG.js.map +0 -1
  208. /package/dist/{chunk-AKJQ345O.js.map → chunk-5AWGZQWU.js.map} +0 -0
  209. /package/dist/{chunk-BLQV2YMO.js.map → chunk-EZOKT7NJ.js.map} +0 -0
  210. /package/dist/{chunk-6D6ROU7X.js.map → chunk-IGMVF3KK.js.map} +0 -0
  211. /package/dist/{chunk-INMSEYT6.js.map → chunk-IVPVYVWG.js.map} +0 -0
  212. /package/dist/{chunk-35TX3AOA.js.map → chunk-NABSRUXG.js.map} +0 -0
  213. /package/dist/{chunk-MG2VTZA4.js.map → chunk-QS3ANOWN.js.map} +0 -0
  214. /package/dist/{chunk-FML6AG2V.js.map → chunk-RANC3OI6.js.map} +0 -0
  215. /package/dist/{chunk-YVTFQHVY.js.map → chunk-TEAZZPDQ.js.map} +0 -0
  216. /package/dist/{chunk-AG2COC64.js.map → chunk-VVYQ2DDS.js.map} +0 -0
@@ -0,0 +1,805 @@
1
+ import {
2
+ QUOTE_OPEN_EVENT
3
+ } from "./chunk-FOUK3NHR.js";
4
+ import {
5
+ setModalOpen
6
+ } from "./chunk-WAMOAAMN.js";
7
+ import {
8
+ controlBaseStyles,
9
+ fontFamilyStyles
10
+ } from "./chunk-NXUS2CH7.js";
11
+ import {
12
+ __decorateClass
13
+ } from "./chunk-EUXUH3YW.js";
14
+
15
+ // src/quote-dialog/QuoteDialog.ts
16
+ import { LitElement, css, html, nothing, svg } from "lit";
17
+ import { customElement, property, query, state } from "lit/decorators.js";
18
+
19
+ // src/shared/mailto.ts
20
+ var DEFAULT_MAILTO_BODY_LIMIT = 1800;
21
+ var ELLIPSIS = "\u2026";
22
+ function bodyText(fields) {
23
+ return fields.map((field) => `${field.label}: ${field.value.trim() || "-"}`).join("\n");
24
+ }
25
+ function buildMailto({ to, subject, fields, maxBodyLength = DEFAULT_MAILTO_BODY_LIMIT, truncateField }) {
26
+ const url = (body) => `mailto:${to}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
27
+ const full = bodyText(fields);
28
+ if (encodeURIComponent(full).length <= maxBodyLength || fields.length === 0) return url(full);
29
+ const target = Math.min(Math.max(truncateField ?? fields.length - 1, 0), fields.length - 1);
30
+ const chars = Array.from(fields[target].value.trim());
31
+ const withLength = (length) => bodyText(fields.map((field, i) => i === target ? { ...field, value: chars.slice(0, length).join("").trimEnd() + ELLIPSIS } : field));
32
+ let low = 0;
33
+ let high = chars.length;
34
+ while (low < high) {
35
+ const mid = Math.ceil((low + high) / 2);
36
+ if (encodeURIComponent(withLength(mid)).length <= maxBodyLength) low = mid;
37
+ else high = mid - 1;
38
+ }
39
+ return url(withLength(low));
40
+ }
41
+
42
+ // src/quote-dialog/quote-mailto.ts
43
+ function buildQuoteMailto({ to, subject, optionLabel, fields, maxBodyLength }) {
44
+ return buildMailto({ to, subject, fields: [{ label: "Option", value: optionLabel }, ...fields], maxBodyLength });
45
+ }
46
+ function fillTemplate(template, values) {
47
+ return template.replace(/\{(\w+)\}/g, (match, key) => Object.hasOwn(values, key) ? values[key] : match);
48
+ }
49
+
50
+ // src/quote-dialog/QuoteDialog.ts
51
+ var CLOSE_ICON = svg`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6 6 18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></svg>`;
52
+ var SUCCESS_ICON = svg`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 12l5 5L20 7" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>`;
53
+ var QuoteDialog = class extends LitElement {
54
+ constructor() {
55
+ super(...arguments);
56
+ this.title = "";
57
+ this.subtitle = "";
58
+ this.options = [];
59
+ this.optionsLabel = "Choose an option";
60
+ this.nameLabel = "Name";
61
+ this.namePlaceholder = "";
62
+ this.emailLabel = "Email";
63
+ this.emailPlaceholder = "";
64
+ this.companyLabel = "Company";
65
+ this.companyPlaceholder = "";
66
+ this.countLabel = "Number of users";
67
+ this.countPlaceholder = "";
68
+ this.messageLabel = "Message";
69
+ this.messagePlaceholder = "";
70
+ this.consentLabel = "I agree to be contacted about this request.";
71
+ this.submitLabel = "Send request";
72
+ this.successTitle = "Thank you";
73
+ this.successText = "";
74
+ this.closeLabel = "Close";
75
+ this.mailto = "";
76
+ this.subject = "Quote request";
77
+ this.mailtoNote = "";
78
+ this.copyLabel = "Copy address";
79
+ this.copiedLabel = "Address copied";
80
+ this.selected = "";
81
+ this.copied = false;
82
+ this.submitted = false;
83
+ this.errors = {};
84
+ this.submittedOption = "";
85
+ this.trigger = null;
86
+ /** Opens for `st-quote-open` events aimed at this dialog's `id`. */
87
+ this.onOpenRequest = (event) => {
88
+ const detail = event.detail;
89
+ if (!detail || !this.id || detail.dialog !== this.id) return;
90
+ this.trigger = detail.trigger ?? null;
91
+ const known = this.options.some((option) => option.id === detail.option);
92
+ this.selected = known ? detail.option : this.options[0]?.id ?? "";
93
+ this.submitted = false;
94
+ this.copied = false;
95
+ this.errors = {};
96
+ void this.open();
97
+ };
98
+ }
99
+ connectedCallback() {
100
+ super.connectedCallback();
101
+ window.addEventListener(QUOTE_OPEN_EVENT, this.onOpenRequest);
102
+ }
103
+ disconnectedCallback() {
104
+ window.removeEventListener(QUOTE_OPEN_EVENT, this.onOpenRequest);
105
+ setModalOpen(this, false);
106
+ super.disconnectedCallback();
107
+ }
108
+ /** Renders the reset form first, so every open starts empty, then shows the modal. */
109
+ async open() {
110
+ await this.updateComplete;
111
+ this.formEl?.reset();
112
+ if (this.dialogEl && !this.dialogEl.open) {
113
+ this.dialogEl.showModal();
114
+ setModalOpen(this, true);
115
+ }
116
+ this.renderRoot.querySelector("input")?.focus();
117
+ }
118
+ close() {
119
+ this.dialogEl?.close();
120
+ }
121
+ /** Runs on every close (Esc, the close button, the backdrop): focus goes back to the opener. */
122
+ onDialogClose() {
123
+ if (!this.dialogEl?.open) setModalOpen(this, false);
124
+ const trigger = this.trigger;
125
+ this.trigger = null;
126
+ trigger?.focus();
127
+ }
128
+ onBackdropClick(event) {
129
+ if (event.target === this.dialogEl) this.close();
130
+ }
131
+ fieldValue(form, name) {
132
+ const field = form.elements.namedItem(name);
133
+ return field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement ? field.value.trim() : "";
134
+ }
135
+ validate(form) {
136
+ const errors = {};
137
+ const check = (name, label, emailCheck = false) => {
138
+ const field = form.elements.namedItem(name);
139
+ if (!(field instanceof HTMLInputElement)) return;
140
+ const blank = name !== "consent" && field.value.trim() === "";
141
+ if (field.validity.valueMissing || blank) errors[name] = name === "consent" ? "Please accept to continue." : `${label} is required.`;
142
+ else if (emailCheck && field.validity.typeMismatch) errors[name] = "Enter a valid email address.";
143
+ };
144
+ check("name", this.nameLabel);
145
+ check("email", this.emailLabel, true);
146
+ check("company", this.companyLabel);
147
+ if (this.consentLabel) check("consent", this.consentLabel);
148
+ return errors;
149
+ }
150
+ onSubmit(event) {
151
+ event.preventDefault();
152
+ const form = event.currentTarget;
153
+ const errors = this.validate(form);
154
+ this.errors = errors;
155
+ const firstInvalid = ["name", "email", "company", "consent"].find((name) => errors[name]);
156
+ if (firstInvalid) {
157
+ this.renderRoot.querySelector(`#quote-${firstInvalid}`)?.focus();
158
+ return;
159
+ }
160
+ const values = {
161
+ name: this.fieldValue(form, "name"),
162
+ email: this.fieldValue(form, "email"),
163
+ company: this.fieldValue(form, "company"),
164
+ count: this.fieldValue(form, "count"),
165
+ message: this.fieldValue(form, "message"),
166
+ consent: form.elements.namedItem("consent")?.checked ?? false
167
+ };
168
+ const option = this.options.find((item) => item.id === this.selected);
169
+ const optionLabel = option?.label ?? this.selected;
170
+ if (this.mailto) {
171
+ window.location.href = buildQuoteMailto({
172
+ to: this.mailto,
173
+ subject: fillTemplate(this.subject, {
174
+ option: option?.subjectLabel || optionLabel,
175
+ company: values.company,
176
+ name: values.name
177
+ }),
178
+ optionLabel,
179
+ fields: [
180
+ { label: this.nameLabel, value: values.name },
181
+ { label: this.emailLabel, value: values.email },
182
+ { label: this.companyLabel, value: values.company },
183
+ { label: this.countLabel, value: values.count },
184
+ { label: this.messageLabel, value: values.message }
185
+ ]
186
+ });
187
+ }
188
+ this.submittedOption = optionLabel;
189
+ this.submitted = true;
190
+ this.dispatchEvent(
191
+ new CustomEvent("quote-submit", {
192
+ detail: { option: this.selected, ...values },
193
+ bubbles: true,
194
+ composed: true
195
+ })
196
+ );
197
+ void this.updateComplete.then(() => this.renderRoot.querySelector(".done")?.focus());
198
+ }
199
+ renderField(name, label, extra = {}) {
200
+ const error = name === "count" ? void 0 : this.errors[name];
201
+ return html`<div class="field">
202
+ <label class="label" for="quote-${name}">${label}${extra.required ? html`<span aria-hidden="true"> *</span>` : nothing}</label>
203
+ <input
204
+ class="input"
205
+ id="quote-${name}"
206
+ name=${name}
207
+ type=${extra.type ?? "text"}
208
+ part="input"
209
+ autocomplete=${extra.autocomplete ?? "off"}
210
+ inputmode=${extra.inputmode ?? nothing}
211
+ placeholder=${extra.placeholder || nothing}
212
+ ?required=${extra.required}
213
+ aria-invalid=${error ? "true" : "false"}
214
+ aria-describedby=${error ? `quote-${name}-error` : nothing}
215
+ />
216
+ ${error ? html`<p class="error" id="quote-${name}-error">${error}</p>` : nothing}
217
+ </div>`;
218
+ }
219
+ /** One sentence listing every failing field, for the live region ("" when valid). */
220
+ errorSummary() {
221
+ const messages = ["name", "email", "company", "consent"].map((name) => this.errors[name]).filter((message) => Boolean(message));
222
+ if (messages.length === 0) return "";
223
+ const count = messages.length;
224
+ return `Please fix ${count} ${count === 1 ? "field" : "fields"}: ${messages.join(" ")}`;
225
+ }
226
+ renderForm() {
227
+ const consentError = this.errors.consent;
228
+ return html`<form part="form" novalidate @submit=${this.onSubmit}>
229
+ <div class="sr-only" aria-live="polite">${this.errorSummary()}</div>
230
+ <fieldset class="options">
231
+ <legend class="legend">${this.optionsLabel}</legend>
232
+ <div class="option-list">
233
+ ${this.options.map(
234
+ (option) => html`<label class="option ${this.selected === option.id ? "selected" : ""}">
235
+ <input
236
+ type="radio"
237
+ name="option"
238
+ value=${option.id}
239
+ ?checked=${this.selected === option.id}
240
+ @change=${() => this.selected = option.id}
241
+ />
242
+ <span class="option-body">
243
+ <span class="option-label">${option.label}</span>
244
+ ${option.hint ? html`<span class="option-hint">${option.hint}</span>` : nothing}
245
+ </span>
246
+ </label>`
247
+ )}
248
+ </div>
249
+ </fieldset>
250
+ <div class="fields">
251
+ ${this.renderField("name", this.nameLabel, { required: true, autocomplete: "name", placeholder: this.namePlaceholder })}
252
+ ${this.renderField("email", this.emailLabel, { required: true, autocomplete: "email", type: "email", placeholder: this.emailPlaceholder })}
253
+ ${this.renderField("company", this.companyLabel, { required: true, autocomplete: "organization", placeholder: this.companyPlaceholder })}
254
+ ${this.renderField("count", this.countLabel, { inputmode: "numeric", placeholder: this.countPlaceholder })}
255
+ </div>
256
+ <div class="field">
257
+ <label class="label" for="quote-message">${this.messageLabel} <span class="optional">(optional)</span></label>
258
+ <textarea class="input" id="quote-message" name="message" rows="3" placeholder=${this.messagePlaceholder || nothing}></textarea>
259
+ </div>
260
+ ${this.consentLabel ? this.renderConsent(consentError) : nothing}
261
+ <button class="submit" part="submit" type="submit">${this.submitLabel}</button>
262
+ </form>`;
263
+ }
264
+ renderConsent(consentError) {
265
+ return html`<div class="field">
266
+ <label class="consent">
267
+ <input
268
+ type="checkbox"
269
+ id="quote-consent"
270
+ name="consent"
271
+ required
272
+ aria-invalid=${consentError ? "true" : "false"}
273
+ aria-describedby=${consentError ? "quote-consent-error" : nothing}
274
+ />
275
+ <span>${this.consentLabel}<span aria-hidden="true"> *</span></span>
276
+ </label>
277
+ ${consentError ? html`<p class="error" id="quote-consent-error">${consentError}</p>` : nothing}
278
+ </div>`;
279
+ }
280
+ async copyAddress() {
281
+ try {
282
+ await navigator.clipboard.writeText(this.mailto);
283
+ this.copied = true;
284
+ } catch {
285
+ }
286
+ }
287
+ /** The mailto note with `{email}` as a link to the address. */
288
+ renderMailtoNote() {
289
+ if (!this.mailto || !this.mailtoNote) return nothing;
290
+ const [before, ...rest] = this.mailtoNote.split("{email}");
291
+ const link = html`<a href="mailto:${this.mailto}">${this.mailto}</a>`;
292
+ return html`<p class="mailto-note" part="mailto-note">
293
+ ${before}${rest.length ? html`${link}${rest.join(this.mailto)}` : nothing}
294
+ </p>`;
295
+ }
296
+ renderSuccess() {
297
+ const showCopy = Boolean(this.mailto && this.mailtoNote);
298
+ return html`<div class="success" role="status">
299
+ <span class="success-icon" aria-hidden="true">${SUCCESS_ICON}</span>
300
+ <h3 class="success-title">${this.successTitle}</h3>
301
+ <p class="success-text">${this.successText.replaceAll("{option}", this.submittedOption)}</p>
302
+ ${this.renderMailtoNote()}
303
+ <div class="success-actions">
304
+ ${showCopy ? html`<button class="copy" part="copy" type="button" @click=${this.copyAddress}>
305
+ ${this.copied ? this.copiedLabel : this.copyLabel}
306
+ </button>` : nothing}
307
+ <button class="done" part="close" type="button" @click=${this.close}>${this.closeLabel}</button>
308
+ </div>
309
+ </div>`;
310
+ }
311
+ render() {
312
+ return html`
313
+ <dialog
314
+ part="dialog"
315
+ role="dialog"
316
+ aria-modal="true"
317
+ aria-labelledby="quote-title"
318
+ @close=${this.onDialogClose}
319
+ @click=${this.onBackdropClick}
320
+ >
321
+ <div class="panel">
322
+ <div class="header">
323
+ <div class="heading">
324
+ <h2 class="title" id="quote-title">${this.title}</h2>
325
+ ${this.subtitle ? html`<p class="subtitle">${this.subtitle}</p>` : nothing}
326
+ </div>
327
+ <button class="close" part="close" type="button" aria-label=${this.closeLabel} @click=${this.close}>
328
+ ${CLOSE_ICON}
329
+ </button>
330
+ </div>
331
+ ${this.submitted ? this.renderSuccess() : this.renderForm()}
332
+ </div>
333
+ </dialog>
334
+ `;
335
+ }
336
+ };
337
+ QuoteDialog.styles = css`
338
+ ${controlBaseStyles}
339
+ /* fontFamilyStyles is a bare declaration: it must sit inside a rule, or it swallows the next one. */
340
+ :host {
341
+ ${fontFamilyStyles}
342
+ }
343
+
344
+ :host {
345
+ display: block;
346
+ }
347
+
348
+ dialog {
349
+ box-sizing: border-box;
350
+ width: calc(100% - 32px);
351
+ max-width: 560px;
352
+ max-height: calc(100vh - 32px);
353
+ margin: auto;
354
+ padding: 0;
355
+ border: 0;
356
+ border-radius: 24px;
357
+ background: var(--st-color-surface);
358
+ color: var(--st-color-text-strong);
359
+ box-shadow: 0 20px 40px -12px #0000001a;
360
+ overflow: auto;
361
+ }
362
+
363
+ dialog::backdrop {
364
+ background: var(--st-quote-dialog-backdrop, rgba(1, 29, 65, 0.55));
365
+ }
366
+
367
+ .panel {
368
+ box-sizing: border-box;
369
+ display: flex;
370
+ flex-direction: column;
371
+ gap: 20px;
372
+ padding: 32px;
373
+ }
374
+
375
+ .header {
376
+ display: flex;
377
+ align-items: flex-start;
378
+ justify-content: space-between;
379
+ gap: 16px;
380
+ }
381
+
382
+ .heading {
383
+ display: flex;
384
+ flex-direction: column;
385
+ gap: 6px;
386
+ }
387
+
388
+ .title {
389
+ margin: 0;
390
+ font-size: 24px;
391
+ font-weight: var(--st-font-weight-bold);
392
+ line-height: 1.25;
393
+ color: var(--st-cms-text-strong, var(--st-cms-navy));
394
+ }
395
+
396
+ .subtitle {
397
+ margin: 0;
398
+ font-size: 15px;
399
+ line-height: 1.5;
400
+ color: var(--st-cms-text, var(--st-cms-navy-2));
401
+ }
402
+
403
+ .close {
404
+ flex: none;
405
+ display: inline-flex;
406
+ align-items: center;
407
+ justify-content: center;
408
+ width: 44px;
409
+ height: 44px;
410
+ margin: 0;
411
+ padding: 0;
412
+ border: 0;
413
+ border-radius: var(--st-radius-full);
414
+ background: var(--st-cms-surface-soft);
415
+ color: var(--st-cms-text-strong, var(--st-cms-navy));
416
+ cursor: pointer;
417
+ }
418
+
419
+ .close:hover {
420
+ background: var(--st-cms-primary-100);
421
+ }
422
+
423
+ .close svg {
424
+ width: 20px;
425
+ height: 20px;
426
+ }
427
+
428
+ .close:focus-visible,
429
+ .submit:focus-visible,
430
+ .done:focus-visible,
431
+ .option input:focus-visible,
432
+ .input:focus-visible,
433
+ .consent input:focus-visible {
434
+ outline: var(--st-focus-ring);
435
+ outline-offset: 2px;
436
+ }
437
+
438
+ form {
439
+ display: flex;
440
+ flex-direction: column;
441
+ gap: var(--st-space-16);
442
+ }
443
+
444
+ .options {
445
+ display: flex;
446
+ flex-direction: column;
447
+ gap: 8px;
448
+ min-width: 0;
449
+ margin: 0;
450
+ padding: 0;
451
+ border: 0;
452
+ }
453
+
454
+ .legend {
455
+ padding: 0;
456
+ margin-bottom: 8px;
457
+ font-size: 14px;
458
+ font-weight: var(--st-font-weight-semibold);
459
+ color: var(--st-cms-text-strong, var(--st-cms-navy));
460
+ }
461
+
462
+ .option-list {
463
+ display: flex;
464
+ flex-wrap: wrap;
465
+ gap: 8px;
466
+ }
467
+
468
+ .option {
469
+ flex: 1 1 150px;
470
+ display: flex;
471
+ align-items: flex-start;
472
+ gap: 10px;
473
+ box-sizing: border-box;
474
+ min-height: 44px;
475
+ padding: 12px 14px;
476
+ border: 1px solid var(--st-cms-primary-200);
477
+ border-radius: 10px;
478
+ background: var(--st-color-surface);
479
+ cursor: pointer;
480
+ }
481
+
482
+ .option.selected {
483
+ border: 2px solid var(--st-cms-brand-navy);
484
+ /* Keep the text where it is when the border thickens. */
485
+ padding: 11px 13px;
486
+ }
487
+
488
+ .option input {
489
+ flex: none;
490
+ width: 16px;
491
+ height: 16px;
492
+ margin: 3px 0 0;
493
+ accent-color: var(--st-cms-brand-navy);
494
+ }
495
+
496
+ .option-body {
497
+ display: flex;
498
+ flex-direction: column;
499
+ gap: 2px;
500
+ }
501
+
502
+ .option-label {
503
+ font-size: 14px;
504
+ font-weight: var(--st-font-weight-semibold);
505
+ color: var(--st-cms-text-strong, var(--st-cms-navy));
506
+ }
507
+
508
+ .option-hint {
509
+ font-size: 12px;
510
+ color: var(--st-cms-text, var(--st-cms-navy-2));
511
+ }
512
+
513
+ .fields {
514
+ display: grid;
515
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
516
+ gap: 12px;
517
+ }
518
+
519
+ .field {
520
+ display: flex;
521
+ flex-direction: column;
522
+ gap: 6px;
523
+ }
524
+
525
+ .label {
526
+ font-size: 14px;
527
+ font-weight: var(--st-font-weight-semibold);
528
+ color: var(--st-cms-text-strong, var(--st-cms-navy));
529
+ }
530
+
531
+ .optional {
532
+ font-weight: var(--st-font-weight-medium);
533
+ color: var(--st-cms-text, var(--st-cms-navy-2));
534
+ }
535
+
536
+ .input {
537
+ box-sizing: border-box;
538
+ width: 100%;
539
+ min-height: 44px;
540
+ padding: var(--st-space-8) var(--st-space-12);
541
+ border: 1px solid var(--st-color-border-strong);
542
+ border-radius: 8px;
543
+ background: var(--st-color-surface);
544
+ color: var(--st-cms-text-strong, var(--st-cms-navy));
545
+ /* 16px stops iOS zooming into the field. */
546
+ font: inherit;
547
+ font-size: 16px;
548
+ }
549
+
550
+ textarea.input {
551
+ min-height: 96px;
552
+ resize: vertical;
553
+ }
554
+
555
+ .input[aria-invalid='true'] {
556
+ border: 2px solid var(--st-color-danger);
557
+ }
558
+
559
+ .error {
560
+ margin: 0;
561
+ font-size: 13px;
562
+ color: var(--st-color-danger-text);
563
+ }
564
+
565
+ /* The always-present polite live region: announces the error summary, never shown. */
566
+ .sr-only {
567
+ position: absolute;
568
+ width: 1px;
569
+ height: 1px;
570
+ margin: -1px;
571
+ padding: 0;
572
+ overflow: hidden;
573
+ clip: rect(0 0 0 0);
574
+ white-space: nowrap;
575
+ border: 0;
576
+ }
577
+
578
+ .consent {
579
+ display: flex;
580
+ align-items: flex-start;
581
+ gap: 10px;
582
+ font-size: 13px;
583
+ line-height: 1.5;
584
+ color: var(--st-cms-text, var(--st-cms-navy-2));
585
+ }
586
+
587
+ .consent input {
588
+ flex: none;
589
+ width: 16px;
590
+ height: 16px;
591
+ margin: 2px 0 0;
592
+ accent-color: var(--st-cms-brand-navy);
593
+ }
594
+
595
+ .submit {
596
+ display: inline-flex;
597
+ align-items: center;
598
+ justify-content: center;
599
+ width: 100%;
600
+ min-height: 48px;
601
+ margin-top: var(--st-space-8);
602
+ padding: 0 var(--st-space-24);
603
+ border: 0;
604
+ border-radius: var(--st-radius-full);
605
+ background: var(--st-cms-brand-navy);
606
+ color: var(--st-cms-on-dark);
607
+ font: inherit;
608
+ font-size: 16px;
609
+ font-weight: var(--st-font-weight-semibold);
610
+ cursor: pointer;
611
+ }
612
+
613
+ .success {
614
+ display: flex;
615
+ flex-direction: column;
616
+ align-items: center;
617
+ gap: 12px;
618
+ padding: 16px 0 8px;
619
+ text-align: center;
620
+ }
621
+
622
+ .success-icon {
623
+ display: inline-flex;
624
+ align-items: center;
625
+ justify-content: center;
626
+ width: 56px;
627
+ height: 56px;
628
+ border-radius: var(--st-radius-full);
629
+ background: var(--st-cms-highlight);
630
+ color: var(--st-cms-navy);
631
+ }
632
+
633
+ .success-icon svg {
634
+ width: 28px;
635
+ height: 28px;
636
+ }
637
+
638
+ .success-title {
639
+ margin: 0;
640
+ font-size: 20px;
641
+ font-weight: var(--st-font-weight-semibold);
642
+ color: var(--st-cms-text-strong, var(--st-cms-navy));
643
+ }
644
+
645
+ .success-text {
646
+ max-width: 400px;
647
+ margin: 0;
648
+ font-size: 15px;
649
+ line-height: 1.6;
650
+ color: var(--st-cms-text, var(--st-cms-navy-2));
651
+ }
652
+
653
+ .mailto-note {
654
+ max-width: 400px;
655
+ margin: 0;
656
+ font-size: 14px;
657
+ line-height: 1.6;
658
+ color: var(--st-cms-text, var(--st-cms-navy-2));
659
+ }
660
+
661
+ .mailto-note a {
662
+ color: var(--st-cms-accent-text, var(--st-cms-brand-navy));
663
+ font-weight: var(--st-font-weight-semibold);
664
+ }
665
+
666
+ .success-actions {
667
+ display: flex;
668
+ flex-wrap: wrap;
669
+ justify-content: center;
670
+ gap: 12px;
671
+ }
672
+
673
+ .done:focus-visible,
674
+ .copy:focus-visible {
675
+ outline: 2px solid var(--st-cms-brand-azure);
676
+ outline-offset: 2px;
677
+ }
678
+
679
+ .done,
680
+ .copy {
681
+ display: inline-flex;
682
+ align-items: center;
683
+ justify-content: center;
684
+ min-height: 44px;
685
+ padding: 0 24px;
686
+ border: 1px solid var(--st-cms-primary-200);
687
+ border-radius: var(--st-radius-full);
688
+ background: var(--st-color-surface);
689
+ color: var(--st-cms-accent-text, var(--st-cms-brand-navy));
690
+ font: inherit;
691
+ font-weight: var(--st-font-weight-semibold);
692
+ cursor: pointer;
693
+ }
694
+
695
+ @media (max-width: 600px) {
696
+ .panel {
697
+ padding: 24px;
698
+ }
699
+ }
700
+ `;
701
+ __decorateClass([
702
+ property({ type: String })
703
+ ], QuoteDialog.prototype, "title", 2);
704
+ __decorateClass([
705
+ property({ type: String })
706
+ ], QuoteDialog.prototype, "subtitle", 2);
707
+ __decorateClass([
708
+ property({ attribute: false })
709
+ ], QuoteDialog.prototype, "options", 2);
710
+ __decorateClass([
711
+ property({ type: String, attribute: "options-label" })
712
+ ], QuoteDialog.prototype, "optionsLabel", 2);
713
+ __decorateClass([
714
+ property({ type: String, attribute: "name-label" })
715
+ ], QuoteDialog.prototype, "nameLabel", 2);
716
+ __decorateClass([
717
+ property({ type: String, attribute: "name-placeholder" })
718
+ ], QuoteDialog.prototype, "namePlaceholder", 2);
719
+ __decorateClass([
720
+ property({ type: String, attribute: "email-label" })
721
+ ], QuoteDialog.prototype, "emailLabel", 2);
722
+ __decorateClass([
723
+ property({ type: String, attribute: "email-placeholder" })
724
+ ], QuoteDialog.prototype, "emailPlaceholder", 2);
725
+ __decorateClass([
726
+ property({ type: String, attribute: "company-label" })
727
+ ], QuoteDialog.prototype, "companyLabel", 2);
728
+ __decorateClass([
729
+ property({ type: String, attribute: "company-placeholder" })
730
+ ], QuoteDialog.prototype, "companyPlaceholder", 2);
731
+ __decorateClass([
732
+ property({ type: String, attribute: "count-label" })
733
+ ], QuoteDialog.prototype, "countLabel", 2);
734
+ __decorateClass([
735
+ property({ type: String, attribute: "count-placeholder" })
736
+ ], QuoteDialog.prototype, "countPlaceholder", 2);
737
+ __decorateClass([
738
+ property({ type: String, attribute: "message-label" })
739
+ ], QuoteDialog.prototype, "messageLabel", 2);
740
+ __decorateClass([
741
+ property({ type: String, attribute: "message-placeholder" })
742
+ ], QuoteDialog.prototype, "messagePlaceholder", 2);
743
+ __decorateClass([
744
+ property({ type: String, attribute: "consent-label" })
745
+ ], QuoteDialog.prototype, "consentLabel", 2);
746
+ __decorateClass([
747
+ property({ type: String, attribute: "submit-label" })
748
+ ], QuoteDialog.prototype, "submitLabel", 2);
749
+ __decorateClass([
750
+ property({ type: String, attribute: "success-title" })
751
+ ], QuoteDialog.prototype, "successTitle", 2);
752
+ __decorateClass([
753
+ property({ type: String, attribute: "success-text" })
754
+ ], QuoteDialog.prototype, "successText", 2);
755
+ __decorateClass([
756
+ property({ type: String, attribute: "close-label" })
757
+ ], QuoteDialog.prototype, "closeLabel", 2);
758
+ __decorateClass([
759
+ property({ type: String })
760
+ ], QuoteDialog.prototype, "mailto", 2);
761
+ __decorateClass([
762
+ property({ type: String })
763
+ ], QuoteDialog.prototype, "subject", 2);
764
+ __decorateClass([
765
+ property({ type: String, attribute: "mailto-note" })
766
+ ], QuoteDialog.prototype, "mailtoNote", 2);
767
+ __decorateClass([
768
+ property({ type: String, attribute: "copy-label" })
769
+ ], QuoteDialog.prototype, "copyLabel", 2);
770
+ __decorateClass([
771
+ property({ type: String, attribute: "copied-label" })
772
+ ], QuoteDialog.prototype, "copiedLabel", 2);
773
+ __decorateClass([
774
+ state()
775
+ ], QuoteDialog.prototype, "selected", 2);
776
+ __decorateClass([
777
+ state()
778
+ ], QuoteDialog.prototype, "copied", 2);
779
+ __decorateClass([
780
+ state()
781
+ ], QuoteDialog.prototype, "submitted", 2);
782
+ __decorateClass([
783
+ state()
784
+ ], QuoteDialog.prototype, "errors", 2);
785
+ __decorateClass([
786
+ state()
787
+ ], QuoteDialog.prototype, "submittedOption", 2);
788
+ __decorateClass([
789
+ query("dialog")
790
+ ], QuoteDialog.prototype, "dialogEl", 2);
791
+ __decorateClass([
792
+ query("form")
793
+ ], QuoteDialog.prototype, "formEl", 2);
794
+ QuoteDialog = __decorateClass([
795
+ customElement("st-quote-dialog")
796
+ ], QuoteDialog);
797
+
798
+ export {
799
+ DEFAULT_MAILTO_BODY_LIMIT,
800
+ buildMailto,
801
+ buildQuoteMailto,
802
+ fillTemplate,
803
+ QuoteDialog
804
+ };
805
+ //# sourceMappingURL=chunk-AYNT7G3J.js.map