@4site/engrid-scripts 0.28.3 → 0.28.4

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.
@@ -42,7 +42,12 @@ export declare class RememberMe {
42
42
  });
43
43
  private updateFieldData;
44
44
  private insertClearRememberMeLink;
45
+ private escapeHtml;
46
+ private buildClearLabelMarkup;
47
+ private getUsernameFromFieldData;
48
+ private getClearLabelPlainText;
45
49
  private getElementByFirstSelector;
50
+ private placeOnRightSide;
46
51
  private insertRememberMeOptin;
47
52
  private useRemote;
48
53
  private createIframe;
@@ -165,16 +165,26 @@ export class RememberMe {
165
165
  }
166
166
  }
167
167
  insertClearRememberMeLink() {
168
+ var _a;
168
169
  let clearRememberMeField = document.getElementById("clear-autofill-data");
170
+ const { html, hasInnerLink } = this.buildClearLabelMarkup();
169
171
  if (!clearRememberMeField) {
170
- clearRememberMeField = document.createElement("a");
172
+ clearRememberMeField = document.createElement(hasInnerLink ? "span" : "a");
171
173
  clearRememberMeField.setAttribute("id", "clear-autofill-data");
172
- clearRememberMeField.classList.add("label-tooltip");
173
- clearRememberMeField.setAttribute("style", "cursor: pointer;");
174
- clearRememberMeField.innerHTML = this.fieldClearLabel;
174
+ if (hasInnerLink) {
175
+ clearRememberMeField.classList.add("clear-autofill-data-wrapper");
176
+ }
177
+ else {
178
+ clearRememberMeField.classList.add("label-tooltip");
179
+ clearRememberMeField.setAttribute("style", "cursor: pointer;");
180
+ }
181
+ clearRememberMeField.innerHTML = html;
175
182
  const targetField = this.getElementByFirstSelector(this.fieldClearSelectorTarget);
176
183
  if (targetField) {
177
- if (this.fieldClearSelectorTargetLocation === "after") {
184
+ if (this.fieldClearSelectorTargetLocation === "rightSide") {
185
+ this.placeOnRightSide(targetField, clearRememberMeField);
186
+ }
187
+ else if (this.fieldClearSelectorTargetLocation === "after") {
178
188
  targetField.appendChild(clearRememberMeField);
179
189
  }
180
190
  else {
@@ -182,7 +192,10 @@ export class RememberMe {
182
192
  }
183
193
  }
184
194
  }
185
- clearRememberMeField.addEventListener("click", (e) => {
195
+ const clickTarget = hasInnerLink
196
+ ? (_a = clearRememberMeField.querySelector("#clear-autofill-data-link")) !== null && _a !== void 0 ? _a : clearRememberMeField
197
+ : clearRememberMeField;
198
+ const onClear = (e) => {
186
199
  e.preventDefault();
187
200
  this.clearFields(["supporter.country" /*, 'supporter.emailAddress'*/]);
188
201
  if (this.useRemote()) {
@@ -201,10 +214,54 @@ export class RememberMe {
201
214
  this.rememberMeOptIn = false;
202
215
  this._events.dispatchClear();
203
216
  window.dispatchEvent(new CustomEvent("RememberMe_Cleared"));
204
- });
217
+ };
218
+ clickTarget.addEventListener("click", onClear);
205
219
  this._events.dispatchLoad(true);
206
220
  window.dispatchEvent(new CustomEvent("RememberMe_Loaded", { detail: { withData: true } }));
207
221
  }
222
+ escapeHtml(value) {
223
+ const map = {
224
+ "&": "&",
225
+ "<": "&lt;",
226
+ ">": "&gt;",
227
+ '"': "&quot;",
228
+ "'": "&#39;",
229
+ };
230
+ return value.replace(/[&<>"']/g, (c) => map[c]);
231
+ }
232
+ buildClearLabelMarkup() {
233
+ var _a;
234
+ const username = this.getUsernameFromFieldData();
235
+ const label = username
236
+ ? this.fieldClearLabel.replace(/\$username/g, () => this.escapeHtml(username))
237
+ : this.fieldClearLabel.replace(/[^\S\r\n]?\$username/g, "");
238
+ // Only the first non-empty {...} segment becomes the clickable clear link.
239
+ // Any additional {...} segments remain as literal text (braces included),
240
+ // and empty {} braces are left as-is. When no non-empty braces are present,
241
+ // the entire element is clickable (legacy behaviour).
242
+ const match = label.match(/\{([^}]+)\}/);
243
+ if (!match) {
244
+ return { html: label, hasInnerLink: false };
245
+ }
246
+ const before = label.slice(0, match.index);
247
+ const linkText = match[1];
248
+ const after = label.slice(((_a = match.index) !== null && _a !== void 0 ? _a : 0) + match[0].length);
249
+ const html = before +
250
+ `<a id="clear-autofill-data-link" class="label-tooltip" style="cursor: pointer;">${linkText}</a>` +
251
+ after;
252
+ return { html, hasInnerLink: true };
253
+ }
254
+ getUsernameFromFieldData() {
255
+ const value = this.fieldData["supporter.firstName"];
256
+ return value ? value.trim() : "";
257
+ }
258
+ getClearLabelPlainText() {
259
+ const username = this.getUsernameFromFieldData();
260
+ const label = username
261
+ ? this.fieldClearLabel.replace(/\$username/g, () => username)
262
+ : this.fieldClearLabel.replace(/[^\S\r\n]?\$username/g, "");
263
+ return label.replace(/\{([^}]*)\}/g, "$1");
264
+ }
208
265
  getElementByFirstSelector(selectorsString) {
209
266
  // iterate through the selectors until we find one that exists
210
267
  let targetField = null;
@@ -217,13 +274,76 @@ export class RememberMe {
217
274
  }
218
275
  return targetField;
219
276
  }
277
+ placeOnRightSide(targetField, elementToPlace) {
278
+ const wrapperClass = "rememberme-right-side-wrapper";
279
+ let wrapper;
280
+ if (targetField.parentElement &&
281
+ targetField.parentElement.classList.contains(wrapperClass)) {
282
+ wrapper = targetField.parentElement;
283
+ }
284
+ else {
285
+ // Read the target's computed styles BEFORE moving it into the flex
286
+ // wrapper. Once inside a flex container, margin collapsing no longer
287
+ // applies and the reported values can change. We transfer the original
288
+ // margin-top to the wrapper itself so the block retains its vertical
289
+ // spacing in the document flow, and zero out the target's own margin-top
290
+ // so it doesn't double-apply inside the flex row.
291
+ const targetStyle = window.getComputedStyle(targetField);
292
+ const targetMarginTop = targetStyle.marginTop;
293
+ const targetMarginBottom = targetStyle.marginBottom;
294
+ wrapper = document.createElement("div");
295
+ wrapper.classList.add(wrapperClass);
296
+ wrapper.style.display = "flex";
297
+ wrapper.style.alignItems = "center";
298
+ wrapper.style.gap = "8px";
299
+ wrapper.style.flexWrap = "wrap";
300
+ // Transfer vertical margins from the target to the wrapper so the
301
+ // surrounding layout is unchanged after the DOM move.
302
+ wrapper.style.marginTop = targetMarginTop;
303
+ wrapper.style.marginBottom = targetMarginBottom;
304
+ if (targetField.parentNode) {
305
+ targetField.parentNode.insertBefore(wrapper, targetField);
306
+ }
307
+ // Zero out the target's own margin-top/-bottom now that the wrapper
308
+ // owns them, so they don't double-apply inside the flex row.
309
+ targetField.style.marginTop = "0px";
310
+ targetField.style.marginBottom = "0px";
311
+ wrapper.appendChild(targetField);
312
+ }
313
+ // Read padding-left AFTER the wrapper exists but before any alignment
314
+ // logic, so it reflects the current computed value.
315
+ const targetPaddingLeft = window.getComputedStyle(targetField).paddingLeft;
316
+ wrapper.appendChild(elementToPlace);
317
+ const applyAlignment = () => {
318
+ const wrapped = elementToPlace.offsetTop > targetField.offsetTop;
319
+ if (wrapped) {
320
+ // When wrapped below, left-align with the target's content area.
321
+ elementToPlace.style.marginTop = "0px";
322
+ elementToPlace.style.marginLeft = targetPaddingLeft;
323
+ }
324
+ else {
325
+ // When side-by-side, both items are already vertically centred by the
326
+ // flex container — no extra margin-top needed on elementToPlace.
327
+ elementToPlace.style.marginTop = "0px";
328
+ elementToPlace.style.marginLeft = "0px";
329
+ }
330
+ };
331
+ // Defer the first alignment check to the next animation frame so the
332
+ // browser has had a chance to perform layout. Without this, offsetTop on
333
+ // both elements is still 0 (or stale) at call time.
334
+ requestAnimationFrame(applyAlignment);
335
+ if (typeof window.ResizeObserver === "function") {
336
+ const observer = new window.ResizeObserver(() => applyAlignment());
337
+ observer.observe(wrapper);
338
+ }
339
+ }
220
340
  insertRememberMeOptin() {
221
341
  let rememberMeOptInField = document.getElementById("remember-me-opt-in");
222
342
  if (!rememberMeOptInField) {
223
343
  const rememberMeLabel = this.rememberMeLabel;
224
344
  const rememberMeInfo = ENGrid.t("rememberMe.tooltip", {
225
345
  label: rememberMeLabel,
226
- clearLabel: this.fieldClearLabel,
346
+ clearLabel: this.getClearLabelPlainText(),
227
347
  });
228
348
  const rememberMeOptInFieldChecked = this.rememberMeOptIn ? "checked" : "";
229
349
  const rememberMeOptInField = document.createElement("div");
@@ -247,9 +367,14 @@ export class RememberMe {
247
367
  `;
248
368
  const targetField = this.getElementByFirstSelector(this.fieldOptInSelectorTarget);
249
369
  if (targetField && targetField.parentNode) {
250
- targetField.parentNode.insertBefore(rememberMeOptInField, this.fieldOptInSelectorTargetLocation == "before"
251
- ? targetField
252
- : targetField.nextSibling);
370
+ if (this.fieldOptInSelectorTargetLocation === "rightSide") {
371
+ this.placeOnRightSide(targetField, rememberMeOptInField);
372
+ }
373
+ else {
374
+ targetField.parentNode.insertBefore(rememberMeOptInField, this.fieldOptInSelectorTargetLocation == "before"
375
+ ? targetField
376
+ : targetField.nextSibling);
377
+ }
253
378
  const rememberMeCheckbox = document.getElementById("remember-me-checkbox");
254
379
  if (rememberMeCheckbox) {
255
380
  rememberMeCheckbox.addEventListener("change", () => {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const AppVersion = "0.28.3";
1
+ export declare const AppVersion = "0.28.4";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const AppVersion = "0.28.3";
1
+ export const AppVersion = "0.28.4";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4site/engrid-scripts",
3
- "version": "0.28.3",
3
+ "version": "0.28.4",
4
4
  "description": "Scripts for the ENGRID Project",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -25,7 +25,7 @@
25
25
  "strongly-typed-events": "^2.0.9"
26
26
  },
27
27
  "homepage": "https://github.com/4site-interactive-studios/engrid#readme",
28
- "gitHead": "c1aaaea0b49ed3d1f27eaaa62814d7ba02b950f7",
28
+ "gitHead": "fb6e68d0a52142cba344b693972b38da7dcea795",
29
29
  "devDependencies": {
30
30
  "nodemon": "^2.0.20",
31
31
  "typescript": "^4.9.5"