PageSourceSearch

https://newterracotta.com/wp-content/themes/piwo-child/js/wishlist.js

js newterracotta.com collected 2026-10-08 09:35:45 UTC 9,463 bytes, 376 lines download raw bytes

1class Wishlist {
2	constructor() {
3		this.storageKey = 'wishlist_samples';
4		this.buttonsSelector = '.wishlist-btn';
5
6		this.init();
7	}
8
9	// Storage
10
11	getItems() {
12		//return Cookies.get(this.storageKey) || [];
13		return JSON.parse(localStorage.getItem(this.storageKey)) || [];
14	}
15
16	saveItems(items) {
17		//Cookies.set(this.storageKey, JSON.stringify(items));
18		localStorage.setItem(this.storageKey, JSON.stringify(items));
19	}
20
21	// Helpers
22
23	exists(id) {
24		return this.getItems().some((item) => item.id === id);
25	}
26
27	add(product) {
28		const items = this.getItems();
29
30		if (!this.exists(product.id)) {
31			items.push(product);
32			this.saveItems(items);
33		}
34	}
35
36	remove(id) {
37		const items = this.getItems().filter((item) => item.id !== id);
38		this.saveItems(items);
39		this.removeDisplay(id);
40	}
41
42	toggle(product) {
43		if (this.exists(product.id)) {
44			this.remove(product.id);
45		} else {
46			this.add(product);
47		}
48
49		this.updateButtons();
50		this.updateHeaderCounter();
51	}
52
53	clear() {
54		localStorage.removeItem(this.storageKey);
55		this.updateButtons();
56	}
57
58	// UI
59
60	updateButtons() {
61		const items = this.getItems();
62
63		document.querySelectorAll(this.buttonsSelector).forEach((button) => {
64			const id = button.dataset.id;
65
66			const active = items.some((item) => item.id === id);
67
68			button.classList.toggle('fav', active);
69		});
70	}
71
72	bindEvents() {
73		document.querySelectorAll(this.buttonsSelector).forEach((button) => {
74			button.addEventListener('click', (event) => {
75				event.preventDefault();
76				const product = {
77					id: button.dataset.id,
78					name: button.dataset.name,
79					image: button.dataset.image,
80					code: button.dataset.code,
81					href: button.dataset.href,
82				};
83
84				const exists = this.exists(product.id);
85
86				/*if (!exists) {
87					//this.flyToWishlist(button);
88					//this.color(button);
89					this.colorCorner(button, false);
90				}*/
91
92				if (exists) {
93					console.log('exists reverse');
94					this.colorCorner(button, true);
95				} else {
96					this.colorCorner(button, false);
97				}
98
99				this.toggle(product);
100			});
101		});
102	}
103
104	removeDisplay(id) {
105		console.log('remove display', id);
106		const container = document.querySelector('#wishlist-products .wishlist-inner');
107
108		if (container) {
109			container.querySelectorAll('.colours-block').forEach((item) => {
110				if (item.dataset.id == id) {
111					item.remove();
112				}
113			});
114
115			this.displayCount();
116		}
117	}
118
119	displayCount() {
120		const header = document.querySelector('#wishlist-products');
121
122		if (!header) {
123			return;
124		}
125
126		const counter = this.getItems().length;
127
128		if (counter) {
129			const selectedSamples = header.querySelector('.wishlist-counter').dataset.content;
130			header.querySelector('.wishlist-counter').innerHTML = `${counter} ${selectedSamples}`;
131		} else {
132			header.querySelector('.wishlist-counter').innerHTML = `${selectedSamples}`;
133		}
134	}
135
136	updateHeaderCounter() {
137		const badge = document.querySelectorAll('.wishlist-bookmark-counter');
138
139		if (!badge) {
140			return;
141		}
142
143		badge.forEach((item) => {
144			const count = this.getItems().length;
145			if (count > 0) {
146				item.classList.add('show');
147			} else {
148				item.classList.remove('show');
149			}
150			item.textContent = count;
151
152			item.textContent = count;
153
154			item.classList.remove('bump');
155
156			requestAnimationFrame(() => {
157				item.classList.add('bump');
158			});
159		});
160	}
161	async colorCorner(button, reverse = false) {
162		const rect = button.getBoundingClientRect();
163		const bookmark = document.querySelector('.fixed-nav .wishlist-bookmark');
164
165		const buttonX = rect.left + rect.width / 2;
166		const buttonY = rect.top + rect.height / 2;
167
168		// Centro da bola
169		const bookmarkRect = bookmark.getBoundingClientRect();
170
171		const centerX = bookmarkRect.left + bookmarkRect.width / 2;
172		//const centerY = bookmarkRect.top + bookmarkRect.height / 2;
173
174		/*const centerX = window.innerWidth;*/
175		const centerY = 0;
176
177		const dx = buttonX - centerX;
178		const dy = buttonY - centerY;
179
180		const radius = Math.sqrt(dx * dx + dy * dy);
181
182		/*const img = new Image();
183		img.crossOrigin = 'anonymous';
184		img.src = button.dataset.image;
185
186		await img.decode();*/
187
188		const img = button.closest('.aCor').querySelector('img');
189		console.log(img);
190		const color = ColorThief.getColorSync(img);
191		const colorHex = color.hex();
192		const colorHexOpacity = colorHex + '4C';
193		const ball = document.createElement('div');
194
195		ball.className = 'wishlist-burst';
196		ball.style.setProperty('--color', colorHex);
197		ball.style.setProperty('--colorOpacity', colorHexOpacity);
198		ball.style.setProperty('--width', `${radius * 2}px`);
199		ball.style.setProperty('--height', `${radius * 2}px`);
200		ball.style.setProperty('--left', `${centerX}px`);
201		ball.style.setProperty('--top', `${centerY}px`);
202		ball.style.setProperty('--buttonX', `${buttonX}px`);
203		ball.style.setProperty('--buttonY', `${buttonY}px`);
204
205		if (reverse) {
206			ball.classList.add('reverse');
207		}
208
209		document.body.appendChild(ball);
210
211		setTimeout(() => {
212			ball.classList.add('animate');
213		}, 100);
214
215		ball.addEventListener('transitionend', () => ball.remove(), { once: true });
216	}
217
218	async color(button) {
219		const rect = button.getBoundingClientRect();
220
221		const startX = rect.left + rect.width / 2;
222		const startY = rect.top + rect.height / 2;
223
224		const burst = document.createElement('div');
225		const url = button.dataset.image;
226
227		const img = new Image();
228		img.crossOrigin = 'anonymous';
229		img.src = url;
230		await img.decode();
231
232		const color = ColorThief.getColorSync(img);
233		const colorHex = color.hex();
234
235		//const color = '#838485';
236		burst.className = 'color-burst';
237		burst.style.background = colorHex;
238
239		burst.style.setProperty('--x', `${startX}px`);
240		burst.style.setProperty('--y', `${startY}px`);
241		burst.style.setProperty('--color', colorHex);
242
243		document.body.appendChild(burst);
244
245		burst.addEventListener('animationend', () => {
246			burst.remove();
247		});
248	}
249	flyToWishlist(button) {
250		const bookmark = document.querySelector('.wishlist-bookmark');
251
252		if (!bookmark) return;
253
254		const start = button.getBoundingClientRect();
255		const end = bookmark.getBoundingClientRect();
256
257		const dot = document.createElement('div');
258		dot.className = 'wishlist-dot';
259
260		document.body.appendChild(dot);
261
262		dot.style.position = 'fixed';
263		dot.style.left = start.left + start.width / 2 + 'px';
264		dot.style.top = start.top + start.height / 2 + 'px';
265		dot.style.opacity = '1';
266
267		requestAnimationFrame(() => {
268			dot.classList.add('show');
269
270			dot.style.left = end.left + end.width / 2 + 'px';
271			dot.style.top = end.top + end.height / 2 + 'px';
272			dot.style.transform = 'scale(.2)';
273			dot.style.opacity = '0';
274		});
275
276		dot.addEventListener(
277			'transitionend',
278			() => {
279				dot.remove();
280
281				const counter = document.querySelector('.wishlist-bookmark-counter');
282
283				if (counter) {
284					counter.classList.remove('bump');
285					requestAnimationFrame(() => counter.classList.add('bump'));
286				}
287			},
288			{ once: true },
289		);
290	}
291
292	displayProducts() {
293		const container = document.querySelector('#wishlist-products .wishlist-inner');
294		const template = document.querySelector('#colours-item');
295		const header = document.querySelector('#wishlist-products');
296		const productsField = document.querySelector('input.requested-samples');
297
298		if (!container || !template) {
299			return;
300		}
301
302		this.displayCount();
303
304		const products = this.getItems();
305
306		productsField.value = products.map((product) => `${product.id} - ${product.name}`).join('\n');
307
308		this.clearProductsValidation(productsField);
309
310		products.forEach((product) => {
311			if (!template) return;
312			const clone = template.content.cloneNode(true);
313
314			clone.querySelector('.colours-block').dataset.id = product.id;
315			clone.querySelector('.product-link').href = product.href;
316
317			clone.querySelector('.aCor').style.backgroundImage = `url(${product.image})`;
318
319			clone.querySelector('.product-code').textContent = product.code;
320			clone.querySelector('.product-name').textContent = product.name;
321
322			const button = clone.querySelector('.wishlist-btn');
323
324			button.dataset.id = product.id;
325			button.dataset.name = product.name;
326			button.dataset.image = product.image;
327			button.dataset.code = product.code;
328			button.dataset.href = product.href;
329
330			container.appendChild(clone);
331		});
332
333		this.bindEvents();
334	}
335
336	// Validation
337
338	showProductsValidation(field) {
339		field.classList.add('wpcf7-not-valid');
340		field.setAttribute('aria-invalid', 'true');
341
342		// Não duplicar a mensagem
343		let errorTip = field.parentElement.querySelector('.wpcf7-not-valid-tip');
344
345		if (!errorTip) {
346			errorTip = document.createElement('span');
347			errorTip.className = 'wpcf7-not-valid-tip';
348			field.parentElement.appendChild(errorTip);
349		}
350	}
351
352	clearProductsValidation(field) {
353		field.classList.remove('wpcf7-not-valid');
354		field.removeAttribute('aria-invalid');
355
356		const errorTip = field.parentElement.querySelector('.wpcf7-not-valid-tip');
357
358		if (errorTip) {
359			errorTip.remove();
360		}
361	}
362
363	// Start
364
365	init() {
366		this.bindEvents();
367		this.updateButtons();
368		this.updateHeaderCounter();
369	}
370}
371
372document.addEventListener('DOMContentLoaded', () => {
373	const wishlist = new Wishlist();
374
375	wishlist.displayProducts();
376});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.