Skip to content

Conversation

@lumixraku
Copy link
Contributor

@lumixraku lumixraku commented May 19, 2025

Description

https://app-flowy-7s1lxq95t-lumixrakus-projects.vercel.app


Checklist

General

  • I've included relevant documentation or comments for the changes introduced.
  • I've tested the changes in multiple environments (e.g., different browsers, operating systems).

Testing

  • I've added or updated tests to validate the changes introduced for AppFlowy Web.

Feature-Specific

  • For feature additions, I've added a preview (video, screenshot, or demo) in the "Feature Preview" section.
  • I've verified that this feature integrates seamlessly with existing functionality.

Summary by Sourcery

Revamp the editor’s selection toolbar with new transformation menus, enhanced styling, and keyboard navigation, while bumping dependencies and refining build/CI configurations.

New Features:

  • Add a “Turn Into” menu for block type conversions
  • Introduce a “More Options” menu for additional inline actions
  • Implement keyboard arrow navigation and enter key selection in toolbar popovers

Enhancements:

  • Separate text highlight action into its own ColorHighlight picker and remove background section from Color
  • Unify icon sizes, spacing, and background colors across toolbar actions and popovers
  • Refactor ActionButton styling and update AppTheme tooltip colors

Build:

  • Enable host binding to 0.0.0.0 in Vite config

CI:

  • Comment out start step in web_coverage CI workflow

Chores:

  • Update pnpm-lock.yaml with dependency bumps (debug, call-bind, semver, and other transitive deps)
  • Relax certain ESLint rules in .eslintrc.cjs

@sourcery-ai
Copy link

sourcery-ai bot commented May 19, 2025

Reviewer's Guide

This PR overhauls the editor selection toolbar UI and interactions: it upgrades dependencies, refactors core button and popover components to use Material-UI styling, introduces keyboard navigation for dropdown menus, and adds new toolbar actions (TurnInto, MoreOptions, ColorHighlight).

File-Level Changes

Change Details Files
Lockfile and build config updates
  • Updated various dependencies (debug→4.4.1, semver→7.7.2, call-bind, array-buffer-byte-length, etc.)
  • Enabled Vite dev server host binding (0.0.0.0)
  • Tweaked GitHub workflow and ESLint rules
pnpm-lock.yaml
vite.config.ts
.github/workflows/web_coverage.yaml
.eslintrc.cjs
General button component refactoring
  • Migrated ActionButton to MUI IconButton with consistent padding and active styling
  • Centralized tooltip and icon container layout
  • Replaced inline styles with utility classes and cn helper
src/components/editor/components/toolbar/selection-toolbar/actions/ActionButton.tsx
Shared Popover improvements
  • Removed unused React import
  • Adjusted default origin formatting
  • Updated background and padding to surface-primary
src/components/_shared/popover/Popover.tsx
Keyboard navigation hook
  • Added useKeyboardNavigation to handle arrow/enter keys
  • Exposed getButtonProps and selectedIndex API
src/components/editor/components/toolbar/selection-toolbar/hooks/useKeyboardNavigation.ts
Heading action redesign
  • Converted to MUI Button list with icons, labels, and tick indicators
  • Introduced headingOptions array and keyboard navigation
  • Updated popover styling (rounded corners, margin)
src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx
Align action redesign
  • Replaced manual SVG buttons with MUI Button list via alignOptions
  • Enabled keyboard navigation and active highlighting
  • Unified popover styling
src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx
New TurnInto action
  • Created TurnInto dropdown with suggestions and turn-into sections
  • Defined blockOptions for different block types
  • Integrated keyboard navigation and current-type detection
src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx
New MoreOptions action
  • Added MoreOptions dropdown for strikethrough and inline formula
  • Reused useKeyboardNavigation and MUI Button list
src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx
Color and ColorHighlight split
  • Separated text color and highlight color into two actions
  • Updated Color to only render font colors
  • Introduced ColorHighlight for background colors
src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx
src/components/editor/components/toolbar/selection-toolbar/actions/ColorHighlight.tsx
Href popover and Href action tweaks
  • Converted state hooks to useState
  • Adjusted input styling and tooltip placement
  • Ensured popover background matches theme
src/components/editor/components/leaf/href/HrefPopover.tsx
src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions
Copy link

github-actions bot commented May 19, 2025

🥷 Ninja i18n – 🛎️ Translations need to be updated

Project /project.inlang

lint rule new reports level link
Missing translation 237 warning contribute (via Fink 🐦)
Identical pattern 3 warning contribute (via Fink 🐦)
Message without source 1 warning contribute (via Fink 🐦)

@codecov-commenter
Copy link

Codecov Report

All modified and coverable lines are covered by tests ✅

Please upload report for BASE (main@f620c34). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #120   +/-   ##
=======================================
  Coverage        ?   58.35%           
=======================================
  Files           ?       61           
  Lines           ?     8078           
  Branches        ?      453           
=======================================
  Hits            ?     4714           
  Misses          ?     3361           
  Partials        ?        3           
Flag Coverage Δ
appflowy_web 58.35% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @lumixraku - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 4 issues found
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 2 issues found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

},
];

function TurnInfo() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting block option data, block detection logic, and menu rendering into separate modules and components to simplify TurnInfo.tsx.

Here are a few small, focused refactors that preserve every bit of functionality while cutting this file down to its essentials.

  1. Extract your block‐option data into blockOptions.ts:
// src/components/SelectionToolbar/blockOptions.ts
import { FC, SVGProps } from 'react'
import {
  ParagraphSvg, Heading1, Heading2, Heading3,
  BulletedListSvg, NumberedListSvg,
  ToggleListIcon, ToggleHeading1Icon, ToggleHeading2Icon, ToggleHeading3Icon,
  QuoteSvg,
} from '@/assets/icons'
import { BlockType } from '@/application/types'

export type BlockOption = {
  type: string
  icon: FC<SVGProps<SVGSVGElement>>
  label: string
  blockType: BlockType
  data?: any
  group: 'text' | 'list' | 'toggle' | 'other'
}

export const blockOptions: BlockOption[] = [
  { type: 'paragraph', icon: ParagraphSvg, label: 'editor.text', blockType: BlockType.Paragraph, group: 'text' },
  { type: 'heading1', icon: Heading1, label: 'toolbar.h1', blockType: BlockType.HeadingBlock, data: { level: 1 }, group: 'text' },
  // …the rest here
]
  1. Pull out the “current block” detection & option‐filtering into a hook:
// src/components/SelectionToolbar/useTurnIntoOptions.ts
import { useSlateStatic } from 'slate-react'
import { getBlockEntry } from '@/application/slate-yjs/utils/editor'
import { blockOptions, BlockOption } from './blockOptions'

export function useTurnIntoOptions() {
  const editor = useSlateStatic()
  let currentGroup: BlockOption['group'] | null = null
  let currentType = ''
  let currentLevel: number | null = null

  try {
    const [node] = getBlockEntry(editor)
    switch (node.type) {
      case BlockType.Paragraph: currentType='paragraph'; currentGroup='text'; break
      case BlockType.HeadingBlock:
        currentType='heading'
        currentLevel=(node.data as any).level
        currentGroup='text'
        break
      // …the rest
    }
  } catch {}

  const suggestions = blockOptions.filter(o =>
    o.group === currentGroup && o.type !== `${currentType}${currentLevel || ''}`
  )
  const turnInto = blockOptions.filter(o => !suggestions.includes(o))

  return { currentType, currentLevel, suggestions, turnInto }
}
  1. Factor out a generic <OptionsMenu> component to handle headers, MUI wiring, keyboard nav, etc.:
// src/components/SelectionToolbar/OptionsMenu.tsx
import { Menu, MenuItem, Typography } from '@mui/material'
import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation'

export function OptionsMenu<T extends { type: string }>(props: {
  anchorEl: HTMLElement | null
  onClose(): void
  groups: { label: string; options: T[] }[]
  onSelect(opt: T): void
}) {
  const open = Boolean(props.anchorEl)
  const total = props.groups.reduce((sum, g) => sum + g.options.length, 0)
  const { getButtonProps, selectedIndex } = useKeyboardNavigation({
    itemCount: total, isOpen: open, onSelect: idx => {
      let offset = 0
      for (const group of props.groups) {
        if (idx < offset + group.options.length) {
          return props.onSelect(group.options[idx - offset])
        }
        offset += group.options.length
      }
    }, onClose: props.onClose
  })

  return (
    <Menu anchorEl={props.anchorEl} open={open} onClose={props.onClose} /* …styles… */>
      {props.groups.map((group, gIdx) => (
        <div key={gIdx}>
          <Typography variant="body2">{group.label}</Typography>
          {group.options.map((opt, idx) => {
            const globalIdx = props.groups
              .slice(0, gIdx)
              .reduce((s, g) => s + g.options.length, 0) + idx
            return (
              <MenuItem
                key={opt.type}
                ref={el => getButtonProps(globalIdx).ref?.(el as any)}
                selected={selectedIndex === globalIdx}
                sx={getButtonProps(globalIdx).sx}
                onClick={() => props.onSelect(opt)}
              >
                <opt.icon /><span>{opt.label}</span>
                {/** your checkmark logic still lives here **/}
              </MenuItem>
            )
          })}
        </div>
      ))}
    </Menu>
  )
}
  1. Your TurnInfo.tsx now just orchestrates:
import { useState, useRef } from 'react'
import { ActionButton } from './ActionButton'
import { OptionsMenu } from './OptionsMenu'
import { useTurnIntoOptions } from './useTurnIntoOptions'
import { useTranslation } from 'react-i18next'

export default function TurnInfo() {
  const { suggestions, turnInto, currentType, currentLevel } = useTurnIntoOptions()
  const [anchorEl, setAnchorEl] = useState<HTMLElement|null>(null)
  const ref = useRef<HTMLButtonElement>(null)
  const { t } = useTranslation()

  const handleSelect = (opt) => {
    // … CustomEditor.turnToBlock logic …
    setAnchorEl(null)
  }

  return (
    <>
      <ActionButton
        ref={ref}
        onClick={e => { e.preventDefault(); setAnchorEl(e.currentTarget) }}
        tooltip={/* displayText(currentType, currentLevel) */ }
      >
        {/* label + icon */}
      </ActionButton>

      <OptionsMenu
        anchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        groups={[
          { label: t('toolbar.suggestions'), options: suggestions },
          { label: t('toolbar.turnInto'),    options: turnInto }
        ]}
        onSelect={handleSelect}
      />
    </>
  )
}

These three extractions immediately cut TurnInfo.tsx by >70% while making each piece independently testable and reusable.

@lumixraku lumixraku force-pushed the feat/toolbar-style branch 2 times, most recently from f29bcd8 to 2b45683 Compare May 19, 2025 03:57
@lumixraku lumixraku force-pushed the feat/toolbar-style branch 2 times, most recently from 0e1d934 to 3e36ad0 Compare May 19, 2025 13:17
@lumixraku lumixraku force-pushed the feat/toolbar-style branch from 3e36ad0 to 39b561e Compare May 19, 2025 14:41
@lumixraku lumixraku changed the title Feat/toolbar style feat: new toolbar style May 19, 2025
@lumixraku lumixraku force-pushed the feat/toolbar-style branch from 5cfffab to 6e64f87 Compare May 20, 2025 03:31
@lumixraku lumixraku force-pushed the feat/toolbar-style branch from e44417c to 12dfeb0 Compare May 20, 2025 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants