| <script lang="ts" context="module"> | |
| import type { | |
| Subscriber, | |
| Invalidator, | |
| Unsubscriber, | |
| Writable | |
| } from "svelte/store"; | |
| import type { tool } from "../tools/"; | |
| export const TOOL_KEY = Symbol("tool"); | |
| type upload_tool = "bg_webcam" | "bg_upload" | "bg_clipboard" | "bg_color"; | |
| type transform_tool = "crop" | "rotate"; | |
| type brush_tool = "brush_color" | "brush_size"; | |
| type eraser_tool = "eraser_size"; | |
| export interface ToolOptions { | |
| order: number; | |
| label: string; | |
| icon: typeof Image; | |
| cb: (...args: any[]) => void; | |
| id: upload_tool | transform_tool | brush_tool | eraser_tool; | |
| } | |
| export interface ToolMeta { | |
| default: upload_tool | transform_tool | brush_tool | eraser_tool | null; | |
| options: ToolOptions[]; | |
| } | |
| export interface ToolContext { | |
| register_tool: (type: tool, opts?: { cb: () => void }) => () => void; | |
| active_tool: { | |
| set: (tool: tool) => void; | |
| subscribe( | |
| this: void, | |
| run: Subscriber<tool | null>, | |
| invalidate?: Invalidator<tool | null> | |
| ): Unsubscriber; | |
| }; | |
| current_color: Writable<string>; | |
| } | |
| </script> | |
| <script lang="ts"> | |
| import { Toolbar } from "@gradio/atoms"; | |
| import { default as IconButton } from "./IconButton.svelte"; | |
| import { getContext, setContext } from "svelte"; | |
| import { writable } from "svelte/store"; | |
| import { EDITOR_KEY, type EditorContext } from "../ImageEditor.svelte"; | |
| import { Image, Crop, Brush, Erase } from "@gradio/icons"; | |
| import { type I18nFormatter } from "@gradio/utils"; | |
| const { active_tool, toolbar_box, editor_box } = | |
| getContext<EditorContext>(EDITOR_KEY); | |
| export let i18n: I18nFormatter; | |
| let tools: tool[] = []; | |
| const cbs: Record<string, () => void> = {}; | |
| let current_color = writable("#000000"); | |
| const tool_context: ToolContext = { | |
| current_color, | |
| register_tool: (type: tool, opts?: { cb: () => void }) => { | |
| tools = [...tools, type]; | |
| if (opts?.cb) { | |
| cbs[type] = opts.cb; | |
| } | |
| return () => { | |
| tools = tools.filter((tool) => tool !== type); | |
| }; | |
| }, | |
| active_tool: { | |
| subscribe: active_tool.subscribe, | |
| set: active_tool.set | |
| } | |
| }; | |
| setContext<ToolContext>(TOOL_KEY, tool_context); | |
| const tools_meta: Record< | |
| tool, | |
| { | |
| order: number; | |
| label: string; | |
| icon: typeof Image; | |
| } | |
| > = { | |
| crop: { | |
| order: 1, | |
| label: i18n("Transform"), | |
| icon: Crop | |
| }, | |
| draw: { | |
| order: 2, | |
| label: i18n("Draw"), | |
| icon: Brush | |
| }, | |
| erase: { | |
| order: 2, | |
| label: i18n("Erase"), | |
| icon: Erase | |
| }, | |
| bg: { | |
| order: 0, | |
| label: i18n("Background"), | |
| icon: Image | |
| } | |
| } as const; | |
| let toolbar_width: number; | |
| let toolbar_wrap: HTMLDivElement; | |
| $: toolbar_width, $editor_box, get_dimensions(); | |
| function get_dimensions(): void { | |
| if (!toolbar_wrap) return; | |
| $toolbar_box = toolbar_wrap.getBoundingClientRect(); | |
| } | |
| function handle_click(e: Event, tool: tool): void { | |
| e.stopPropagation(); | |
| $active_tool = tool; | |
| cbs[tool] && cbs[tool](); | |
| } | |
| </script> | |
| <slot /> | |
| <div | |
| class="toolbar-wrap" | |
| bind:clientWidth={toolbar_width} | |
| bind:this={toolbar_wrap} | |
| > | |
| <Toolbar show_border={false}> | |
| {#each tools as tool (tool)} | |
| <IconButton | |
| highlight={$active_tool === tool} | |
| on:click={(e) => handle_click(e, tool)} | |
| Icon={tools_meta[tool].icon} | |
| size="medium" | |
| padded={false} | |
| label={tools_meta[tool].label + " button"} | |
| transparent={true} | |
| offset={tool === "draw" ? -2 : tool === "erase" ? -6 : 0} | |
| /> | |
| {/each} | |
| </Toolbar> | |
| </div> | |
| <style> | |
| .toolbar-wrap { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| margin-left: var(--spacing-xl); | |
| height: 100%; | |
| } | |
| </style> | |