refactor: change to use iframe

feat: add more blocks
This commit is contained in:
zernonia 2024-03-23 17:19:01 +08:00
parent cb962888ab
commit 6c66e974e5
15 changed files with 485 additions and 11 deletions

View File

@ -0,0 +1,12 @@
<script setup lang="ts">
import { useUrlSearchParams } from '@vueuse/core'
import ComponentLoader from './ComponentLoader.vue'
const params = useUrlSearchParams('hash-params')
</script>
<template>
<div :class="params.containerClass">
<ComponentLoader :name="params.name.toString()" :type-name="'block'" />
</div>
</template>

View File

@ -7,7 +7,6 @@ import MagicString from 'magic-string'
import { cssVariables } from '../config/shiki' import { cssVariables } from '../config/shiki'
import StyleSwitcher from './StyleSwitcher.vue' import StyleSwitcher from './StyleSwitcher.vue'
import Spinner from './Spinner.vue' import Spinner from './Spinner.vue'
import ComponentLoader from './ComponentLoader.vue'
import { useConfigStore } from '@/stores/config' import { useConfigStore } from '@/stores/config'
// import { BlockCopyCodeButton } from '@/components/block-copy-code-button' // import { BlockCopyCodeButton } from '@/components/block-copy-code-button'
@ -26,7 +25,10 @@ const props = defineProps<{
const { style, codeConfig } = useConfigStore() const { style, codeConfig } = useConfigStore()
const isLoading = ref(true)
const tabValue = ref('preview')
const resizableRef = ref<InstanceType<typeof ResizablePanel>>() const resizableRef = ref<InstanceType<typeof ResizablePanel>>()
const rawString = ref('') const rawString = ref('')
const codeHtml = ref('') const codeHtml = ref('')
const metadata = reactive({ const metadata = reactive({
@ -90,10 +92,10 @@ watch([style, codeConfig], async () => {
<template> <template>
<Tabs <Tabs
:id="name" :id="name"
default-value="preview" v-model="tabValue"
class="relative grid w-full scroll-m-20 gap-4" class="relative grid w-full scroll-m-20 gap-4"
:style=" { :style=" {
'--container-height': metadata.iframeHeight, '--container-height': metadata.iframeHeight ?? '600px',
}" }"
> >
<div class="flex flex-col items-center gap-4 sm:flex-row"> <div class="flex flex-col items-center gap-4 sm:flex-row">
@ -205,6 +207,8 @@ watch([style, codeConfig], async () => {
</div> </div>
</div> </div>
<TabsContent <TabsContent
v-show="tabValue === 'preview'"
force-mount
value="preview" value="preview"
class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0" class="relative after:absolute after:inset-0 after:right-3 after:z-0 after:rounded-lg after:bg-muted h-[--container-height] px-0"
> >
@ -216,9 +220,15 @@ watch([style, codeConfig], async () => {
:default-size="100" :default-size="100"
:min-size="25" :min-size="25"
> >
<div :class="metadata.containerClass"> <div v-if="isLoading" class="flex items-center justify-center h-full">
<ComponentLoader :key="style" :name="name" :type-name="'block'" /> <Spinner />
</div> </div>
<iframe
v-show="!isLoading"
:src="`/blocks/renderer#name=${name}&style=${style}&containerClass=${encodeURIComponent(metadata.containerClass ?? '')}`"
class="relative z-20 w-full bg-background h-[--container-height]"
@load="isLoading = false"
/>
</ResizablePanel> </ResizablePanel>
<ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" /> <ResizableHandle id="block-resizable-handle" class="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 sm:block" />
<ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" /> <ResizablePanel id="block-resizable-panel-2" :default-size="0" :min-size="0" />
@ -226,7 +236,7 @@ watch([style, codeConfig], async () => {
</TabsContent> </TabsContent>
<TabsContent value="code" class="h-[--container-height]"> <TabsContent value="code" class="h-[--container-height]">
<div <div
class="language-vue !h-full !mt-0" class="language-vue !h-full !max-h-[none] !mt-0"
v-html="codeHtml" v-html="codeHtml"
/> />
</TabsContent> </TabsContent>

View File

@ -57,7 +57,7 @@ import('../../../__registry__/index').then((res) => {
</PageAction> </PageAction>
</PageHeader> </PageHeader>
<section> <section class="grid scroll-mt-24 gap-24 lg:gap-48">
<BlockPreview v-for="block in blocks" :key="block" :name="block" /> <BlockPreview v-for="block in blocks" :key="block" :name="block" />
</section> </section>
</template> </template>

View File

@ -84,7 +84,10 @@ watch(() => $route.path, (n) => {
</script> </script>
<template> <template>
<div vaul-drawer-wrapper class="flex min-h-screen flex-col bg-background"> <div v-if="$route.data.frontmatter.layout === false">
<Content :key="$route.path" />
</div>
<div v-else vaul-drawer-wrapper class="flex min-h-screen flex-col bg-background">
<header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border"> <header class="sticky z-40 top-0 bg-background/80 backdrop-blur-lg border-b border-border">
<div <div
class="container flex h-14 max-w-screen-2xl items-center" class="container flex h-14 max-w-screen-2xl items-center"
@ -298,4 +301,4 @@ watch(() => $route.path, (n) => {
<NewYorkSonner :theme="'system'" /> <NewYorkSonner :theme="'system'" />
<NewYorkToaster /> <NewYorkToaster />
</div> </div>
</template>../components/CodeConfigCustomizer.vue </template>

View File

@ -1109,6 +1109,27 @@ export const Index = {
component: () => import("../src/lib/registry/default/block/Authentication01.vue").then((m) => m.default), component: () => import("../src/lib/registry/default/block/Authentication01.vue").then((m) => m.default),
files: ["../src/lib/registry/default/block/Authentication01.vue"], files: ["../src/lib/registry/default/block/Authentication01.vue"],
}, },
"Authentication02": {
name: "Authentication02",
type: "components:block",
registryDependencies: ["button","card","input","label"],
component: () => import("../src/lib/registry/default/block/Authentication02.vue").then((m) => m.default),
files: ["../src/lib/registry/default/block/Authentication02.vue"],
},
"Authentication03": {
name: "Authentication03",
type: "components:block",
registryDependencies: ["button","card","input","label"],
component: () => import("../src/lib/registry/default/block/Authentication03.vue").then((m) => m.default),
files: ["../src/lib/registry/default/block/Authentication03.vue"],
},
"Authentication04": {
name: "Authentication04",
type: "components:block",
registryDependencies: ["button","input","label"],
component: () => import("../src/lib/registry/default/block/Authentication04.vue").then((m) => m.default),
files: ["../src/lib/registry/default/block/Authentication04.vue"],
},
}, "new-york": { }, "new-york": {
"AccordionDemo": { "AccordionDemo": {
name: "AccordionDemo", name: "AccordionDemo",
@ -2216,5 +2237,26 @@ export const Index = {
component: () => import("../src/lib/registry/new-york/block/Authentication01.vue").then((m) => m.default), component: () => import("../src/lib/registry/new-york/block/Authentication01.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/block/Authentication01.vue"], files: ["../src/lib/registry/new-york/block/Authentication01.vue"],
}, },
"Authentication02": {
name: "Authentication02",
type: "components:block",
registryDependencies: ["button","card","input","label"],
component: () => import("../src/lib/registry/new-york/block/Authentication02.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/block/Authentication02.vue"],
},
"Authentication03": {
name: "Authentication03",
type: "components:block",
registryDependencies: ["button","card","input","label"],
component: () => import("../src/lib/registry/new-york/block/Authentication03.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/block/Authentication03.vue"],
},
"Authentication04": {
name: "Authentication04",
type: "components:block",
registryDependencies: ["button","input","label"],
component: () => import("../src/lib/registry/new-york/block/Authentication04.vue").then((m) => m.default),
files: ["../src/lib/registry/new-york/block/Authentication04.vue"],
},
}, },
} }

View File

@ -0,0 +1,10 @@
---
title: Blocks - shadcn-vue
layout: false
---
<script setup>
import BlockPage from "../../../.vitepress/theme/components/BlockPage.vue"
</script>
<BlockPage />

View File

@ -2,7 +2,7 @@
export const description export const description
= 'A simple login form with email and password. The submit button says \'Sign in\'.' = 'A simple login form with email and password. The submit button says \'Sign in\'.'
export const iframeHeight = '600px' export const iframeHeight = '600px'
export const containerClass = 'w-full h-full flex items-center justify-center px-4' export const containerClass = 'w-full h-screen flex items-center justify-center px-4'
</script> </script>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -0,0 +1,60 @@
<script lang="ts">
export const description
= 'A login form with email and password. There\'s an option to login with Google and a link to sign up if you don\'t have an account.'
export const iframeHeight = '600px'
export const containerClass = 'w-full h-screen flex items-center justify-center px-4'
</script>
<script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/lib/registry/default/ui/card'
import { Input } from '@/lib/registry/default/ui/input'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<Card class="mx-auto max-w-sm">
<CardHeader>
<CardTitle class="text-2xl">
Login
</CardTitle>
<CardDescription>
Enter your email below to login to your account
</CardDescription>
</CardHeader>
<CardContent>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div class="grid gap-2">
<div class="flex items-center">
<Label for="password">Password</Label>
<a href="#" class="ml-auto inline-block text-sm underline">
Forgot your password?
</a>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" class="w-full">
Login
</Button>
<Button variant="outline" class="w-full">
Login with Google
</Button>
</div>
<div class="mt-4 text-center text-sm">
Don't have an account?
<a href="#" class="underline">
Sign up
</a>
</div>
</CardContent>
</Card>
</template>

View File

@ -0,0 +1,65 @@
<script lang="ts">
export const description
= 'A sign up form with first name, last name, email and password inside a card. There\'s an option to sign up with GitHub and a link to login if you already have an account'
export const iframeHeight = '600px'
export const containerClass = 'w-full h-screen flex items-center justify-center px-4'
</script>
<script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/lib/registry/default/ui/card'
import { Input } from '@/lib/registry/default/ui/input'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<Card class="mx-auto max-w-sm">
<CardHeader>
<CardTitle class="text-xl">
Sign Up
</CardTitle>
<CardDescription>
Enter your information to create an account
</CardDescription>
</CardHeader>
<CardContent>
<div class="grid gap-4">
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="first-name">First name</Label>
<Input id="first-name" placeholder="Max" required />
</div>
<div class="grid gap-2">
<Label for="last-name">Last name</Label>
<Input id="last-name" placeholder="Robinson" required />
</div>
</div>
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div class="grid gap-2">
<Label for="password">Password</Label>
<Input id="password" type="password" />
</div>
<Button type="submit" class="w-full">
Create an account
</Button>
<Button variant="outline" class="w-full">
Sign up with GitHub
</Button>
</div>
<div class="mt-4 text-center text-sm">
Already have an account?
<a href="#" class="underline">
Sign in
</a>
</div>
</CardContent>
</Card>
</template>

View File

@ -0,0 +1,73 @@
<script lang="ts">
export const description
= 'A login page with two columns. The first column has the login form with email and password. There\'s a Forgot your passwork link and a link to sign up if you do not have an account. The second column has a cover image.'
export const iframeHeight = '800px'
export const containerClass = 'w-full h-full p-4 lg:p-0'
</script>
<script setup lang="ts">
import { Button } from '@/lib/registry/default/ui/button'
import { Input } from '@/lib/registry/default/ui/input'
import { Label } from '@/lib/registry/default/ui/label'
</script>
<template>
<div class="w-full lg:grid lg:min-h-[600px] lg:grid-cols-2 xl:min-h-[800px]">
<div class="flex items-center justify-center py-12">
<div class="mx-auto grid w-[350px] gap-6">
<div class="grid gap-2 text-center">
<h1 class="text-3xl font-bold">
Login
</h1>
<p class="text-balance text-muted-foreground">
Enter your email below to login to your account
</p>
</div>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div class="grid gap-2">
<div class="flex items-center">
<Label for="password">Password</Label>
<a
href="/forgot-password"
class="ml-auto inline-block text-sm underline"
>
Forgot your password?
</a>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" class="w-full">
Login
</Button>
<Button variant="outline" class="w-full">
Login with Google
</Button>
</div>
<div class="mt-4 text-center text-sm">
Don't have an account?
<a href="#" class="underline">
Sign up
</a>
</div>
</div>
</div>
<div class="hidden bg-muted lg:block">
<img
src="/placeholder.svg"
alt="Image"
width="1920"
height="1080"
class="h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
>
</div>
</div>
</template>

View File

@ -2,7 +2,7 @@
export const description export const description
= 'A simple login form with email and password. The submit button says \'Sign in\'.' = 'A simple login form with email and password. The submit button says \'Sign in\'.'
export const iframeHeight = '600px' export const iframeHeight = '600px'
export const containerClass = 'w-full h-full flex items-center justify-center px-4' export const containerClass = 'w-full h-screen flex items-center justify-center px-4'
</script> </script>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -0,0 +1,60 @@
<script lang="ts">
export const description
= 'A login form with email and password. There\'s an option to login with Google and a link to sign up if you don\'t have an account.'
export const iframeHeight = '600px'
export const containerClass = 'w-full h-screen flex items-center justify-center px-4'
</script>
<script setup lang="ts">
import { Button } from '@/lib/registry/new-york/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/lib/registry/new-york/ui/card'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Label } from '@/lib/registry/new-york/ui/label'
</script>
<template>
<Card class="mx-auto max-w-sm">
<CardHeader>
<CardTitle class="text-2xl">
Login
</CardTitle>
<CardDescription>
Enter your email below to login to your account
</CardDescription>
</CardHeader>
<CardContent>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div class="grid gap-2">
<div class="flex items-center">
<Label for="password">Password</Label>
<a href="#" class="ml-auto inline-block text-sm underline">
Forgot your password?
</a>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" class="w-full">
Login
</Button>
<Button variant="outline" class="w-full">
Login with Google
</Button>
</div>
<div class="mt-4 text-center text-sm">
Don't have an account?
<a href="#" class="underline">
Sign up
</a>
</div>
</CardContent>
</Card>
</template>

View File

@ -0,0 +1,65 @@
<script lang="ts">
export const description
= 'A sign up form with first name, last name, email and password inside a card. There\'s an option to sign up with GitHub and a link to login if you already have an account'
export const iframeHeight = '600px'
export const containerClass = 'w-full h-screen flex items-center justify-center px-4'
</script>
<script setup lang="ts">
import { Button } from '@/lib/registry/new-york/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/lib/registry/new-york/ui/card'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Label } from '@/lib/registry/new-york/ui/label'
</script>
<template>
<Card class="mx-auto max-w-sm">
<CardHeader>
<CardTitle class="text-xl">
Sign Up
</CardTitle>
<CardDescription>
Enter your information to create an account
</CardDescription>
</CardHeader>
<CardContent>
<div class="grid gap-4">
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="first-name">First name</Label>
<Input id="first-name" placeholder="Max" required />
</div>
<div class="grid gap-2">
<Label for="last-name">Last name</Label>
<Input id="last-name" placeholder="Robinson" required />
</div>
</div>
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div class="grid gap-2">
<Label for="password">Password</Label>
<Input id="password" type="password" />
</div>
<Button type="submit" class="w-full">
Create an account
</Button>
<Button variant="outline" class="w-full">
Sign up with GitHub
</Button>
</div>
<div class="mt-4 text-center text-sm">
Already have an account?
<a href="#" class="underline">
Sign in
</a>
</div>
</CardContent>
</Card>
</template>

View File

@ -0,0 +1,73 @@
<script lang="ts">
export const description
= 'A login page with two columns. The first column has the login form with email and password. There\'s a Forgot your passwork link and a link to sign up if you do not have an account. The second column has a cover image.'
export const iframeHeight = '800px'
export const containerClass = 'w-full h-full p-4 lg:p-0'
</script>
<script setup lang="ts">
import { Button } from '@/lib/registry/new-york/ui/button'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Label } from '@/lib/registry/new-york/ui/label'
</script>
<template>
<div class="w-full lg:grid lg:min-h-[600px] lg:grid-cols-2 xl:min-h-[800px]">
<div class="flex items-center justify-center py-12">
<div class="mx-auto grid w-[350px] gap-6">
<div class="grid gap-2 text-center">
<h1 class="text-3xl font-bold">
Login
</h1>
<p class="text-balance text-muted-foreground">
Enter your email below to login to your account
</p>
</div>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div class="grid gap-2">
<div class="flex items-center">
<Label for="password">Password</Label>
<a
href="/forgot-password"
class="ml-auto inline-block text-sm underline"
>
Forgot your password?
</a>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" class="w-full">
Login
</Button>
<Button variant="outline" class="w-full">
Login with Google
</Button>
</div>
<div class="mt-4 text-center text-sm">
Don't have an account?
<a href="#" class="underline">
Sign up
</a>
</div>
</div>
</div>
<div class="hidden bg-muted lg:block">
<img
src="/placeholder.svg"
alt="Image"
width="1920"
height="1080"
class="h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
>
</div>
</div>
</template>

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB