@gudhub/ssg-web-components-library 1.0.116 → 1.0.118

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 (31) hide show
  1. package/package.json +1 -1
  2. package/src/components/get-in-touch-form/get-in-touch-form.html +0 -3
  3. package/src/components/get-in-touch-form/get-in-touch-form.js +16 -72
  4. package/src/components/get-in-touch-form/get-in-touch-form.scss +0 -12
  5. package/src/components/liqpay-component/config.js +6 -0
  6. package/src/components/liqpay-component/liqpay-component.html +91 -0
  7. package/src/components/liqpay-component/liqpay-component.js +197 -0
  8. package/src/components/liqpay-component/liqpay-component.json +14 -0
  9. package/src/components/liqpay-component/liqpay-component.md +0 -0
  10. package/src/components/liqpay-component/liqpay-component.scss +337 -0
  11. package/src/components/liqpay-payment-button/config.js +6 -0
  12. package/src/components/liqpay-payment-button/liqpay-payment-button.html +5 -0
  13. package/src/components/liqpay-payment-button/liqpay-payment-button.js +23 -0
  14. package/src/components/liqpay-payment-button/liqpay-payment-button.json +4 -0
  15. package/src/components/liqpay-payment-button/liqpay-payment-button.md +0 -0
  16. package/src/components/liqpay-payment-button/liqpay-payment-button.scss +7 -0
  17. package/src/components/masonry-gallery/masonry-gallery.js +68 -24
  18. package/src/components/video-slider/animations/cascade-strips.js +89 -0
  19. package/src/components/video-slider/animations/fade.js +31 -0
  20. package/src/components/video-slider/animations/multi-strip.js +66 -0
  21. package/src/components/video-slider/animations/sectors.js +104 -0
  22. package/src/components/video-slider/animations/wipe-left.js +43 -0
  23. package/src/components/video-slider/animations/zoom.js +51 -0
  24. package/src/components/video-slider/config.js +6 -0
  25. package/src/components/video-slider/utils/draw-image-cover.js +27 -0
  26. package/src/components/video-slider/utils/easing.js +5 -0
  27. package/src/components/video-slider/video-slider.html +5 -0
  28. package/src/components/video-slider/video-slider.js +177 -0
  29. package/src/components/video-slider/video-slider.readme.md +218 -0
  30. package/src/components/video-slider/video-slider.scss +16 -0
  31. package/src/config.js +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gudhub/ssg-web-components-library",
3
- "version": "1.0.116",
3
+ "version": "1.0.118",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -10,9 +10,6 @@
10
10
  <button type="submit" class="btn">${buttonText ? buttonText : 'Get in touch'}</button>
11
11
  <div class="loader"></div>
12
12
  </div>
13
- <div class="recaptcha-notice">
14
- This site is protected by reCAPTCHA.
15
- </div>
16
13
  </form>
17
14
  <div class="overflow success">
18
15
  <div class="icon_wrapper">
@@ -5,7 +5,6 @@ import defaultConfigs from './get-in-touch-form-data.json';
5
5
  import { checkInputsValidations } from './inputsValidation.js';
6
6
 
7
7
  let recaptchaPromise = null;
8
- let recaptchaBadgeObserver = null;
9
8
 
10
9
  function loadRecaptcha(siteKey) {
11
10
  if (!siteKey) {
@@ -36,41 +35,16 @@ function loadRecaptcha(siteKey) {
36
35
  return recaptchaPromise;
37
36
  }
38
37
 
39
- function hideRecaptchaBadge() {
40
- const badge = document.querySelector('.grecaptcha-badge');
41
-
42
- if (!badge) return;
43
-
44
- badge.style.setProperty('visibility', 'hidden', 'important');
45
- badge.style.setProperty('opacity', '0', 'important');
46
- badge.style.setProperty('pointer-events', 'none', 'important');
47
- }
48
-
49
- function observeRecaptchaBadge() {
50
- hideRecaptchaBadge();
51
-
52
- if (recaptchaBadgeObserver) return;
53
-
54
- recaptchaBadgeObserver = new MutationObserver(() => {
55
- hideRecaptchaBadge();
56
- });
57
-
58
- recaptchaBadgeObserver.observe(document.body, {
59
- childList: true,
60
- subtree: true
61
- });
62
- }
63
-
64
38
  class GetInTouchForm extends GHComponent {
65
39
  constructor() {
66
40
  super();
67
41
 
68
- this.formId = this.getAttribute('data-form-id');
42
+ this.formId = this.getAttribute("data-form-id");
69
43
  this.defaultConfigs = defaultConfigs;
70
44
 
71
45
  this.generateInput = this.generateInput;
72
46
  this.isFormSubmitted = false;
73
-
47
+
74
48
  this.placement = 'main';
75
49
  this.config = window.getConfig()?.componentsConfigs?.formConfig || window.getConfig()?.formConfig;
76
50
 
@@ -92,11 +66,7 @@ class GetInTouchForm extends GHComponent {
92
66
  this.attachEventListeners();
93
67
 
94
68
  if (this.recaptcha_site_key) {
95
- loadRecaptcha(this.recaptcha_site_key)
96
- .then(() => {
97
- observeRecaptchaBadge();
98
- })
99
- .catch(console.error);
69
+ loadRecaptcha(this.recaptcha_site_key).catch(console.error);
100
70
  }
101
71
  }
102
72
  }
@@ -106,26 +76,15 @@ class GetInTouchForm extends GHComponent {
106
76
  super.render(html);
107
77
  this.attachEventListeners();
108
78
 
109
- if (this.recaptcha_site_key) {
110
- loadRecaptcha(this.recaptcha_site_key)
111
- .then(() => {
112
- observeRecaptchaBadge();
113
- })
114
- .catch(console.error);
115
- }
79
+ loadRecaptcha(this.recaptcha_site_key).catch(console.error);
116
80
  }
117
81
 
118
82
  attachEventListeners() {
119
- const form = this.getElementsByTagName('form')[0];
120
- const restartButton = this.getElementsByClassName('restart_button')[0];
83
+ this.getElementsByTagName('form')[0]
84
+ .addEventListener('submit', (e) => this.handleSubmit(e));
121
85
 
122
- if (form) {
123
- form.addEventListener('submit', (e) => this.handleSubmit(e));
124
- }
125
-
126
- if (restartButton) {
127
- restartButton.addEventListener('click', () => this.hideFail());
128
- }
86
+ this.getElementsByClassName('restart_button')[0]
87
+ .addEventListener('click', () => this.hideFail());
129
88
  }
130
89
 
131
90
  onParentPopupClose() {
@@ -140,7 +99,7 @@ class GetInTouchForm extends GHComponent {
140
99
  initConfig(formConfigs) {
141
100
  try {
142
101
  this.config = formConfigs.find(({ id }) => id === this.formId);
143
- if (!this.config) throw new Error('Config not found');
102
+ if (!this.config) throw new Error("Config not found");
144
103
  } catch {
145
104
  const defaultId = this.isInPopup ? 'default popup' : 'default';
146
105
  this.config = defaultConfigs.find(({ id }) => id === defaultId);
@@ -148,7 +107,7 @@ class GetInTouchForm extends GHComponent {
148
107
 
149
108
  this.titleName = this.getAttribute('data-form-title') || this.config.title;
150
109
  this.subtitleName = this.getAttribute('data-form-subtitle') || this.config.subtitle;
151
- this.placement = this.getAttribute('data-form-placement') || 'main';
110
+ this.placement = this.getAttribute('data-form-placement') || "main";
152
111
  this.buttonText = this.getAttribute('data-form-button-text') || this.config.button_text;
153
112
  }
154
113
 
@@ -162,8 +121,6 @@ class GetInTouchForm extends GHComponent {
162
121
 
163
122
  return new Promise((resolve, reject) => {
164
123
  grecaptcha.ready(() => {
165
- hideRecaptchaBadge();
166
-
167
124
  grecaptcha.execute(this.recaptcha_site_key, { action })
168
125
  .then(resolve)
169
126
  .catch(reject);
@@ -189,6 +146,7 @@ class GetInTouchForm extends GHComponent {
189
146
  }
190
147
 
191
148
  this.handleSuccessFormValidation(form, token);
149
+
192
150
  } catch (error) {
193
151
  console.error(error);
194
152
  this.showFail();
@@ -221,7 +179,7 @@ class GetInTouchForm extends GHComponent {
221
179
  inputsValidation = async (form) => {
222
180
  const inputs = Array.from(form.querySelectorAll('input'));
223
181
  return checkInputsValidations(inputs);
224
- };
182
+ }
225
183
 
226
184
  verifyRecaptcha = async (recaptchaToken) => {
227
185
  const response = await fetch('/api/verify-recaptcha', {
@@ -242,7 +200,7 @@ class GetInTouchForm extends GHComponent {
242
200
  }
243
201
 
244
202
  return data;
245
- };
203
+ }
246
204
 
247
205
  createDataObject(form, placement, recaptchaToken) {
248
206
  const inputs = {};
@@ -272,24 +230,13 @@ class GetInTouchForm extends GHComponent {
272
230
 
273
231
  addLoader() {
274
232
  this.classList.add('loading');
275
-
276
- const btn = this.querySelector('button[type="submit"]');
277
-
278
- if (btn) {
279
- btn.disabled = true;
280
- }
233
+ this.querySelector('button[type="submit"]').disabled = true;
281
234
  }
282
235
 
283
236
  removeLoader() {
284
237
  this.classList.remove('loading');
285
-
286
238
  const btn = this.querySelector('button[type="submit"]');
287
-
288
- if (btn) {
289
- setTimeout(() => {
290
- btn.disabled = false;
291
- }, 500);
292
- }
239
+ setTimeout(() => btn.disabled = false, 500);
293
240
  }
294
241
 
295
242
  showSuccess({ email, phone }) {
@@ -331,9 +278,6 @@ class GetInTouchForm extends GHComponent {
331
278
 
332
279
  hideFail() {
333
280
  const el = this.querySelector('.overflow.fail');
334
-
335
- if (!el) return;
336
-
337
281
  el.style.opacity = 0;
338
282
 
339
283
  setTimeout(() => {
@@ -371,4 +315,4 @@ class GetInTouchForm extends GHComponent {
371
315
 
372
316
  if (!customElements.get('get-in-touch-form')) {
373
317
  customElements.define('get-in-touch-form', GetInTouchForm);
374
- }
318
+ }
@@ -271,12 +271,6 @@ get-in-touch-form {
271
271
  }
272
272
  }
273
273
  }
274
- .recaptcha-notice {
275
- margin-top: 10px;
276
- font-size: 12px;
277
- line-height: 1.4;
278
- opacity: 0.7;
279
- }
280
274
  &[data-in-popup] {
281
275
  .get-in-touch-form {
282
276
  padding-top: 0;
@@ -374,10 +368,4 @@ popup-container[data-position="bottom-right"] {
374
368
  }
375
369
  }
376
370
  }
377
- }
378
-
379
- body .grecaptcha-badge {
380
- visibility: hidden !important;
381
- opacity: 0 !important;
382
- pointer-events: none !important;
383
371
  }
@@ -0,0 +1,6 @@
1
+ export const liqpayComponent = {
2
+ tag: 'liqpay-component',
3
+ category: 'Payment',
4
+ src: '@gudhub/ssg-web-components-library/src/components/liqpay-component/liqpay-component.js',
5
+ serverOnly: false
6
+ };
@@ -0,0 +1,91 @@
1
+ <section class="liqpay-component">
2
+ <!-- <div class="liqpay-component__logo">
3
+ <div class="liqpay-component__logo-badge">
4
+ <div class="liqpay-component__logo-badge-text">
5
+ <span class="liqpay-component__logo-gud">GUD</span>
6
+ <span class="liqpay-component__logo-hub">HUB</span>
7
+ </div>
8
+ </div>
9
+ <span class="liqpay-component__logo-crm">CRM</span>
10
+ </div> -->
11
+
12
+ <h1 class="liqpay-component__title" data-liqpay-title></h1>
13
+
14
+ <p class="liqpay-component__subtitle" data-liqpay-subtitle></p>
15
+
16
+ <div class="liqpay-component__card">
17
+ <div class="liqpay-component__amount-row">
18
+ <div>
19
+ <p class="liqpay-component__amount-label">Amount</p>
20
+ <p class="liqpay-component__amount" data-liqpay-amount></p>
21
+ </div>
22
+ <div class="liqpay-component__secured-badge">
23
+ <div class="liqpay-component__secured-icon" aria-hidden="true">
24
+ <svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
25
+ <polyline points="2,5 4,7.5 8,3" stroke="#ffffff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
26
+ </svg>
27
+ </div>
28
+ <span class="liqpay-component__secured-text">Secured</span>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="liqpay-component__divider"></div>
33
+
34
+ <div class="liqpay-component__summary">
35
+ <div class="liqpay-component__summary-row">
36
+ <span class="liqpay-component__summary-label">Description</span>
37
+ <strong class="liqpay-component__summary-value" data-liqpay-description></strong>
38
+ </div>
39
+
40
+ <div class="liqpay-component__summary-row" data-liqpay-order-row>
41
+ <span class="liqpay-component__summary-label">Order ID</span>
42
+ <strong class="liqpay-component__summary-value" data-liqpay-order-id></strong>
43
+ </div>
44
+ </div>
45
+
46
+ <p class="liqpay-component__text" data-liqpay-text></p>
47
+
48
+ <form
49
+ class="liqpay-component__form"
50
+ method="POST"
51
+ accept-charset="utf-8"
52
+ data-liqpay-form
53
+ >
54
+ <input type="hidden" name="data" data-liqpay-data>
55
+ <input type="hidden" name="signature" data-liqpay-signature>
56
+
57
+ <button class="liqpay-component__button" type="button" data-liqpay-button>
58
+ <svg class="liqpay-component__button-icon" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
59
+ <rect x="2" y="7" width="14" height="10" rx="2" stroke="#ffffff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
60
+ <path d="M6 7V5a3 3 0 016 0v2" stroke="#ffffff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
61
+ </svg>
62
+ <span data-liqpay-button-text></span>
63
+ </button>
64
+ </form>
65
+
66
+ <p class="liqpay-component__error" data-liqpay-error hidden></p>
67
+
68
+ <p class="liqpay-component__powered">
69
+ Powered by <span class="liqpay-component__powered-brand">LiqPay</span>
70
+ </p>
71
+ </div>
72
+
73
+ <div class="liqpay-component__security">
74
+ <div class="liqpay-component__security-item">
75
+ <div class="liqpay-component__security-icon" aria-hidden="true">
76
+ <svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
77
+ <polyline points="2,5 4,7.5 8,3" stroke="#16a34a" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
78
+ </svg>
79
+ </div>
80
+ <span>256-bit Encryption</span>
81
+ </div>
82
+ <div class="liqpay-component__security-item">
83
+ <div class="liqpay-component__security-icon" aria-hidden="true">
84
+ <svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
85
+ <polyline points="2,5 4,7.5 8,3" stroke="#16a34a" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
86
+ </svg>
87
+ </div>
88
+ <span>PCI DSS Compliant</span>
89
+ </div>
90
+ </div>
91
+ </section>
@@ -0,0 +1,197 @@
1
+ import html from './liqpay-component.html';
2
+ import './liqpay-component.scss';
3
+ import jsonTemplate from './liqpay-component.json';
4
+
5
+ class LiqpayComponent extends window.GHComponent {
6
+ constructor() {
7
+ super();
8
+ super.setDefaultData(jsonTemplate);
9
+
10
+ this.isLoading = false;
11
+ }
12
+
13
+ async onServerRender() {
14
+ super.render(html);
15
+ }
16
+
17
+ onClientRender() {
18
+ this.initComponent();
19
+ }
20
+
21
+ initComponent() {
22
+ this.titleElement = this.querySelector('[data-liqpay-title]');
23
+ this.subtitleElement = this.querySelector('[data-liqpay-subtitle]');
24
+ this.amountElement = this.querySelector('[data-liqpay-amount]');
25
+ this.descriptionElement = this.querySelector('[data-liqpay-description]');
26
+ this.textElement = this.querySelector('[data-liqpay-text]');
27
+ this.orderRowElement = this.querySelector('[data-liqpay-order-row]');
28
+ this.orderIdElement = this.querySelector('[data-liqpay-order-id]');
29
+ this.formElement = this.querySelector('[data-liqpay-form]');
30
+ this.dataInput = this.querySelector('[data-liqpay-data]');
31
+ this.signatureInput = this.querySelector('[data-liqpay-signature]');
32
+ this.buttonElement = this.querySelector('[data-liqpay-button]');
33
+ this.buttonTextElement = this.querySelector('[data-liqpay-button-text]');
34
+ this.errorElement = this.querySelector('[data-liqpay-error]');
35
+
36
+ this.renderPaymentInfo();
37
+ this.bindEvents();
38
+ }
39
+
40
+ getConfig() {
41
+ return {
42
+ ...jsonTemplate,
43
+ ...(this.json || {})
44
+ };
45
+ }
46
+
47
+ renderPaymentInfo() {
48
+ const config = this.getConfig();
49
+
50
+ console.log("CONFIG:", config);
51
+
52
+ const amount = Number(config.amount || 0);
53
+ const currency = config.currency || 'UAH';
54
+
55
+ this.titleElement.textContent = config.title || '';
56
+ this.subtitleElement.textContent = config.subtitle || '';
57
+ this.descriptionElement.textContent = config.payment_description || '';
58
+ this.textElement.textContent = config.description || '';
59
+ this.amountElement.textContent = this.formatAmount(amount, currency);
60
+ this.buttonTextElement.textContent = config.button_text || 'Сплатити';
61
+
62
+ if (config.order_id) {
63
+ this.orderIdElement.textContent = config.order_id;
64
+ this.orderRowElement.hidden = false;
65
+ } else {
66
+ this.orderIdElement.textContent = '';
67
+ this.orderRowElement.hidden = true;
68
+ }
69
+
70
+ this.formElement.action = config.liqpay_checkout_url;
71
+ }
72
+
73
+ bindEvents() {
74
+ this.buttonElement.addEventListener('click', () => this.handlePayment());
75
+ }
76
+
77
+ async handlePayment() {
78
+ if (this.isLoading) {
79
+ return;
80
+ }
81
+
82
+ const config = this.getConfig();
83
+
84
+ this.hideError();
85
+
86
+ if (!this.validatePaymentConfig(config)) {
87
+ this.showError('Некоректні дані для оплати.');
88
+ return;
89
+ }
90
+
91
+ try {
92
+ this.setLoading(true);
93
+
94
+ const payment = await this.createPayment(config);
95
+
96
+ this.dataInput.value = payment.data;
97
+ this.signatureInput.value = payment.signature;
98
+
99
+ this.formElement.submit();
100
+ } catch (error) {
101
+ console.error('Failed to create LiqPay payment:', error);
102
+ this.showError(
103
+ config.error_text || 'Не вдалося підготувати оплату. Спробуйте ще раз.'
104
+ );
105
+ } finally {
106
+ this.setLoading(false);
107
+ }
108
+ }
109
+
110
+ async createPayment(config) {
111
+ const response = await fetch(`https://gudhub-nodejs.ngrok.io:443${config.create_payment_url}`, {
112
+ method: 'POST',
113
+ headers: {
114
+ 'Content-Type': 'application/json'
115
+ },
116
+ body: JSON.stringify({
117
+ amount: config.amount,
118
+ currency: config.currency,
119
+ order_id: config.order_id,
120
+ description: config.payment_description
121
+ })
122
+ });
123
+
124
+ if (!response.ok) {
125
+ const errorText = await response.text();
126
+
127
+ throw new Error(`Payment request failed with status ${response.status}: ${errorText}`);
128
+ }
129
+
130
+ const payment = await response.json();
131
+
132
+ if (!payment?.data || !payment?.signature) {
133
+ throw new Error('Invalid payment response');
134
+ }
135
+
136
+ return payment;
137
+ }
138
+
139
+ validatePaymentConfig(config) {
140
+ const amount = Number(config.amount);
141
+
142
+ if (!Number.isFinite(amount) || amount <= 0) {
143
+ return false;
144
+ }
145
+
146
+ if (!config.currency) {
147
+ return false;
148
+ }
149
+
150
+ if (!config.order_id) {
151
+ return false;
152
+ }
153
+
154
+ if (!config.payment_description) {
155
+ return false;
156
+ }
157
+
158
+ if (!config.create_payment_url) {
159
+ return false;
160
+ }
161
+
162
+ if (!config.liqpay_checkout_url) {
163
+ return false;
164
+ }
165
+
166
+ return true;
167
+ }
168
+
169
+ setLoading(isLoading) {
170
+ const config = this.getConfig();
171
+
172
+ this.isLoading = isLoading;
173
+ this.buttonElement.disabled = isLoading;
174
+ this.buttonTextElement.textContent = isLoading
175
+ ? config.loading_text || 'Підготовка оплати...'
176
+ : config.button_text || 'Сплатити';
177
+ }
178
+
179
+ showError(message) {
180
+ this.errorElement.textContent = message;
181
+ this.errorElement.hidden = false;
182
+ }
183
+
184
+ hideError() {
185
+ this.errorElement.textContent = '';
186
+ this.errorElement.hidden = true;
187
+ }
188
+
189
+ formatAmount(amount, currency) {
190
+ return new Intl.NumberFormat('uk-UA', {
191
+ style: 'currency',
192
+ currency
193
+ }).format(amount);
194
+ }
195
+ }
196
+
197
+ window.customElements.define('liqpay-component', LiqpayComponent);
@@ -0,0 +1,14 @@
1
+ {
2
+ "title": "Test Payment",
3
+ "subtitle": "Complete your test payment securely with LiqPay.",
4
+ "description": "After clicking the button you will be redirected to the LiqPay secure payment page.",
5
+ "amount": 1,
6
+ "currency": "UAH",
7
+ "order_id": "test-order-001",
8
+ "payment_description": "Test LiqPay payment",
9
+ "button_text": "Сплатити",
10
+ "loading_text": "Підготовка оплати...",
11
+ "error_text": "Не вдалося підготувати оплату. Спробуйте ще раз.",
12
+ "create_payment_url": "/liqpay/create-payment",
13
+ "liqpay_checkout_url": "https://www.liqpay.ua/api/3/checkout"
14
+ }