diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index c300c63f2..a6c2c09bb 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -3,7 +3,6 @@ import type { Preview } from "@storybook/react"; import React from "react"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; - import "./global.css"; const preview: Preview = { diff --git a/electron.vite.config.ts b/electron.vite.config.ts index c53d4c87a..2a7ab6fe0 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -61,15 +61,15 @@ export default defineConfig({ plugins: [ ViteImageOptimizer(), tsconfigPaths(), + svgr({ + svgrOptions: { exportType: "default", ref: true, svgo: false, titleProp: true }, + include: "**/*.svg", + }), react({}), flow(), viteStaticCopy({ targets: [{ src: "node_modules/monaco-editor/min/vs/*", dest: "monaco" }], }), - svgr({ - svgrOptions: { exportType: "default", ref: true, svgo: false, titleProp: true }, - include: "**/*.svg", - }), ], }, }); diff --git a/frontend/app/asset/magnify.svg b/frontend/app/asset/magnify.svg new file mode 100644 index 000000000..09a4919ca --- /dev/null +++ b/frontend/app/asset/magnify.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/frontend/app/block/block.less b/frontend/app/block/block.less index a0960175e..956ecea0a 100644 --- a/frontend/app/block/block.less +++ b/frontend/app/block/block.less @@ -70,6 +70,8 @@ flex-direction: column; .block-frame-default-header { + max-height: var(--header-height); + min-height: var(--header-height); display: flex; padding: 4px 5px 4px 10px; align-items: center; @@ -90,8 +92,10 @@ .block-frame-default-header-iconview { display: flex; + flex-shrink: 1; align-items: center; gap: 8px; + overflow-x: hidden; .block-frame-view-icon { font-size: var(--header-icon-size); @@ -103,10 +107,9 @@ } .block-frame-view-type { - line-height: 12px; font-weight: 700; - text-wrap: nowrap; overflow-x: hidden; + text-wrap: nowrap; text-overflow: ellipsis; } @@ -119,8 +122,8 @@ font: var(--fixed-font); font-size: 11px; opacity: 0.7; - text-wrap: nowrap; overflow-x: hidden; + text-wrap: nowrap; text-overflow: ellipsis; } @@ -148,6 +151,7 @@ min-width: 0; gap: 8px; align-items: center; + overflow-x: hidden; .block-frame-div { display: flex; @@ -200,6 +204,7 @@ .block-frame-end-icons { display: flex; + flex-shrink: 0; .iconbutton { display: flex; @@ -207,6 +212,19 @@ padding: 4px 6px; align-items: center; } + + .block-frame-magnify { + justify-content: center; + align-items: center; + padding: 0; + + svg { + #arrow1, + #arrow2 { + fill: var(--main-text-color); + } + } + } } } @@ -220,7 +238,7 @@ align-items: center; justify-content: center; - i { + .iconbutton { opacity: 0.7; font-size: 45px; margin: -30px 0 0 0; diff --git a/frontend/app/block/blockframe.tsx b/frontend/app/block/blockframe.tsx index 2e7406f36..60a80daca 100644 --- a/frontend/app/block/blockframe.tsx +++ b/frontend/app/block/blockframe.tsx @@ -6,6 +6,7 @@ import { Button } from "@/app/element/button"; import { ContextMenuModel } from "@/app/store/contextmenu"; import { atoms, globalStore, useBlockAtom, WOS } from "@/app/store/global"; import * as services from "@/app/store/services"; +import { MagnifyIcon } from "@/element/magnify"; import { LayoutTreeState } from "@/layout/index"; import { getLayoutStateAtomForTab } from "@/layout/lib/layoutAtom"; import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil"; @@ -77,13 +78,11 @@ const OptMagnifyButton = React.memo( const tabId = globalStore.get(atoms.activeTabId); const tabAtom = WOS.getWaveObjectAtom(WOS.makeORef("tab", tabId)); const layoutTreeState = util.useAtomValueSafe(getLayoutStateAtomForTab(tabId, tabAtom)); - if (!isBlockMagnified(layoutTreeState, blockData.oid)) { - return null; - } + const isMagnified = isBlockMagnified(layoutTreeState, blockData.oid); const magnifyDecl: HeaderIconButton = { elemtype: "iconbutton", - icon: "regular@magnifying-glass-minus", - title: "Minimize", + icon: , + title: isMagnified ? "Minimize" : "Magnify", click: layoutModel?.onMagnifyToggle, }; return ; diff --git a/frontend/app/block/blockutil.tsx b/frontend/app/block/blockutil.tsx index 613284534..b991c6317 100644 --- a/frontend/app/block/blockutil.tsx +++ b/frontend/app/block/blockutil.tsx @@ -163,7 +163,7 @@ export const IconButton = React.memo(({ decl, className }: { decl: HeaderIconBut useLongClick(buttonRef, decl.click, decl.longClick); return (
- + {typeof decl.icon === "string" ? : decl.icon}
); }); diff --git a/frontend/app/element/magnify.less b/frontend/app/element/magnify.less new file mode 100644 index 000000000..48a402f3b --- /dev/null +++ b/frontend/app/element/magnify.less @@ -0,0 +1,36 @@ +.magnify-icon { + display: inline-block; + width: 15px; + height: 15px; + svg { + #arrow1 { + transform: rotate(180deg); + transform-origin: calc(29.167% + 4px) calc(70.833% + 4px); // account for path offset in the svg itself + } + #arrow2 { + transform: rotate(-180deg); + transform-origin: calc(70.833% + 4px) calc(29.167% + 4px); + } + #arrow1, + #arrow2 { + transition: transform 300ms ease-in; + } + } + &.enabled { + svg { + #arrow1, + #arrow2 { + transform: rotate(0deg); + } + } + } +} + +@media (prefers-reduced-motion) { + .magnify-icon svg { + #arrow1, + #arrow2 { + transition: none; + } + } +} diff --git a/frontend/app/element/magnify.stories.tsx b/frontend/app/element/magnify.stories.tsx new file mode 100644 index 000000000..5e52da4e6 --- /dev/null +++ b/frontend/app/element/magnify.stories.tsx @@ -0,0 +1,25 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { MagnifyIcon } from "./magnify"; + +const meta = { + title: "Icons/Magnify", + component: MagnifyIcon, + args: { + enabled: true, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Enabled: Story = { + args: { + enabled: true, + }, +}; + +export const Disabled: Story = { + args: { + enabled: false, + }, +}; diff --git a/frontend/app/element/magnify.tsx b/frontend/app/element/magnify.tsx new file mode 100644 index 000000000..f6ceadeb0 --- /dev/null +++ b/frontend/app/element/magnify.tsx @@ -0,0 +1,15 @@ +import clsx from "clsx"; +import MagnifySVG from "../asset/magnify.svg"; +import "./magnify.less"; + +interface MagnifyIconProps { + enabled: boolean; +} + +export function MagnifyIcon({ enabled }: MagnifyIconProps) { + return ( +
+ +
+ ); +} diff --git a/frontend/app/theme.less b/frontend/app/theme.less index 3ae59f0fe..7d2633ae8 100644 --- a/frontend/app/theme.less +++ b/frontend/app/theme.less @@ -32,6 +32,7 @@ --header-font: 700 11px / normal "Inter", sans-serif; --header-icon-size: 14px; --header-icon-width: 16px; + --header-height: 30px; --tab-green: rgb(88, 193, 66); diff --git a/frontend/layout/lib/tilelayout.less b/frontend/layout/lib/tilelayout.less index 8ef09840e..08a29903c 100644 --- a/frontend/layout/lib/tilelayout.less +++ b/frontend/layout/lib/tilelayout.less @@ -117,3 +117,16 @@ border-radius: calc(var(--block-border-radius) + 2px); } } + +@media (prefers-reduced-motion) { + .tile-layout { + &.animate { + .tile-node, + .placeholder { + transition-duration: none; + transition-timing-function: none; + transition-property: none; + } + } + } +} diff --git a/frontend/types/custom.d.ts b/frontend/types/custom.d.ts index ba8d797eb..508df4989 100644 --- a/frontend/types/custom.d.ts +++ b/frontend/types/custom.d.ts @@ -123,7 +123,7 @@ declare global { type HeaderIconButton = { elemtype: "iconbutton"; - icon: string; + icon: string | React.ReactNode; className?: string; title?: string; click?: (e: React.MouseEvent) => void;