Skip to content

Commit 2abb9e4

Browse files
Merge pull request #510 from aziontech/fix/icons-gallery-buttons
fix: (icons-gallery) restore copy/download actions and gradient mask after refactor
2 parents 9ed7c70 + e9ff368 commit 2abb9e4

File tree

2 files changed

+144
-210
lines changed

2 files changed

+144
-210
lines changed

apps/icons-gallery/src/components/IconCard.vue

Lines changed: 143 additions & 209 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<script setup>
2-
// /* global ClipboardItem */
3-
/* global defineProps */
42
import { ref } from 'vue'
53
4+
// SVGs são importados lazily via Vite glob (substitui o require() do webpack)
5+
const svgModules = import.meta.glob(
6+
'../../node_modules/@aziontech/icons/src/svg-raw/**/*.svg',
7+
{ query: '?raw', import: 'default', eager: false }
8+
)
9+
610
// Props
711
const props = defineProps({
812
name: {
@@ -36,32 +40,80 @@
3640
const showImageCheckIcon = ref(false)
3741
const mySlot = ref(null)
3842
39-
// Methods
40-
// Extração do valor em pixels de uma string no formato `text-[XXpx]`
41-
// eslint-disable-next-line no-unused-vars
43+
// Extrai pixels de "20px" → 20
4244
function getPixelSize(sizeClass) {
4345
if (typeof sizeClass === 'string' && sizeClass.endsWith('px')) {
4446
return parseInt(sizeClass.replace('px', ''), 10)
4547
}
46-
return sizeClass // Se já for um número ou outra unidade que não é px, retorna diretamente
48+
return sizeClass
49+
}
50+
51+
// Carrega o SVG raw do ícone pelo nome (ex: "ai-ai-pillar" ou "pi-address-book")
52+
async function loadSvg(iconName) {
53+
const targetFile = `/${iconName}.svg`
54+
const key = Object.keys(svgModules).find((k) => k.endsWith(targetFile))
55+
if (!key) throw new Error(`SVG not found: ${iconName}`)
56+
return await svgModules[key]()
4757
}
4858
49-
// Mapeia as classes do Tailwind para tamanhos reais em pixels
50-
// eslint-disable-next-line no-unused-vars
51-
function getDimension(sizeClass) {
52-
const sizeMap = {
53-
'text-xs': 12,
54-
'text-sm': 16,
55-
'text-base': 20,
56-
'text-lg': 24,
57-
'text-xl': 28,
58-
'text-2xl': 32,
59-
'text-3xl': 40,
60-
'text-4xl': 48,
61-
'text-5xl': 56,
62-
'text-6xl': 64
59+
// Aplica cor e dimensão ao SVG (substitui currentColor e redimensiona)
60+
function applyColorAndSize(svg, color, size) {
61+
const dimension = getPixelSize(size)
62+
svg = svg.replace(/fill="currentColor"/g, `fill="${color}"`)
63+
64+
// Os SVGs não têm viewBox: captura o width/height original e cria um,
65+
// caso contrário os paths ficam em coordenadas 14×14 e não escalam.
66+
if (!svg.includes('viewBox')) {
67+
const origW = (svg.match(/width="([^"]*)"/) || [])[1] || '14'
68+
const origH = (svg.match(/height="([^"]*)"/) || [])[1] || '14'
69+
svg = svg.replace('<svg', `<svg viewBox="0 0 ${origW} ${origH}"`)
70+
}
71+
72+
svg = svg.replace(/\s*(width|height)="[^"]*"/g, '')
73+
svg = svg.replace('<svg', `<svg width="${dimension}" height="${dimension}"`)
74+
return svg
75+
}
76+
77+
// Renderiza SVG em canvas e retorna o blob PNG
78+
function svgToPngBlob(svgString, dimension) {
79+
return new Promise((resolve, reject) => {
80+
// xmlns é obrigatório para o browser renderizar SVG via img.src/blob URL
81+
if (!svgString.includes('xmlns=')) {
82+
svgString = svgString.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"')
83+
}
84+
85+
const canvas = document.createElement('canvas')
86+
canvas.width = dimension
87+
canvas.height = dimension
88+
const ctx = canvas.getContext('2d')
89+
const img = new Image()
90+
img.onload = () => {
91+
ctx.drawImage(img, 0, 0, dimension, dimension)
92+
canvas.toBlob((blob) => resolve(blob), 'image/png')
93+
URL.revokeObjectURL(img.src)
94+
}
95+
img.onerror = (e) => {
96+
URL.revokeObjectURL(img.src)
97+
reject(new Error(`Failed to load SVG as image: ${e}`))
98+
}
99+
const svgBlob = new Blob([svgString], { type: 'image/svg+xml' })
100+
img.src = URL.createObjectURL(svgBlob)
101+
})
102+
}
103+
104+
async function copyToClipboard(text) {
105+
if (navigator.clipboard && navigator.clipboard.writeText) {
106+
await navigator.clipboard.writeText(text)
107+
} else {
108+
const textArea = document.createElement('textarea')
109+
textArea.value = text
110+
textArea.style.position = 'fixed'
111+
textArea.style.left = '-9999px'
112+
document.body.appendChild(textArea)
113+
textArea.select()
114+
document.execCommand('copy')
115+
document.body.removeChild(textArea)
63116
}
64-
return sizeMap[sizeClass] || 100 // Valor padrão caso a classe não seja mapeada
65117
}
66118
67119
async function downloadIcon() {
@@ -73,96 +125,52 @@
73125
}
74126
75127
async function downloadSVG() {
76-
// try {
77-
// const iconPath = require(`@/assets/svg-raw/${props.name}.svg`);
78-
// const response = await fetch(iconPath);
79-
// if (!response.ok) throw new Error("Network response was not ok");
80-
// let svg = await response.text();
81-
// if (!svg.includes("fill=")) {
82-
// svg = svg.replace(/<path/g, `<path fill="${props.color}"`);
83-
// } else {
84-
// svg = svg.replace(/fill="[^"]*"/g, `fill="${props.color}"`);
85-
// }
86-
// const dimension = getPixelSize(props.size); // Usa o valor correto em pixels
87-
// if (!svg.includes("width=")) {
88-
// svg = svg.replace(
89-
// /<svg/,
90-
// `<svg width="${dimension}" height="${dimension}"`
91-
// );
92-
// } else {
93-
// svg = svg
94-
// .replace(/(width|height)="[^"]*"/g, "")
95-
// .replace(/<svg/, `<svg width="${dimension}" height="${dimension}"`);
96-
// }
97-
// const blob = new Blob([svg], { type: "image/svg+xml" });
98-
// const url = window.URL.createObjectURL(blob);
99-
// const link = document.createElement("a");
100-
// link.href = url;
101-
// link.download = `${props.name.toLowerCase()}.svg`;
102-
// document.body.appendChild(link);
103-
// link.click();
104-
// document.body.removeChild(link);
105-
// } catch (error) {
106-
// console.error("Failed to download SVG:", error);
107-
// }
128+
try {
129+
let svg = await loadSvg(props.name)
130+
svg = applyColorAndSize(svg, props.color, props.size)
131+
const blob = new Blob([svg], { type: 'image/svg+xml' })
132+
const url = URL.createObjectURL(blob)
133+
const link = document.createElement('a')
134+
link.href = url
135+
link.download = `${props.name.toLowerCase()}.svg`
136+
document.body.appendChild(link)
137+
link.click()
138+
document.body.removeChild(link)
139+
URL.revokeObjectURL(url)
140+
} catch (error) {
141+
console.error('Failed to download SVG:', error)
142+
}
108143
}
109144
110145
async function downloadPNG() {
111-
// try {
112-
// const iconPath = require(`@/assets/svg-raw/${props.name}.svg`);
113-
// const response = await fetch(iconPath);
114-
// if (!response.ok) throw new Error("Network response was not ok");
115-
// let svg = await response.text();
116-
// svg = svg.replace(/fill="[^"]*"/g, `fill="${props.color}"`);
117-
// const dimension = getDimension(props.size); // Usa o tamanho correto
118-
// svg = svg
119-
// .replace(/(width|height)="[^"]*"/g, "")
120-
// .replace(/<svg/, `<svg width="${dimension}" height="${dimension}"`);
121-
// const canvas = document.createElement("canvas");
122-
// canvas.width = dimension;
123-
// canvas.height = dimension;
124-
// const ctx = canvas.getContext("2d");
125-
// const img = new Image();
126-
// img.onload = () => {
127-
// ctx.drawImage(img, 0, 0, dimension, dimension);
128-
// canvas.toBlob((blob) => {
129-
// const element = document.createElement("a");
130-
// element.download = `${props.name.toLowerCase()}.png`;
131-
// element.href = window.URL.createObjectURL(blob);
132-
// element.click();
133-
// element.remove();
134-
// }, "image/png");
135-
// };
136-
// const svgBlob = new Blob([svg], { type: "image/svg+xml" });
137-
// img.src = URL.createObjectURL(svgBlob);
138-
// } catch (error) {
139-
// console.error("Failed to download PNG:", error);
140-
// }
146+
try {
147+
let svg = await loadSvg(props.name)
148+
const dimension = getPixelSize(props.size)
149+
svg = applyColorAndSize(svg, props.color, props.size)
150+
const blob = await svgToPngBlob(svg, dimension)
151+
const url = URL.createObjectURL(blob)
152+
const link = document.createElement('a')
153+
link.href = url
154+
link.download = `${props.name.toLowerCase()}.png`
155+
document.body.appendChild(link)
156+
link.click()
157+
document.body.removeChild(link)
158+
URL.revokeObjectURL(url)
159+
} catch (error) {
160+
console.error('Failed to download PNG:', error)
161+
}
141162
}
142163
143164
async function copyCode() {
144-
// try {
145-
// const textToCopy = `<i class='${props.icon.toLowerCase()}'></i>`;
146-
// if (navigator.clipboard && navigator.clipboard.writeText) {
147-
// await navigator.clipboard.writeText(textToCopy);
148-
// } else {
149-
// // Fallback for browsers without clipboard API
150-
// const textArea = document.createElement("textarea");
151-
// textArea.value = textToCopy;
152-
// textArea.style.position = "fixed";
153-
// textArea.style.left = "-9999px";
154-
// document.body.appendChild(textArea);
155-
// textArea.select();
156-
// document.execCommand("copy");
157-
// document.body.removeChild(textArea);
158-
// }
159-
// showCheckIcon.value = true;
160-
// setTimeout(() => {
161-
// showCheckIcon.value = false;
162-
// }, 1200);
163-
// } catch (error) {
164-
// console.error("Failed to copy:", error);
165-
// }
165+
try {
166+
await copyToClipboard(`<i class="${props.icon}"></i>`)
167+
showCheckIcon.value = true
168+
setTimeout(() => {
169+
showCheckIcon.value = false
170+
}, 1200)
171+
} catch (error) {
172+
console.error('Failed to copy:', error)
173+
}
166174
}
167175
168176
async function copyImage() {
@@ -174,113 +182,39 @@
174182
}
175183
176184
async function copySVG() {
177-
// try {
178-
// const iconPath = require(`@/assets/svg-raw/${props.name}.svg`);
179-
// const response = await fetch(iconPath);
180-
// if (!response.ok) throw new Error("Network response was not ok");
181-
// let svg = await response.text();
182-
// if (!svg.includes("fill=")) {
183-
// svg = svg.replace(/<path/g, `<path fill="${props.color}"`);
184-
// } else {
185-
// svg = svg.replace(/fill="[^"]*"/g, `fill="${props.color}"`);
186-
// }
187-
// const dimension = getPixelSize(props.size); // Usa o valor correto em pixels
188-
// if (!svg.includes("width=")) {
189-
// svg = svg.replace(
190-
// /<svg/,
191-
// `<svg width="${dimension}" height="${dimension}"`
192-
// );
193-
// } else {
194-
// svg = svg
195-
// .replace(/(width|height)="[^"]*"/g, "")
196-
// .replace(/<svg/, `<svg width="${dimension}" height="${dimension}"`);
197-
// }
198-
// if (navigator.clipboard && navigator.clipboard.writeText) {
199-
// await navigator.clipboard.writeText(svg);
200-
// } else {
201-
// // Fallback for browsers without clipboard API
202-
// const textArea = document.createElement("textarea");
203-
// textArea.value = svg;
204-
// textArea.style.position = "fixed";
205-
// textArea.style.left = "-9999px";
206-
// document.body.appendChild(textArea);
207-
// textArea.select();
208-
// document.execCommand("copy");
209-
// document.body.removeChild(textArea);
210-
// }
211-
// console.log("SVG content copied to clipboard!");
212-
// showImageCheckIcon.value = true;
213-
// setTimeout(() => {
214-
// showImageCheckIcon.value = false;
215-
// }, 1200);
216-
// } catch (error) {
217-
// console.error("Failed to copy SVG content:", error);
218-
// }
185+
try {
186+
let svg = await loadSvg(props.name)
187+
svg = applyColorAndSize(svg, props.color, props.size)
188+
await copyToClipboard(svg)
189+
showImageCheckIcon.value = true
190+
setTimeout(() => {
191+
showImageCheckIcon.value = false
192+
}, 1200)
193+
} catch (error) {
194+
console.error('Failed to copy SVG:', error)
195+
}
219196
}
220197
221198
async function copyPNG() {
222-
// try {
223-
// const iconPath = require(`@/assets/svg-raw/${props.name}.svg`);
224-
// const response = await fetch(iconPath);
225-
// if (!response.ok) throw new Error("Network response was not ok");
226-
// let svg = await response.text();
227-
// svg = svg.replace(/fill="[^"]*"/g, `fill="${props.color}"`);
228-
// const dimension = getPixelSize(props.size); // Usa o valor correto em pixels
229-
// svg = svg
230-
// .replace(/(width|height)="[^"]*"/g, "")
231-
// .replace(/<svg/, `<svg width="${dimension}" height="${dimension}"`);
232-
// const canvas = document.createElement("canvas");
233-
// canvas.width = dimension;
234-
// canvas.height = dimension;
235-
// const ctx = canvas.getContext("2d");
236-
// const img = new Image();
237-
// img.onload = async () => {
238-
// ctx.drawImage(img, 0, 0, dimension, dimension);
239-
// canvas.toBlob(async (blob) => {
240-
// try {
241-
// if (
242-
// typeof ClipboardItem !== "undefined" &&
243-
// navigator.clipboard &&
244-
// navigator.clipboard.write
245-
// ) {
246-
// const clipboardItem = new ClipboardItem({ "image/png": blob });
247-
// await navigator.clipboard.write([clipboardItem]);
248-
// console.log("PNG copied to clipboard!");
249-
// } else {
250-
// // Fallback: copy as base64 data URL text
251-
// const reader = new FileReader();
252-
// reader.onloadend = async () => {
253-
// const base64data = reader.result;
254-
// if (navigator.clipboard && navigator.clipboard.writeText) {
255-
// await navigator.clipboard.writeText(base64data);
256-
// } else {
257-
// const textArea = document.createElement("textarea");
258-
// textArea.value = base64data;
259-
// textArea.style.position = "fixed";
260-
// textArea.style.left = "-9999px";
261-
// document.body.appendChild(textArea);
262-
// textArea.select();
263-
// document.execCommand("copy");
264-
// document.body.removeChild(textArea);
265-
// }
266-
// console.log("PNG copied as base64 to clipboard!");
267-
// };
268-
// reader.readAsDataURL(blob);
269-
// }
270-
// showImageCheckIcon.value = true;
271-
// setTimeout(() => {
272-
// showImageCheckIcon.value = false;
273-
// }, 1200);
274-
// } catch (err) {
275-
// console.error("Failed to copy PNG to clipboard:", err);
276-
// }
277-
// }, "image/png");
278-
// };
279-
// const svgBlob = new Blob([svg], { type: "image/svg+xml" });
280-
// img.src = URL.createObjectURL(svgBlob);
281-
// } catch (error) {
282-
// console.error("Failed to copy PNG:", error);
283-
// }
199+
try {
200+
let svg = await loadSvg(props.name)
201+
const dimension = getPixelSize(props.size)
202+
svg = applyColorAndSize(svg, props.color, props.size)
203+
const blob = await svgToPngBlob(svg, dimension)
204+
if (typeof ClipboardItem !== 'undefined' && navigator.clipboard?.write) {
205+
await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })])
206+
} else {
207+
const reader = new FileReader()
208+
reader.onloadend = () => copyToClipboard(reader.result)
209+
reader.readAsDataURL(blob)
210+
}
211+
showImageCheckIcon.value = true
212+
setTimeout(() => {
213+
showImageCheckIcon.value = false
214+
}, 1200)
215+
} catch (error) {
216+
console.error('Failed to copy PNG:', error)
217+
}
284218
}
285219
</script>
286220
<template>

0 commit comments

Comments
 (0)