a tool for shared writing and social publishing
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Feature/user made templates (#85)

* init stuff for end user templating

* wire up template stuff!

* add key to template list

* new icon for template

* fix route type error

---------

Co-authored-by: celine <celine@hyperlink.academy>

authored by

Jared Pereira
celine
and committed by
GitHub
2173997a 65e9c73f

+420 -158
+126
actions/createNewLeafletFromTemplate.ts
··· 1 + "use server"; 2 + 3 + import { createServerClient } from "@supabase/ssr"; 4 + import { drizzle } from "drizzle-orm/postgres-js"; 5 + import { NextRequest } from "next/server"; 6 + import postgres from "postgres"; 7 + import { Fact } from "src/replicache"; 8 + import { Attributes } from "src/replicache/attributes"; 9 + import { Database } from "supabase/database.types"; 10 + import { v7 } from "uuid"; 11 + 12 + import { 13 + entities, 14 + permission_tokens, 15 + permission_token_rights, 16 + entity_sets, 17 + facts, 18 + } from "drizzle/schema"; 19 + import { sql } from "drizzle-orm"; 20 + import { redirect } from "next/navigation"; 21 + 22 + let supabase = createServerClient<Database>( 23 + process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, 24 + process.env.SUPABASE_SERVICE_ROLE_KEY as string, 25 + { cookies: {} }, 26 + ); 27 + 28 + export async function createNewLeafletFromTemplate( 29 + template_id: string, 30 + redirectUser?: boolean, 31 + ) { 32 + let res = await supabase 33 + .from("permission_tokens") 34 + .select("*, permission_token_rights(*)") 35 + .eq("id", template_id) 36 + .single(); 37 + let rootEntity = res.data?.root_entity; 38 + if (!rootEntity || !res.data) return { title: "Leaflet not found" }; 39 + let { data } = await supabase.rpc("get_facts", { 40 + root: rootEntity, 41 + }); 42 + let initialFacts = (data as unknown as Fact<keyof typeof Attributes>[]) || []; 43 + 44 + let oldEntityIDToNewID = {} as { [k: string]: string }; 45 + let oldEntities = initialFacts.reduce((acc, f) => { 46 + if (!acc.includes(f.entity)) acc.push(f.entity); 47 + return acc; 48 + }, [] as string[]); 49 + let newEntities = [] as string[]; 50 + 51 + for (let oldEntity of oldEntities) { 52 + let newEntity = v7(); 53 + oldEntityIDToNewID[oldEntity] = newEntity; 54 + newEntities.push(newEntity); 55 + } 56 + 57 + let newFacts = [] as Array<Pick<Fact<any>, "entity" | "attribute" | "data">>; 58 + for (let fact of initialFacts) { 59 + let entity = oldEntityIDToNewID[fact.entity]; 60 + let data = fact.data; 61 + if ( 62 + data.type === "ordered-reference" || 63 + data.type == "spatial-reference" || 64 + data.type === "reference" 65 + ) { 66 + data.value = oldEntityIDToNewID[data.value]; 67 + } 68 + if (data.type === "image") { 69 + let url = data.src.split("?"); 70 + let paths = url[0].split("/"); 71 + let newID = v7(); 72 + await supabase.storage 73 + .from("minilink-user-assets") 74 + .copy(paths[paths.length - 1], newID); 75 + let newPath = [...paths]; 76 + newPath[newPath.length - 1] = newID; 77 + let newURL = newPath.join("/"); 78 + if (url[1]) newURL += `?${url[1]}`; 79 + data.src = newURL; 80 + } 81 + newFacts.push({ entity, attribute: fact.attribute, data }); 82 + } 83 + 84 + const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 85 + const db = drizzle(client); 86 + 87 + let { permissionToken } = await db.transaction(async (tx) => { 88 + // Create a new entity set 89 + let [entity_set] = await tx.insert(entity_sets).values({}).returning(); 90 + await tx 91 + .insert(entities) 92 + .values(newEntities.map((e) => ({ id: e, set: entity_set.id }))); 93 + await tx.insert(facts).values( 94 + newFacts.map((f) => ({ 95 + id: v7(), 96 + entity: f.entity, 97 + attribute: f.attribute, 98 + data: sql`${f.data}`, 99 + })), 100 + ); 101 + 102 + let [permissionToken] = await tx 103 + .insert(permission_tokens) 104 + .values({ root_entity: oldEntityIDToNewID[rootEntity] }) 105 + .returning(); 106 + 107 + //and give it all the permission on that entity set 108 + let [rights] = await tx 109 + .insert(permission_token_rights) 110 + .values({ 111 + token: permissionToken.id, 112 + entity_set: entity_set.id, 113 + read: true, 114 + write: true, 115 + create_token: true, 116 + change_entity_set: true, 117 + }) 118 + .returning(); 119 + 120 + return { permissionToken, rights, entity_set }; 121 + }); 122 + 123 + client.end(); 124 + if (redirectUser) redirect(`/${permissionToken.id}`); 125 + return permissionToken.id; 126 + }
+14 -16
app/[leaflet_id]/Leaflet.tsx
··· 32 32 <EntitySetProvider 33 33 set={props.token.permission_token_rights[0].entity_set} 34 34 > 35 - <PopUpProvider> 36 - <ThemeProvider entityID={props.leaflet_id}> 37 - <ThemeBackgroundProvider entityID={props.leaflet_id}> 38 - <UpdateLeafletTitle entityID={props.leaflet_id} /> 39 - <AddLeafletToHomepage /> 40 - <SelectionManager /> 41 - <div 42 - className="leafletContentWrapper w-full relative overflow-x-scroll snap-x snap-mandatory no-scrollbar grow items-stretch flex h-full" 43 - id="page-carousel" 44 - > 45 - <Pages rootPage={props.leaflet_id} /> 46 - </div> 47 - <MobileFooter entityID={props.leaflet_id} /> 48 - </ThemeBackgroundProvider> 49 - </ThemeProvider> 50 - </PopUpProvider> 35 + <ThemeProvider entityID={props.leaflet_id}> 36 + <ThemeBackgroundProvider entityID={props.leaflet_id}> 37 + <UpdateLeafletTitle entityID={props.leaflet_id} /> 38 + <AddLeafletToHomepage /> 39 + <SelectionManager /> 40 + <div 41 + className="leafletContentWrapper w-full relative overflow-x-scroll snap-x snap-mandatory no-scrollbar grow items-stretch flex h-full" 42 + id="page-carousel" 43 + > 44 + <Pages rootPage={props.leaflet_id} /> 45 + </div> 46 + <MobileFooter entityID={props.leaflet_id} /> 47 + </ThemeBackgroundProvider> 48 + </ThemeProvider> 51 49 </EntitySetProvider> 52 50 </ReplicacheProvider> 53 51 );
+51 -2
app/home/CreateNewButton.tsx
··· 1 1 "use client"; 2 2 3 3 import { createNewLeaflet } from "actions/createNewLeaflet"; 4 + import { createNewLeafletFromTemplate } from "actions/createNewLeafletFromTemplate"; 4 5 import { HoverButton } from "components/Buttons"; 5 6 import { 6 7 AddTiny, 7 8 BlockCanvasPageSmall, 8 9 BlockDocPageSmall, 10 + TemplateSmall, 9 11 } from "components/Icons"; 10 12 import { Menu, MenuItem } from "components/Layout"; 13 + import { create } from "zustand"; 14 + import { combine, createJSONStorage, persist } from "zustand/middleware"; 11 15 16 + export const useTemplateState = create( 17 + persist( 18 + combine( 19 + { 20 + templates: [] as { id: string; name: string }[], 21 + }, 22 + (set) => ({ 23 + removeTemplate: (template: { id: string }) => 24 + set((state) => { 25 + return { 26 + templates: state.templates.filter((t) => t.id !== template.id), 27 + }; 28 + }), 29 + addTemplate: (template: { id: string; name: string }) => 30 + set((state) => { 31 + if (state.templates.find((t) => t.id === template.id)) return state; 32 + return { templates: [...state.templates, template] }; 33 + }), 34 + }), 35 + ), 36 + { 37 + name: "home-templates", 38 + storage: createJSONStorage(() => localStorage), 39 + }, 40 + ), 41 + ); 12 42 export const CreateNewLeafletButton = (props: {}) => { 43 + let templates = useTemplateState((s) => s.templates); 13 44 return ( 14 45 <Menu 15 46 trigger={ 16 47 <HoverButton 48 + id="new-leaflet-button" 17 49 noLabelOnMobile 18 50 icon=<AddTiny className="m-1 shrink-0" /> 19 51 label="New Leaflet" ··· 30 62 > 31 63 <BlockDocPageSmall />{" "} 32 64 <div className="flex flex-col"> 33 - <div>Start New Doc</div> 65 + <div>New Doc</div> 34 66 <div className="text-tertiary text-sm font-normal"> 35 67 A good ol&apos; text document 36 68 </div> ··· 44 76 > 45 77 <BlockCanvasPageSmall /> 46 78 <div className="flex flex-col"> 47 - Start New Canvas 79 + New Canvas 48 80 <div className="text-tertiary text-sm font-normal"> 49 81 A digital whiteboard 50 82 </div> 51 83 </div> 52 84 </MenuItem> 85 + {templates.length > 0 && ( 86 + <hr className="border-border-light mx-2 mb-0.5" /> 87 + )} 88 + {templates.map((t) => { 89 + return ( 90 + <MenuItem 91 + key={t.id} 92 + onSelect={async () => { 93 + let id = await createNewLeafletFromTemplate(t.id, false); 94 + window.open(`/${id}`, "_blank"); 95 + }} 96 + > 97 + <TemplateSmall /> 98 + New {t.name} 99 + </MenuItem> 100 + ); 101 + })} 53 102 </Menu> 54 103 ); 55 104 };
+2 -2
app/home/LeafletList.tsx
··· 12 12 }); 13 13 14 14 return ( 15 - <div className="homeLeafletGrid grow w-full h-full overflow-y-scroll no-scrollbar pt-3 pb-28 sm:pt-6 sm:pb-12 "> 16 - <div className="grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-8 gap-x-4 sm:gap-6 grow"> 15 + <div className="homeLeafletGrid grow w-full h-full overflow-y-scroll no-scrollbar "> 16 + <div className="grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-8 gap-x-4 sm:gap-6 grow pt-3 pb-28 sm:pt-6 sm:pb-12 sm:pl-6"> 17 17 {leaflets 18 18 .sort((a, b) => (a.added_at > b.added_at ? -1 : 1)) 19 19 .filter((d) => !d.hidden)
+114 -11
app/home/LeafletOptions.tsx
··· 1 1 "use client"; 2 - import { DeleteSmall, HideSmall, MoreOptionsTiny } from "components/Icons"; 2 + import { 3 + DeleteSmall, 4 + HideSmall, 5 + MoreOptionsTiny, 6 + TemplateRemoveSmall, 7 + TemplateSmall, 8 + } from "components/Icons"; 3 9 import { Menu, MenuItem } from "components/Layout"; 4 10 import { PermissionToken } from "src/replicache"; 5 11 import { mutate } from "swr"; 6 12 import { hideDoc } from "./storage"; 13 + import { useState } from "react"; 14 + import { ButtonPrimary } from "components/Buttons"; 15 + import { useTemplateState } from "./CreateNewButton"; 16 + import { Item } from "@radix-ui/react-dropdown-menu"; 17 + import { useSmoker } from "components/Toast"; 7 18 8 19 export const LeafletOptions = (props: { 9 20 leaflet: PermissionToken; 10 - setState: (s: "normal" | "deleting") => void; 21 + isTemplate: boolean; 11 22 }) => { 23 + let [state, setState] = useState<"normal" | "template">("normal"); 24 + let [open, setOpen] = useState(false); 25 + let smoker = useSmoker(); 12 26 return ( 13 27 <> 14 28 <Menu 29 + open={open} 15 30 align="end" 31 + onOpenChange={(o) => { 32 + setOpen(o); 33 + setState("normal"); 34 + }} 16 35 trigger={ 17 36 <div className="bg-accent-1 text-accent-2 px-2 py-1 border border-accent-2 rounded-md"> 18 37 <MoreOptionsTiny /> 19 38 </div> 20 39 } 21 40 > 22 - <MenuItem 23 - onSelect={() => { 24 - hideDoc(props.leaflet); 25 - mutate("leaflets"); 26 - }} 27 - > 28 - <HideSmall /> 29 - Remove from home 30 - </MenuItem> 41 + {state === "normal" ? ( 42 + <> 43 + {!props.isTemplate ? ( 44 + <MenuItem 45 + onSelect={(e) => { 46 + e.preventDefault(); 47 + setState("template"); 48 + }} 49 + > 50 + <TemplateSmall /> Designate as Template 51 + </MenuItem> 52 + ) : ( 53 + <MenuItem 54 + onSelect={(e) => { 55 + useTemplateState.getState().removeTemplate(props.leaflet); 56 + let newLeafletButton = 57 + document.getElementById("new-leaflet-button"); 58 + if (!newLeafletButton) return; 59 + let rect = newLeafletButton.getBoundingClientRect(); 60 + smoker({ 61 + static: true, 62 + text: <strong>Removed template!</strong>, 63 + position: { 64 + y: rect.top, 65 + x: rect.right + 5, 66 + }, 67 + }); 68 + }} 69 + > 70 + <TemplateRemoveSmall /> Remove from Templates 71 + </MenuItem> 72 + )} 73 + <MenuItem 74 + onSelect={() => { 75 + hideDoc(props.leaflet); 76 + mutate("leaflets"); 77 + }} 78 + > 79 + <HideSmall /> 80 + Remove from home 81 + </MenuItem> 82 + </> 83 + ) : state === "template" ? ( 84 + <AddTemplateForm 85 + leaflet={props.leaflet} 86 + close={() => setOpen(false)} 87 + /> 88 + ) : null} 31 89 </Menu> 32 90 </> 33 91 ); 34 92 }; 93 + 94 + const AddTemplateForm = (props: { 95 + leaflet: PermissionToken; 96 + close: () => void; 97 + }) => { 98 + let [name, setName] = useState(""); 99 + let smoker = useSmoker(); 100 + return ( 101 + <div className="flex flex-col gap-2 px-3 py-1"> 102 + <label className="font-bold flex flex-col gap-1 text-secondary"> 103 + Template Name 104 + <input 105 + value={name} 106 + onChange={(e) => setName(e.target.value)} 107 + type="text" 108 + className=" text-primary font-normal border border-border rounded-md outline-none px-2 py-1 w-64" 109 + /> 110 + </label> 111 + 112 + <ButtonPrimary 113 + onClick={() => { 114 + useTemplateState.getState().addTemplate({ 115 + name, 116 + id: props.leaflet.id, 117 + }); 118 + let newLeafletButton = document.getElementById("new-leaflet-button"); 119 + if (!newLeafletButton) return; 120 + let rect = newLeafletButton.getBoundingClientRect(); 121 + smoker({ 122 + static: true, 123 + text: <strong>Added {name}!</strong>, 124 + position: { 125 + y: rect.top, 126 + x: rect.right + 5, 127 + }, 128 + }); 129 + props.close(); 130 + }} 131 + className="place-self-end" 132 + > 133 + Add Template 134 + </ButtonPrimary> 135 + </div> 136 + ); 137 + };
+19 -3
app/home/LeafletPreview.tsx
··· 21 21 import { LeafletOptions } from "./LeafletOptions"; 22 22 import { CanvasContent } from "components/Canvas"; 23 23 import { useSubscribe } from "replicache-react"; 24 + import { TemplateSmall } from "components/Icons"; 25 + import { theme } from "tailwind.config"; 26 + import { useTemplateState } from "./CreateNewButton"; 24 27 25 28 export const LeafletPreview = (props: { 26 29 token: PermissionToken; 27 30 leaflet_id: string; 28 31 }) => { 29 32 let [state, setState] = useState<"normal" | "deleting">("normal"); 30 - let parentRootPage = useReferenceToEntity("root/page", props.leaflet_id)[0] 31 - ?.entity; 33 + let isTemplate = useTemplateState( 34 + (s) => !!s.templates.find((t) => t.id === props.token.id), 35 + ); 32 36 let root = 33 37 useReferenceToEntity("root/page", props.leaflet_id)[0]?.entity || 34 38 props.leaflet_id; 35 39 let firstPage = useEntity(root, "root/page")[0]; 36 40 let page = firstPage?.data.value || root; 41 + 37 42 return ( 38 43 <div className="relative max-h-40 h-40"> 39 44 <ThemeProvider local entityID={root}> ··· 62 67 )} 63 68 </div> 64 69 <div className="flex justify-end pt-1 shrink-0"> 65 - <LeafletOptions leaflet={props.token} setState={setState} /> 70 + <LeafletOptions leaflet={props.token} isTemplate={isTemplate} /> 66 71 </div> 72 + <LeafletTemplateIndicator isTemplate={isTemplate} /> 67 73 </ThemeProvider> 68 74 </div> 69 75 ); ··· 167 173 </div> 168 174 ); 169 175 }; 176 + 177 + const LeafletTemplateIndicator = (props: { isTemplate: boolean }) => { 178 + if (!props.isTemplate) return; 179 + 180 + return ( 181 + <div className="absolute -top-3 right-1"> 182 + <TemplateSmall fill={theme.colors["bg-page"]} /> 183 + </div> 184 + ); 185 + };
+1 -1
app/home/page.tsx
··· 72 72 <ThemeProvider entityID={root_entity}> 73 73 <div className="flex h-full bg-bg-leaflet"> 74 74 <ThemeBackgroundProvider entityID={root_entity}> 75 - <div className="home relative max-w-screen-lg w-full h-full mx-auto flex sm:flex-row flex-col-reverse sm:gap-4 px-2 sm:px-6 "> 75 + <div className="home relative max-w-screen-lg w-full h-full mx-auto flex sm:flex-row flex-col-reverse px-2 sm:px-6 "> 76 76 <div className="homeOptions z-10 shrink-0 sm:static absolute bottom-0 place-self-end sm:place-self-start flex sm:flex-col flex-row-reverse gap-2 sm:w-fit w-full items-center pb-2 pt-1 sm:pt-7"> 77 77 <CreateNewLeafletButton /> 78 78 <HomeHelp />
+4 -1
app/layout.tsx
··· 4 4 import { Analytics } from "@vercel/analytics/react"; 5 5 import "./globals.css"; 6 6 import localFont from "next/font/local"; 7 + import { PopUpProvider } from "components/Toast"; 7 8 8 9 export const metadata = { 9 10 title: "Leaflet", ··· 52 53 <Analytics /> 53 54 <ServiceWorker /> 54 55 <InitialPageLoad> 55 - <ViewportSizeLayout>{children}</ViewportSizeLayout> 56 + <PopUpProvider> 57 + <ViewportSizeLayout>{children}</ViewportSizeLayout> 58 + </PopUpProvider> 56 59 </InitialPageLoad> 57 60 </body> 58 61 </html>
+2 -93
app/template/[template_id]/route.ts
··· 16 16 } from "drizzle/schema"; 17 17 import { sql } from "drizzle-orm"; 18 18 import { redirect } from "next/navigation"; 19 + import { createNewLeafletFromTemplate } from "actions/createNewLeafletFromTemplate"; 19 20 20 21 let supabase = createServerClient<Database>( 21 22 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, ··· 26 27 request: NextRequest, 27 28 { params }: { params: { template_id: string } }, 28 29 ) { 29 - let res = await supabase 30 - .from("permission_tokens") 31 - .select("*, permission_token_rights(*)") 32 - .eq("id", params.template_id) 33 - .single(); 34 - let rootEntity = res.data?.root_entity; 35 - if (!rootEntity || !res.data) return { title: "Leaflet not found" }; 36 - let { data } = await supabase.rpc("get_facts", { 37 - root: rootEntity, 38 - }); 39 - let initialFacts = (data as unknown as Fact<keyof typeof Attributes>[]) || []; 40 - 41 - let oldEntityIDToNewID = {} as { [k: string]: string }; 42 - let oldEntities = initialFacts.reduce((acc, f) => { 43 - if (!acc.includes(f.entity)) acc.push(f.entity); 44 - return acc; 45 - }, [] as string[]); 46 - let newEntities = [] as string[]; 47 - 48 - for (let oldEntity of oldEntities) { 49 - let newEntity = v7(); 50 - oldEntityIDToNewID[oldEntity] = newEntity; 51 - newEntities.push(newEntity); 52 - } 53 - 54 - let newFacts = [] as Array<Pick<Fact<any>, "entity" | "attribute" | "data">>; 55 - for (let fact of initialFacts) { 56 - let entity = oldEntityIDToNewID[fact.entity]; 57 - let data = fact.data; 58 - if ( 59 - data.type === "ordered-reference" || 60 - data.type == "spatial-reference" || 61 - data.type === "reference" 62 - ) { 63 - data.value = oldEntityIDToNewID[data.value]; 64 - } 65 - if (data.type === "image") { 66 - let url = data.src.split("?"); 67 - let paths = url[0].split("/"); 68 - let newID = v7(); 69 - await supabase.storage 70 - .from("minilink-user-assets") 71 - .copy(paths[paths.length - 1], newID); 72 - let newPath = [...paths]; 73 - newPath[newPath.length - 1] = newID; 74 - let newURL = newPath.join("/"); 75 - if (url[1]) newURL += `?${url[1]}`; 76 - data.src = newURL; 77 - } 78 - newFacts.push({ entity, attribute: fact.attribute, data }); 79 - } 80 - 81 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 82 - const db = drizzle(client); 83 - 84 - let { permissionToken } = await db.transaction(async (tx) => { 85 - // Create a new entity set 86 - let [entity_set] = await tx.insert(entity_sets).values({}).returning(); 87 - await tx 88 - .insert(entities) 89 - .values(newEntities.map((e) => ({ id: e, set: entity_set.id }))); 90 - await tx.insert(facts).values( 91 - newFacts.map((f) => ({ 92 - id: v7(), 93 - entity: f.entity, 94 - attribute: f.attribute, 95 - data: sql`${f.data}`, 96 - })), 97 - ); 98 - 99 - let [permissionToken] = await tx 100 - .insert(permission_tokens) 101 - .values({ root_entity: oldEntityIDToNewID[rootEntity] }) 102 - .returning(); 103 - 104 - //and give it all the permission on that entity set 105 - let [rights] = await tx 106 - .insert(permission_token_rights) 107 - .values({ 108 - token: permissionToken.id, 109 - entity_set: entity_set.id, 110 - read: true, 111 - write: true, 112 - create_token: true, 113 - change_entity_set: true, 114 - }) 115 - .returning(); 116 - 117 - return { permissionToken, rights, entity_set }; 118 - }); 119 - 120 - client.end(); 121 - return redirect(`/${permissionToken.id}`); 30 + await createNewLeafletFromTemplate(params.template_id, true); 122 31 }
+2
components/Buttons.tsx
··· 33 33 } 34 34 35 35 export const HoverButton = (props: { 36 + id?: string; 36 37 icon: React.ReactNode; 37 38 label: string; 38 39 background: string; ··· 43 44 return ( 44 45 <div className="sm:w-8 sm:h-8 relative "> 45 46 <div 47 + id={props.id} 46 48 className={` 47 49 z-10 group/hover-button 48 50 w-max h-max rounded-full p-1 flex gap-2
+62 -20
components/Icons.tsx
··· 184 184 ); 185 185 }; 186 186 187 + export const BlockMailboxSmall = (props: Props) => { 188 + return ( 189 + <svg 190 + width="24" 191 + height="24" 192 + viewBox="0 0 24 24" 193 + fill="none" 194 + xmlns="http://www.w3.org/2000/svg" 195 + {...props} 196 + > 197 + <path 198 + fillRule="evenodd" 199 + clipRule="evenodd" 200 + d="M18.1405 4.10733V14.9176C18.1405 15.2887 18.5305 15.5304 18.8628 15.3655L19.2479 15.1743C19.418 15.0899 19.5256 14.9163 19.5256 14.7264L19.5249 7.23625C19.5249 7.05424 19.6238 6.8866 19.7831 6.79857L22.2488 5.43608C22.4081 5.34806 22.507 5.18044 22.507 4.99845V2.50061C22.507 2.13292 22.1235 1.891 21.7916 2.04937L18.4251 3.65608C18.2512 3.73908 18.1405 3.91463 18.1405 4.10733ZM13.2665 5.20447L13.2658 5.20481L9.16584 7.18112C10.9757 7.42867 12.2115 8.34096 13.0005 9.50354C13.9051 10.8366 14.1943 12.4518 14.1943 13.6609V19.7119L20.874 16.5746V9.8999C20.874 9.43611 20.8228 8.90363 20.6975 8.35957C20.62 8.02319 20.8299 7.68772 21.1663 7.61026C21.5027 7.53281 21.8381 7.7427 21.9156 8.07908C22.0635 8.72117 22.124 9.34811 22.124 9.8999V16.654C22.124 17.0851 21.8776 17.4783 21.4897 17.6664L21.4828 17.6697L21.4828 17.6697L13.835 21.2617C13.7519 21.3007 13.6611 21.321 13.5693 21.321H11.8129C11.4677 21.321 11.1879 21.0412 11.1879 20.696C11.1879 20.3508 11.4677 20.071 11.8129 20.071H12.9443V18.4761L6.13761 21.6453C5.21494 22.0749 4.14744 21.5593 3.8857 20.6072L3.22811 20.87C2.16076 21.2966 1 20.5105 1 19.3611V13.6342C1 13.0109 1.35661 12.4424 1.91788 12.1712L9.49069 8.51114C9.09212 8.41656 8.64806 8.36444 8.15318 8.36444C7.22369 8.36444 6.45813 8.52476 5.82706 8.7905L5.40702 8.99297C4.84121 9.30264 4.40116 9.70358 4.05806 10.1421C3.84535 10.4139 3.45254 10.4619 3.18068 10.2492C2.90883 10.0365 2.86088 9.64367 3.07359 9.37181C3.52707 8.79224 4.10319 8.27899 4.82065 7.88892L4.82464 7.88675C4.97994 7.80248 5.14184 7.72398 5.31055 7.65182L12.7248 4.07797L12.7265 4.07714C13.6982 3.6124 14.7434 3.38306 16.0829 3.38306C16.2492 3.38306 16.4118 3.38805 16.5707 3.39786C16.9153 3.41914 17.1773 3.71568 17.156 4.0602C17.1347 4.40472 16.8382 4.66676 16.4937 4.64549C16.3611 4.6373 16.2242 4.63306 16.0829 4.63306C14.9016 4.63306 14.0456 4.83202 13.2665 5.20447ZM12.9443 13.9836V17.0972L5.61 20.5121C5.38343 20.6176 5.11901 20.4761 5.08106 20.229L4.27726 14.9961C4.25197 14.8314 4.33833 14.6698 4.48927 14.5993L12.3675 10.921C12.5404 11.297 12.6688 11.6895 12.7601 12.0787C12.747 12.094 12.7345 12.1101 12.7226 12.1269L10.2306 15.6673L5.96792 14.9839C5.62709 14.9293 5.30651 15.1613 5.25187 15.5021C5.19723 15.8429 5.42923 16.1635 5.77006 16.2182L10.4181 16.9633C10.6539 17.0011 10.8906 16.9012 11.0281 16.7059L12.9443 13.9836ZM10.9801 9.17961C11.2561 9.37907 11.4945 9.60675 11.7005 9.85293L3.96045 13.4667C3.30638 13.7721 2.93216 14.4723 3.04175 15.1858L3.68032 19.3431L2.76418 19.7093C2.51787 19.8077 2.25 19.6263 2.25 19.3611V13.6342C2.25 13.4904 2.3323 13.3592 2.46182 13.2966L10.9801 9.17961Z" 201 + fill="currentColor" 202 + /> 203 + </svg> 204 + ); 205 + }; 206 + 187 207 export const CanvasWidenSmall = (props: Props) => { 188 208 return ( 189 209 <svg ··· 350 370 ); 351 371 }; 352 372 353 - export const BlockMailboxSmall = (props: Props) => { 354 - return ( 355 - <svg 356 - width="24" 357 - height="24" 358 - viewBox="0 0 24 24" 359 - fill="none" 360 - xmlns="http://www.w3.org/2000/svg" 361 - {...props} 362 - > 363 - <path 364 - fillRule="evenodd" 365 - clipRule="evenodd" 366 - d="M18.1405 4.10733V14.9176C18.1405 15.2887 18.5305 15.5304 18.8628 15.3655L19.2479 15.1743C19.418 15.0899 19.5256 14.9163 19.5256 14.7264L19.5249 7.23625C19.5249 7.05424 19.6238 6.8866 19.7831 6.79857L22.2488 5.43608C22.4081 5.34806 22.507 5.18044 22.507 4.99845V2.50061C22.507 2.13292 22.1235 1.891 21.7916 2.04937L18.4251 3.65608C18.2512 3.73908 18.1405 3.91463 18.1405 4.10733ZM13.2665 5.20447L13.2658 5.20481L9.16584 7.18112C10.9757 7.42867 12.2115 8.34096 13.0005 9.50354C13.9051 10.8366 14.1943 12.4518 14.1943 13.6609V19.7119L20.874 16.5746V9.8999C20.874 9.43611 20.8228 8.90363 20.6975 8.35957C20.62 8.02319 20.8299 7.68772 21.1663 7.61026C21.5027 7.53281 21.8381 7.7427 21.9156 8.07908C22.0635 8.72117 22.124 9.34811 22.124 9.8999V16.654C22.124 17.0851 21.8776 17.4783 21.4897 17.6664L21.4828 17.6697L21.4828 17.6697L13.835 21.2617C13.7519 21.3007 13.6611 21.321 13.5693 21.321H11.8129C11.4677 21.321 11.1879 21.0412 11.1879 20.696C11.1879 20.3508 11.4677 20.071 11.8129 20.071H12.9443V18.4761L6.13761 21.6453C5.21494 22.0749 4.14744 21.5593 3.8857 20.6072L3.22811 20.87C2.16076 21.2966 1 20.5105 1 19.3611V13.6342C1 13.0109 1.35661 12.4424 1.91788 12.1712L9.49069 8.51114C9.09212 8.41656 8.64806 8.36444 8.15318 8.36444C7.22369 8.36444 6.45813 8.52476 5.82706 8.7905L5.40702 8.99297C4.84121 9.30264 4.40116 9.70358 4.05806 10.1421C3.84535 10.4139 3.45254 10.4619 3.18068 10.2492C2.90883 10.0365 2.86088 9.64367 3.07359 9.37181C3.52707 8.79224 4.10319 8.27899 4.82065 7.88892L4.82464 7.88675C4.97994 7.80248 5.14184 7.72398 5.31055 7.65182L12.7248 4.07797L12.7265 4.07714C13.6982 3.6124 14.7434 3.38306 16.0829 3.38306C16.2492 3.38306 16.4118 3.38805 16.5707 3.39786C16.9153 3.41914 17.1773 3.71568 17.156 4.0602C17.1347 4.40472 16.8382 4.66676 16.4937 4.64549C16.3611 4.6373 16.2242 4.63306 16.0829 4.63306C14.9016 4.63306 14.0456 4.83202 13.2665 5.20447ZM12.9443 13.9836V17.0972L5.61 20.5121C5.38343 20.6176 5.11901 20.4761 5.08106 20.229L4.27726 14.9961C4.25197 14.8314 4.33833 14.6698 4.48927 14.5993L12.3675 10.921C12.5404 11.297 12.6688 11.6895 12.7601 12.0787C12.747 12.094 12.7345 12.1101 12.7226 12.1269L10.2306 15.6673L5.96792 14.9839C5.62709 14.9293 5.30651 15.1613 5.25187 15.5021C5.19723 15.8429 5.42923 16.1635 5.77006 16.2182L10.4181 16.9633C10.6539 17.0011 10.8906 16.9012 11.0281 16.7059L12.9443 13.9836ZM10.9801 9.17961C11.2561 9.37907 11.4945 9.60675 11.7005 9.85293L3.96045 13.4667C3.30638 13.7721 2.93216 14.4723 3.04175 15.1858L3.68032 19.3431L2.76418 19.7093C2.51787 19.8077 2.25 19.6263 2.25 19.3611V13.6342C2.25 13.4904 2.3323 13.3592 2.46182 13.2966L10.9801 9.17961Z" 367 - fill="currentColor" 368 - /> 369 - </svg> 370 - ); 371 - }; 372 - 373 373 export const PaintSmall = (props: Props) => { 374 374 return ( 375 375 <svg ··· 404 404 fillRule="evenodd" 405 405 clipRule="evenodd" 406 406 d="M21.9086 4.05276C22.1404 4.2465 22.2413 4.54919 22.1704 4.83765L19.1103 17.2859C19.0122 17.685 18.6163 17.946 18.197 17.888L11.5121 16.963L8.54942 20.5609C8.35258 20.8 8.03148 20.9083 7.7239 20.8394C7.41632 20.7705 7.17693 20.5366 7.10845 20.2382L5.94505 15.1675L2.48769 12.377C2.26588 12.1979 2.15768 11.9191 2.20286 11.6429C2.24804 11.3666 2.43993 11.1338 2.70802 11.03L21.0686 3.91666C21.3529 3.80653 21.6768 3.85902 21.9086 4.05276ZM7.61043 15.1067L8.15704 17.4891L8.99127 15.6038C9.02763 15.5216 9.07806 15.446 9.14044 15.3801L11.7987 12.5713L7.61043 15.1067ZM11.399 15.3435L17.6887 16.2137L20.2095 5.95963L4.59334 12.0097L6.75339 13.7531L16.0425 8.12998C16.3902 7.9195 16.8449 7.99024 17.1071 8.29558C17.3693 8.60092 17.3582 9.04691 17.0811 9.33966L11.399 15.3435Z" 407 + fill="currentColor" 408 + /> 409 + </svg> 410 + ); 411 + }; 412 + 413 + export const TemplateSmall = (props: Props & { fill?: string }) => { 414 + return ( 415 + <svg 416 + width="24" 417 + height="24" 418 + viewBox="0 0 24 24" 419 + fill="none" 420 + xmlns="http://www.w3.org/2000/svg" 421 + {...props} 422 + > 423 + <path 424 + d="M14.1876 3.5073C14.3657 2.68428 14.8409 1.80449 15.1974 1.39941L15.2085 1.38682C15.5258 1.02605 16.1664 0.297788 17.7348 0.0551971C19.7272 -0.252968 22.338 1.22339 23.1781 3.53026C23.9464 5.63998 22.4863 7.65134 21.1778 8.49107C20.443 8.96256 19.8776 9.29865 19.5389 9.6655C19.6381 9.88024 19.8755 10.4623 19.9945 10.8588C20.1304 11.312 20.1356 11.8263 20.2444 12.3342C20.6412 13.1008 21.4615 14.6122 21.6483 14.9894C21.9441 15.5868 22.0637 16.0554 21.901 16.59C21.7793 16.99 21.3809 18.0037 21.2098 18.4064C21.1134 18.6333 20.6741 19.1794 20.165 19.3516C19.5207 19.5694 19.2 19.533 18.2867 19.1682C17.9231 19.3768 17.3068 19.3194 17.0874 19.2128C16.9902 19.5392 16.6234 19.8695 16.4353 20.0055C16.5008 20.1749 16.6684 20.619 16.5759 21.4191C16.4257 22.7176 14.6119 24.4819 12.2763 23.8544C10.5744 23.3971 10.2099 22.1002 10.0744 21.5462C8.16651 22.8209 5.74592 21.9772 4.43632 21.1133C3.44653 20.4603 3.16063 19.4467 3.2199 18.7888C2.57837 19.147 1.33433 19.2159 0.756062 17.9729C0.320217 17.036 0.838862 15.6535 2.49397 14.7706C3.56898 14.1971 5.01017 14.061 6.14456 14.136C5.47545 12.9417 4.17774 10.4051 3.97777 9.74456C3.72779 8.91889 3.94746 8.3129 4.30348 7.88113C4.6595 7.44936 5.21244 6.90396 5.75026 6.38129C6.28808 5.85862 7.06074 5.85862 7.7349 6.07072C8.27424 6.2404 9.36352 6.65146 9.84074 6.83578C10.5069 6.63086 11.9689 6.18102 12.4877 6.02101C13.0065 5.861 13.184 5.78543 13.7188 5.90996C13.8302 5.37643 14.0045 4.35336 14.1876 3.5073Z" 425 + fill={props.fill || "transparent"} 426 + /> 427 + <path 428 + fillRule="evenodd" 429 + clipRule="evenodd" 430 + d="M19.6271 2.81856C18.4896 2.07854 17.4326 2.39759 17.0578 2.82147C16.5869 3.3541 16.4234 4.10723 16.2512 5.11887L16.2231 5.28522L16.2231 5.28523C16.0919 6.06363 15.9405 6.96241 15.5423 7.80533L17.4557 8.48962C18.0778 7.71969 18.7304 7.28473 19.2974 6.92363L19.3687 6.87829C20.0258 6.46022 20.473 6.17579 20.7913 5.5972C21.0667 5.09643 21.0978 4.64884 20.9415 4.23092C20.7767 3.79045 20.3738 3.3044 19.6271 2.81856ZM15.0777 5.02873C14.9299 5.89897 14.7941 6.69891 14.4321 7.4083L13.4555 7.05901C13.2858 6.99834 13.1067 7.02061 12.9624 7.10485L9.80359 8.05107L7.26833 7.14438C6.97342 7.03892 6.64447 7.11834 6.43018 7.3457L5.27176 8.53601C5.26453 8.54323 5.25745 8.55058 5.2505 8.55806L5.2454 8.56361C5.04511 8.78343 4.9652 9.11292 5.1156 9.43079L5.85241 10.9196L5.8643 10.9002C5.84832 10.9944 5.86158 11.0964 5.91361 11.1952L7.63738 14.4667C7.6921 14.5705 7.78376 14.6501 7.89428 14.6896L17.4633 18.1118C17.8658 18.2557 18.2352 17.83 18.0359 17.4518L16.3121 14.1803C16.2574 14.0764 16.1657 13.9969 16.0552 13.9574L6.48624 10.5352C6.32266 10.4767 6.16454 10.5123 6.04784 10.6006L6.56002 9.76447L16.8798 13.4551L18.7426 16.9905L18.0747 17.8398L19.1912 18.2615C19.6606 18.4294 20.1033 18.1358 20.2179 17.728L20.7391 16.3648C20.8239 16.1511 20.8112 15.9108 20.7039 15.7071L19.124 12.7086L18.8949 11.321C18.8935 11.3129 18.892 11.3049 18.8904 11.2969C18.8874 11.234 18.8741 11.1705 18.8496 11.1087L18.1936 9.45372C18.7455 8.68856 19.3357 8.28878 19.927 7.9122C19.9681 7.88603 20.0096 7.85977 20.0514 7.83331C20.6663 7.44436 21.3511 7.01112 21.8182 6.16211C22.2345 5.40522 22.3314 4.60167 22.0392 3.82037C21.7555 3.06161 21.1334 2.40034 20.2662 1.83615C18.7307 0.837123 17.0056 1.11114 16.1798 2.04515C15.4528 2.86736 15.2586 3.96572 15.0958 4.92219L15.0777 5.02872L15.0777 5.02873ZM13.2681 8.23675L11.6653 8.71688L16.3567 10.3947L16.6254 9.4374L13.2681 8.23675ZM16.5481 11.7078L16.5582 11.7114L17.1419 11.9202L17.0098 12.2569L6.82932 8.61605L7.1065 8.33124L9.5842 9.21734L9.59688 9.22187L16.5481 11.7078ZM12.5683 20.4811C12.3863 20.7644 12.3505 20.965 12.3648 21.0689C12.4003 21.3259 12.5444 21.4722 12.7748 21.5613C13.0331 21.6611 13.3469 21.659 13.544 21.6032C14.1553 21.4302 14.2952 21.0637 14.2611 20.8923C14.2391 20.7814 14.1421 20.578 13.7906 20.338C13.6004 20.2082 13.3469 20.076 13.0173 19.9508C12.834 20.1242 12.681 20.3057 12.5683 20.4811ZM11.7567 19.6004C11.6942 19.6815 11.6359 19.764 11.5822 19.8476C11.3276 20.2439 11.1351 20.7322 11.2038 21.2293C11.3096 21.9955 11.8139 22.4463 12.3521 22.6544C12.8626 22.8518 13.4377 22.8513 13.863 22.731C14.7279 22.4863 15.6213 21.724 15.4107 20.664C15.3104 20.1591 14.9656 19.7211 14.4515 19.3701C14.3677 19.3128 14.2783 19.2571 14.1833 19.203C14.5987 19.0436 14.9889 19.0051 15.2827 19.1025C15.59 19.2042 15.9215 19.0377 16.0233 18.7304C16.125 18.4232 15.9585 18.0916 15.6513 17.9899C14.6724 17.6656 13.5751 18.0821 12.7766 18.6397C12.6141 18.5938 12.4436 18.5504 12.265 18.5097C11.5393 18.3444 10.9698 18.307 10.5034 18.3825C10.018 18.4612 9.67582 18.657 9.40873 18.8961C9.28258 19.009 9.17849 19.1268 9.09292 19.2277C9.06338 19.2625 9.03727 19.2937 9.01306 19.3227L9.01291 19.3228C8.96046 19.3856 8.91693 19.4377 8.86682 19.4922C8.73913 19.6313 8.63181 19.7134 8.47722 19.761C8.03942 19.896 7.30137 19.8237 6.60705 19.5851C6.27195 19.4699 5.98787 19.3293 5.79222 19.1916C5.64379 19.0871 5.59428 19.019 5.58047 19L5.58045 19C5.57827 18.997 5.57698 18.9952 5.57634 18.9947C5.57144 18.9579 5.57397 18.938 5.57539 18.9305C5.57674 18.9233 5.57829 18.9201 5.58128 18.9156C5.59031 18.9023 5.63142 18.8546 5.76375 18.7965C6.04383 18.6735 6.48291 18.6061 7.03421 18.5487C7.12534 18.5392 7.22003 18.5299 7.31675 18.5205L7.31734 18.5205L7.31774 18.5204C7.75337 18.478 8.22986 18.4315 8.60602 18.3399C8.83695 18.2837 9.10046 18.1956 9.31444 18.0333C9.55604 17.8501 9.73703 17.5659 9.72457 17.1949C9.71117 16.7955 9.50249 16.4807 9.2559 16.2553C9.01235 16.0327 8.69774 15.863 8.36729 15.7333C7.70363 15.4729 6.85166 15.3254 6.00343 15.2972C5.15473 15.2689 4.25916 15.3581 3.51015 15.6085C2.78202 15.852 2.05623 16.2973 1.79807 17.0767C1.6963 17.3839 1.86287 17.7155 2.1701 17.8173C2.47733 17.919 2.80889 17.7525 2.91065 17.4452C2.99481 17.1912 3.28176 16.9207 3.8818 16.7201C4.46096 16.5264 5.209 16.4433 5.96437 16.4685C6.7202 16.4937 7.43275 16.6256 7.93908 16.8243C8.19363 16.9243 8.36538 17.0292 8.46519 17.1204C8.4773 17.1315 8.4878 17.1419 8.49689 17.1515C8.45501 17.1668 8.39992 17.1838 8.3287 17.2012C8.04154 17.2711 7.67478 17.3072 7.24492 17.3496L7.24413 17.3497L7.24246 17.3498C7.13635 17.3603 7.02639 17.3711 6.91284 17.3829C6.38763 17.4376 5.76632 17.5153 5.29238 17.7234C5.0477 17.8309 4.78839 17.9954 4.60986 18.2599C4.42009 18.541 4.36482 18.8707 4.42432 19.213C4.49899 19.6426 4.83826 19.9534 5.11763 20.15C5.42736 20.368 5.81812 20.5533 6.22607 20.6935C7.01783 20.9656 8.03865 21.1226 8.82239 20.8811C9.248 20.7499 9.52379 20.5096 9.73004 20.285C9.79974 20.2091 9.87042 20.1246 9.92975 20.0536L9.92977 20.0536L9.92995 20.0534C9.9503 20.0291 9.96932 20.0063 9.98649 19.9861C10.0618 19.8973 10.1248 19.8281 10.1905 19.7694C10.3069 19.6651 10.4472 19.579 10.6908 19.5395C10.9181 19.5027 11.2529 19.5041 11.7567 19.6004Z" 431 + fill="currentColor" 432 + /> 433 + </svg> 434 + ); 435 + }; 436 + export const TemplateRemoveSmall = (props: Props) => { 437 + return ( 438 + <svg 439 + width="24" 440 + height="24" 441 + viewBox="0 0 24 24" 442 + fill="none" 443 + xmlns="http://www.w3.org/2000/svg" 444 + > 445 + <path 446 + fillRule="evenodd" 447 + clipRule="evenodd" 448 + d="M21.6598 1.22969C22.0503 0.839167 22.6835 0.839167 23.074 1.22969C23.4646 1.62021 23.4646 2.25338 23.074 2.6439L21.9991 3.71887C22 3.72121 22.001 3.72355 22.002 3.7259L21.0348 4.69374C21.0347 4.69033 21.0345 4.68693 21.0344 4.68353L17.2882 8.42972L17.2977 8.43313L16.3813 9.35011L16.3714 9.34656L15.5955 10.1224L15.6058 10.1261L14.6894 11.0431L14.6787 11.0393L14.3959 11.3221L14.4067 11.326L13.4903 12.2429L13.479 12.2389L12.8919 12.8261L12.9034 12.8302L10.2156 15.5198L10.2028 15.5152L9.35969 16.3583C9.36255 16.3614 9.36541 16.3645 9.36826 16.3676L7.20585 18.5314C7.19871 18.5321 7.19159 18.5328 7.18448 18.5335L6.26611 19.4519C6.27069 19.4539 6.27528 19.4559 6.27989 19.4579L5.40679 20.3316C5.40244 20.3291 5.39809 20.3267 5.39376 20.3242L2.54817 23.1698C2.15765 23.5603 1.52448 23.5603 1.13396 23.1698C0.743434 22.7793 0.743433 22.1461 1.13396 21.7556L4.57518 18.3144C4.5862 18.296 4.59778 18.2779 4.6099 18.2599C4.72342 18.0917 4.86961 17.964 5.02393 17.8656L6.39488 16.4947C6.25376 16.4822 6.10989 16.4734 5.96441 16.4685C5.20904 16.4433 4.461 16.5264 3.88183 16.7201C3.2818 16.9207 2.99485 17.1912 2.91069 17.4452C2.80892 17.7525 2.47737 17.919 2.17013 17.8173C1.8629 17.7155 1.69634 17.3839 1.79811 17.0767C2.05627 16.2973 2.78206 15.852 3.51019 15.6085C4.2592 15.3581 5.15477 15.2689 6.00346 15.2972C6.48903 15.3133 6.97583 15.3686 7.42782 15.4617L8.11942 14.7701L7.89431 14.6896C7.7838 14.6501 7.69213 14.5705 7.63742 14.4667L5.91365 11.1952C5.86162 11.0964 5.84836 10.9944 5.86434 10.9002L5.85245 10.9196L5.11563 9.4308C4.96523 9.11293 5.04515 8.78343 5.24544 8.56361L5.25054 8.55806C5.25749 8.55058 5.26457 8.54323 5.2718 8.53601L6.43022 7.3457C6.6445 7.11834 6.97346 7.03892 7.26837 7.14439L9.80363 8.05107L12.9624 7.10485C13.1067 7.02062 13.2859 6.99834 13.4555 7.05901L14.4322 7.40831C14.7942 6.69891 14.93 5.89897 15.0777 5.02873L15.0777 5.02872L15.0958 4.9222C15.2586 3.96572 15.4529 2.86736 16.1798 2.04515C17.0056 1.11114 18.7307 0.837125 20.2663 1.83615C20.4285 1.94168 20.5821 2.05061 20.7266 2.16294L21.6598 1.22969ZM19.8899 2.99965C19.8075 2.93935 19.72 2.87895 19.6271 2.81856C18.4897 2.07854 17.4326 2.39759 17.0579 2.82147C16.5869 3.3541 16.4234 4.10723 16.2512 5.11887L16.2231 5.28522L16.2231 5.28523C16.1304 5.83581 16.0274 6.44661 15.8342 7.05527L19.8899 2.99965ZM14.288 8.60148L13.2682 8.23675L11.6654 8.71688L13.5122 9.37736L14.288 8.60148ZM12.5953 10.2942L9.59692 9.22187L9.58424 9.21734L7.10654 8.33124L6.82935 8.61605L12.3125 10.577L12.5953 10.2942ZM11.3957 11.4938L6.56005 9.76447L6.04788 10.6006C6.16458 10.5123 6.32269 10.4767 6.48628 10.5352L10.8085 12.081L11.3957 11.4938ZM17.0099 12.2569L16.2294 11.9778L15.313 12.8948L16.8798 13.4551L18.7426 16.9905L18.0747 17.8398L19.1912 18.2615C19.6607 18.4294 20.1033 18.1358 20.2179 17.728L20.7391 16.3648C20.824 16.1511 20.8112 15.9108 20.7039 15.7071L19.124 12.7086L18.8949 11.321L18.8931 11.3104L18.8904 11.2969C18.8874 11.234 18.8742 11.1705 18.8497 11.1087L18.3522 9.8537L16.5121 11.6949L16.5482 11.7078L16.5582 11.7115L17.1419 11.9202L17.0099 12.2569ZM12.0382 16.1716L14.7261 13.482L16.0553 13.9574C16.1658 13.9969 16.2575 14.0764 16.3122 14.1803L18.0359 17.4518C18.2352 17.83 17.8658 18.2557 17.4633 18.1118L12.0382 16.1716ZM8.44038 19.7717L7.26492 20.9479C7.80247 21.0274 8.35468 21.0252 8.82243 20.8811C9.24804 20.7499 9.52382 20.5096 9.73008 20.285C9.79978 20.2091 9.87046 20.1246 9.92979 20.0536L9.92981 20.0536L9.92999 20.0534L9.9306 20.0527C9.95072 20.0286 9.96953 20.0061 9.98653 19.9861C10.0618 19.8973 10.1248 19.8281 10.1905 19.7694C10.307 19.6651 10.4472 19.579 10.6908 19.5395C10.9182 19.5027 11.2529 19.5041 11.7567 19.6004C11.6943 19.6815 11.6359 19.764 11.5823 19.8476C11.3276 20.2439 11.1352 20.7322 11.2038 21.2293C11.3097 21.9955 11.8139 22.4463 12.3522 22.6544C12.8626 22.8518 13.4377 22.8513 13.8631 22.731C14.7279 22.4863 15.6213 21.724 15.4107 20.664C15.3105 20.1591 14.9656 19.7211 14.4516 19.3701C14.3677 19.3128 14.2783 19.2571 14.1833 19.203C14.5987 19.0436 14.9889 19.0051 15.2828 19.1025C15.59 19.2042 15.9215 19.0377 16.0233 18.7304C16.1251 18.4232 15.9585 18.0916 15.6513 17.9899C14.6724 17.6656 13.5751 18.0821 12.7766 18.6397C12.6141 18.5938 12.4436 18.5504 12.265 18.5097C11.5394 18.3444 10.9698 18.307 10.5035 18.3825C10.018 18.4612 9.67586 18.657 9.40877 18.8961C9.28262 19.009 9.17853 19.1268 9.09296 19.2277C9.06342 19.2625 9.03731 19.2937 9.0131 19.3227L9.01295 19.3228C8.9605 19.3856 8.91697 19.4377 8.86686 19.4922C8.73917 19.6313 8.63185 19.7134 8.47726 19.761C8.46519 19.7648 8.45289 19.7683 8.44038 19.7717ZM12.5683 20.4811C12.3863 20.7644 12.3505 20.965 12.3648 21.0689C12.4003 21.3259 12.5445 21.4722 12.7749 21.5613C13.0331 21.6611 13.3469 21.659 13.544 21.6032C14.1554 21.4302 14.2952 21.0637 14.2612 20.8923C14.2391 20.7814 14.1422 20.578 13.7907 20.338C13.6005 20.2082 13.347 20.076 13.0173 19.9508C12.8341 20.1242 12.681 20.3057 12.5683 20.4811Z" 407 449 fill="currentColor" 408 450 /> 409 451 </svg>
+2 -1
components/Layout.tsx
··· 13 13 }; 14 14 15 15 export const Menu = (props: { 16 + open?: boolean; 16 17 trigger: React.ReactNode; 17 18 children: React.ReactNode; 18 19 align?: "start" | "end" | "center"; ··· 22 23 onOpenChange?: (o: boolean) => void; 23 24 }) => { 24 25 return ( 25 - <DropdownMenu.Root onOpenChange={props.onOpenChange}> 26 + <DropdownMenu.Root onOpenChange={props.onOpenChange} open={props.open}> 26 27 <DropdownMenu.Trigger>{props.trigger}</DropdownMenu.Trigger> 27 28 <DropdownMenu.Portal> 28 29 <NestedCardThemeProvider>
+21 -8
components/Toast.tsx
··· 17 17 18 18 type Smoke = { 19 19 position: { x: number; y: number }; 20 - text: string; 20 + text: React.ReactNode; 21 + static?: boolean; 21 22 error?: boolean; 22 23 }; 23 24 ··· 45 46 export const PopUpProvider: React.FC<React.PropsWithChildren<unknown>> = ( 46 47 props, 47 48 ) => { 48 - let [state, setState] = useState<Smokes>([]); 49 + let [smokes, setState] = useState<Smokes>([]); 49 50 let [toastState, setToastState] = useState<Toast | null>(null); 50 51 let toastTimeout = useRef<number | null>(null); 51 52 let toaster = useCallback( ··· 64 65 }, 65 66 [setToastState], 66 67 ); 68 + console.log(smokes); 67 69 return ( 68 70 <PopUpContext.Provider 69 71 value={{ setSmokeState: setState, setToastState: toaster }} 70 72 > 71 73 {props.children} 72 - {state.map((toast) => ( 73 - <Smoke {...toast.position} error={toast.error} key={toast.key}> 74 - {toast.text} 74 + {smokes.map((smoke) => ( 75 + <Smoke 76 + {...smoke.position} 77 + error={smoke.error} 78 + key={smoke.key} 79 + static={smoke.static} 80 + > 81 + {smoke.text} 75 82 </Smoke> 76 83 ))} 77 84 <Toast toast={toastState} setToast={setToastState} /> ··· 125 132 }; 126 133 127 134 const Smoke: React.FC< 128 - React.PropsWithChildren<{ x: number; y: number; error?: boolean }> 135 + React.PropsWithChildren<{ 136 + x: number; 137 + y: number; 138 + error?: boolean; 139 + static?: boolean; 140 + }> 129 141 > = (props) => { 130 142 return ( 131 143 <div ··· 138 150 <style jsx>{` 139 151 .smoke { 140 152 left: ${props.x}px; 153 + top: ${props.y}px; 141 154 animation-name: fadeout; 142 155 animation-duration: 2s; 143 156 } 144 157 145 158 @keyframes fadeout { 146 159 from { 147 - top: ${props.y - 20}px; 160 + ${props.static ? "" : `top: ${props.y - 20}px;`} 148 161 opacity: 100%; 149 162 } 150 163 151 164 to { 152 - top: ${props.y - 60}px; 165 + ${props.static ? "" : `top: ${props.y - 60}px;`} 153 166 opacity: 0%; 154 167 } 155 168 }