|
1 | 1 | <script setup> |
2 | | - // /* global ClipboardItem */ |
3 | | - /* global defineProps */ |
4 | 2 | import { ref } from 'vue' |
5 | 3 |
|
| 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 | +
|
6 | 10 | // Props |
7 | 11 | const props = defineProps({ |
8 | 12 | name: { |
|
36 | 40 | const showImageCheckIcon = ref(false) |
37 | 41 | const mySlot = ref(null) |
38 | 42 |
|
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 |
42 | 44 | function getPixelSize(sizeClass) { |
43 | 45 | if (typeof sizeClass === 'string' && sizeClass.endsWith('px')) { |
44 | 46 | return parseInt(sizeClass.replace('px', ''), 10) |
45 | 47 | } |
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]() |
47 | 57 | } |
48 | 58 |
|
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) |
63 | 116 | } |
64 | | - return sizeMap[sizeClass] || 100 // Valor padrão caso a classe não seja mapeada |
65 | 117 | } |
66 | 118 |
|
67 | 119 | async function downloadIcon() { |
|
73 | 125 | } |
74 | 126 |
|
75 | 127 | 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 | + } |
108 | 143 | } |
109 | 144 |
|
110 | 145 | 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 | + } |
141 | 162 | } |
142 | 163 |
|
143 | 164 | 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 | + } |
166 | 174 | } |
167 | 175 |
|
168 | 176 | async function copyImage() { |
|
174 | 182 | } |
175 | 183 |
|
176 | 184 | 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 | + } |
219 | 196 | } |
220 | 197 |
|
221 | 198 | 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 | + } |
284 | 218 | } |
285 | 219 | </script> |
286 | 220 | <template> |
|
0 commit comments