Skip to content

feat(editor): add date grouping configurations #12679

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 36 commits into
base: canary
Choose a base branch
from

Conversation

NorkzYT
Copy link
Contributor

@NorkzYT NorkzYT commented Jun 2, 2025

This PR adds the ability to group-by date with configuration which an example is shown in the image below:

image

Summary by CodeRabbit

  • New Features

    • Added advanced date-based grouping options: relative dates, day, week (customizable start), month, and year.
    • Introduced sorting controls for date groups with "Oldest first" and "Newest first" toggles.
    • Added ability to hide empty groups and toggle visibility of all groups.
    • Enhanced group settings menu with richer date grouping configuration options.
    • Added a new date group view component showing group names and counts.
    • Added search functionality to find group-by configurations by name.
  • Improvements

    • Improved labeling and sorting of date groups for clarity and consistency.
    • Streamlined group-by matching logic and enhanced internal group trait reactivity.
    • Added consistent sorting logic for relative and numeric date keys.
    • Enhanced popup positioning using middleware for better placement and offset.
    • Updated menu component sizing with increased width and height limits for usability.
    • Added configurable submenu behavior including open-on-hover, custom positioning middleware, and auto-height styling.
    • Enabled optional menu closing behavior on selection for buttons and submenus.
    • Exposed reactive view data signals for improved data observation.
    • Added test coverage for date key comparison and sorting logic.

…lization capabilities

♻️ (define.ts): refactor existing group configurations for improved readability and maintainability

✨ (matcher.ts): add findGroupByConfigByName function to retrieve GroupByConfig by name for better configurability
✨ (date-group.ts): introduce DateGroupView component to enhance date grouping visualization in the UI

✨ (setting.ts): introduce dateModeLabel function to improve date mode labeling
♻️ (setting.ts): refactor group sorting logic for better readability and performance
📝 (setting.ts): update comments and formatting for improved code clarity
💡 (setting.ts): enhance code structure by simplifying conditional checks and removing unnecessary lines

✨ (setting.ts): add date grouping options and sorting functionality to enhance user experience
💡 (setting.ts): improve code readability by simplifying template literals and structure

✨ (trait.ts): introduce new signal and computed properties for better state management in GroupTrait class
♻️ (trait.ts): refactor group data handling and sorting logic for improved readability and maintainability
🐛 (trait.ts): fix potential issues with undefined values in groupInfo and property access
💡 (trait.ts): add comments to clarify the purpose of new functions and properties
@NorkzYT NorkzYT requested a review from a team as a code owner June 2, 2025 01:51
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

graphite-app bot commented Jun 2, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@graphite-app graphite-app bot requested a review from a team June 2, 2025 01:51
Copy link

coderabbitai bot commented Jun 2, 2025

"""

Walkthrough

This update introduces comprehensive date-based grouping options to the group-by framework, including new configurations for relative, daily, weekly, monthly, and yearly date groupings. It adds a custom DateGroupView component, enhances group setting menus with date-specific options, and refactors group trait logic to support sorting and hiding of empty date groups. Additionally, floating UI middleware support was added to various popup menus for consistent positioning, and unit tests were introduced for date key comparison logic.

Changes

File(s) Change Summary
.../group-by/define.ts Added six new date-based grouping configurations and a helper for building them; introduced DateGroupView renderer; refactored existing matcher definitions for conciseness.
.../group-by/matcher.ts Added findGroupByConfigByName function to retrieve group-by config by name; minor formatting in constructor.
.../group-by/renderer/date-group.ts Introduced new DateGroupView web component for displaying date group headers with count badges; registered as a custom element.
.../group-by/setting.ts Added dateModeLabel helper; refactored GroupSetting for conciseness; expanded popup menus for date-specific grouping options (mode, week start, sort order, hide empty groups); added optional middleware parameters to popup functions.
.../group-by/trait.ts Added date group sorting logic (compareDateKeys); added methods for hiding empty groups, changing date sort order, and switching group modes; refactored computed properties and group sorting/filtering logic for date support; updated signatures.
.../group-by/compare-date-keys.ts Added utilities to compare and sort date keys including relative and numeric keys, supporting ascending and descending order.
.../tests/compare-date-keys.unit.spec.ts Added unit tests for compareDateKeys function validating sorting behavior of relative and numeric date keys.
.../vitest.config.ts Added Vitest configuration for unit testing with ES2018 target, Istanbul coverage, and happy-dom environment.
.../package.json Added vitest dependency version ^3.2.3 for testing.
.../common/properties.ts Added optional middleware parameter to popPropertiesSetting for floating UI middleware support.
.../sort/add-sort.ts Added optional middleware parameter to popCreateSort for floating UI middleware support.
.../widget-presets/quick-setting-bar/filter/root-panel-view.ts Added optional middleware parameter to popFilterRoot for floating UI middleware support.
.../widget-presets/quick-setting-bar/sort/root-panel.ts Added optional middleware parameter to popSortRoot for floating UI middleware support.
.../widget-presets/tools/presets/view-options/view-options.ts Integrated floating UI middleware (autoPlacement, offset, shift) into popup calls for consistent popup positioning behavior; added closeMenu callback support for nested popups.
.../components/src/context-menu/menu-renderer.ts Adjusted CSS styling of menu component to increase minimum width and add height constraints; added optional placement parameter to popup functions.
.../components/src/context-menu/sub-menu.ts Added optional openOnHover, middleware, autoHeight, and closeOnSelect properties to submenu configuration; modified submenu opening logic to support these options; introduced dropdownSubMenuMiddleware constant.
.../components/src/context-menu/button.ts Added optional closeOnSelect property to menu buttons to control menu closing behavior on selection.
.../view-presets/kanban/kanban-view-manager.ts Added changeGroupHide method to KanbanSingleView groupTrait for updating group hide state.
.../view-presets/table/table-view-manager.ts Added changeGroupHide method to TableSingleView groupTrait for updating group hide state.
.../core/view-manager/single-view.ts Added reactive data$ property to SingleView interface and SingleViewBase class for accessing view data reactively.
.../widget-presets/quick-setting-bar/filter/condition-view.ts Replaced deprecated submenu middleware with explicit floating-ui middleware for positioning; adjusted menu style for dynamic sizing.
.../core/filter/add-filter.ts Added minimum height style to popup menu created by popCreateFilter.
.../property-presets/date/cell-renderer-css.ts Reduced height of date value container from 46px to 30px.
.../core/filter/literal/define.ts Added explicit size attribute to date-picker component.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GroupSetting
    participant GroupTrait
    participant DateGroupView

    User->>GroupSetting: Opens group setting popup
    GroupSetting->>GroupTrait: Reads current group config
    GroupSetting->>User: Shows date grouping options (mode, week start, sort, hide empty)
    User->>GroupSetting: Selects new date grouping mode or sort order
    GroupSetting->>GroupTrait: Updates group mode/sort/hideEmpty via methods
    GroupTrait->>DateGroupView: Updates group data and sorting
    DateGroupView->>User: Renders updated date group headers and counts
Loading

Possibly related PRs

Suggested labels

mod:i18n, app:core

Suggested reviewers

  • zzj3720

Poem

🗓️ A hop through the fields of time,
Now groups by day, week, or chime.
With badges and menus, so neat,
Sorting dates is quite a feat!
Empty groups can hide away—
Rabbits love a tidy display!
🐇✨
"""

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@graphite-app graphite-app bot requested a review from a team June 2, 2025 01:51
@NorkzYT
Copy link
Contributor Author

NorkzYT commented Jun 2, 2025

This PR is being worked and testing functionality on all options.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
blocksuite/affine/data-view/src/core/group-by/setting.ts (1)

31-47: Consider using a const object for better type safety.

The switch statement works, but a const object would provide better type safety and maintainability.

-const dateModeLabel = (key?: string) => {
-  switch (key) {
-    case 'date-relative':
-      return 'Relative';
-    case 'date-day':
-      return 'Day';
-    case 'date-week-mon':
-    case 'date-week-sun':
-      return 'Week';
-    case 'date-month':
-      return 'Month';
-    case 'date-year':
-      return 'Year';
-    default:
-      return '';
-  }
-};
+const DATE_MODE_LABELS = {
+  'date-relative': 'Relative',
+  'date-day': 'Day',
+  'date-week-mon': 'Week',
+  'date-week-sun': 'Week',
+  'date-month': 'Month',
+  'date-year': 'Year',
+} as const;
+
+const dateModeLabel = (key?: string) => {
+  return key && key in DATE_MODE_LABELS ? DATE_MODE_LABELS[key as keyof typeof DATE_MODE_LABELS] : '';
+};
blocksuite/affine/data-view/src/core/group-by/define.ts (2)

38-44: Consider making the date format configurable.

The date format is hardcoded as 'MMM d yyyy'. Consider extracting this as a constant or making it configurable for internationalization support.

+const DATE_FORMAT = 'MMM d yyyy';
+
 const rangeLabel = (a: Date, b: Date) =>
-  `${fmt(a, 'MMM d yyyy')} – ${fmt(b, 'MMM d yyyy')}`;
+  `${fmt(a, DATE_FORMAT)} – ${fmt(b, DATE_FORMAT)}`;

61-83: Consider adding internationalization support for date labels.

The relative date labels are hardcoded in English. For better internationalization support, consider making these configurable or using a translation system.

blocksuite/affine/data-view/src/core/group-by/trait.ts (1)

183-199: Consider extracting the date group check logic.

The implementation is correct, but the date group check using string prefix could be more robust.

-    if (!gi || !gi.config.name?.startsWith('date-')) return;
+    const isDateGroup = gi?.config.matchType.type === 'date';
+    if (!isDateGroup) return;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e58c11 and 792ad58.

📒 Files selected for processing (5)
  • blocksuite/affine/data-view/src/core/group-by/define.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/matcher.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (6 hunks)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (1)
blocksuite/affine/data-view/src/core/group-by/trait.ts (2)
  • property (69-71)
  • Group (55-85)
🪛 ESLint
blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)

🔇 Additional comments (13)
blocksuite/affine/data-view/src/core/group-by/matcher.ts (2)

12-22: LGTM! Well-implemented config lookup function.

The findGroupByConfigByName function provides a clean way to retrieve group configurations by name, properly combining both external and internal configurations.


25-25: Minor formatting change looks good.

The constructor formatting adjustment maintains the same functionality.

blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (1)

7-49: Well-designed date group component!

The component implementation is clean and follows best practices:

  • Proper use of reactive properties with the group accessor
  • Thoughtful CSS with hover states and flexible layout
  • Conditional rendering of the count badge
  • Good fallback text for unnamed groups
blocksuite/affine/data-view/src/core/group-by/setting.ts (1)

215-394: Excellent enhancement of date grouping UI!

The implementation provides comprehensive date grouping configuration options including:

  • Multiple date grouping modes (relative, day, week, month, year)
  • Configurable week start day
  • Sort order controls
  • Empty group visibility toggle

The UI structure is well-organized with logical menu groupings.

blocksuite/affine/data-view/src/core/group-by/define.ts (4)

13-23: LGTM! Appropriate date-fns imports.

All imported functions are utilized in the date grouping implementations.


45-59: Well-structured factory function.

The buildDateCfg helper provides a clean abstraction for creating date group configurations with consistent behavior.


141-234: Clean refactoring of existing matchers.

The ternary expressions and arrow functions improve code readability while maintaining the same functionality.


95-119: ⚠️ Potential issue

Fix incorrect week range calculation.

The week range calculation adds only 6 days instead of 6 full days (which would be 7 days total). This results in incomplete week ranges.

   v =>
     v
-      ? rangeLabel(new Date(v), new Date(v + 6 * DAY_MS))
+      ? rangeLabel(new Date(v), new Date(v + 7 * DAY_MS - 1))
       : '',

Apply the same fix to both dateWeekSunCfg and dateWeekMonCfg.

Likely an incorrect or invalid review comment.

blocksuite/affine/data-view/src/core/group-by/trait.ts (5)

29-43: Well-implemented date sorting logic.

The compareDateKeys function correctly handles different date key formats with appropriate fallbacks.


88-103: Clean constructor implementation.

The explicit constructor with properly typed parameters improves code clarity and type safety.


104-106: Good use of reactive signals for UI state.

The signals provide a clean way to manage group display options reactively.


107-124: Proper support for named group configurations.

The updated logic correctly handles both named configurations and type-based matching with appropriate fallbacks.


270-309: Correct usage of jsonValueSet.

The method properly handles moving cards between groups with appropriate value transformations.

@graphite-app graphite-app bot requested a review from a team June 3, 2025 19:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (1)

1-5: Import sorting issue persists from previous review.

The import statements are still not sorted according to the project's ESLint configuration, as previously flagged.

🧰 Tools
🪛 ESLint

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)

🧹 Nitpick comments (1)
blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (1)

40-47: Consider adding error boundary and accessibility improvements.

The render method correctly accesses the reactive signal and implements reasonable fallback logic. However, consider these improvements:

  1. Add error handling in case this.group is undefined
  2. Consider accessibility attributes for screen readers
 protected override render() {
+  if (!this.group) {
+    return html`<div class="dv-date-group">Loading...</div>`;
+  }
   const name = this.group.name$.value;
   const count = this.group.rows.length;
-  return html`<div class="dv-date-group">
+  return html`<div class="dv-date-group" role="group" aria-label="Date group: ${name || 'Ungroups'}">
     <span>${name || 'Ungroups'}</span>
-    ${count ? html`<span class="counter">${count}</span>` : ''}
+    ${count ? html`<span class="counter" aria-label="${count} items">${count}</span>` : ''}
   </div>`;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 792ad58 and 83ad609.

📒 Files selected for processing (3)
  • blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (6 hunks)
  • packages/backend/server/src/env.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/backend/server/src/env.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (1)
blocksuite/affine/data-view/src/core/group-by/trait.ts (2)
  • property (69-71)
  • Group (55-85)
🪛 ESLint
blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)

🔇 Additional comments (3)
blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts (3)

7-35: Well-structured component with proper styling.

The DateGroupView component follows good practices:

  • Proper inheritance chain with SignalWatcher for reactivity and WithDisposable for cleanup
  • CSS custom properties for theming consistency
  • Flexbox layout with appropriate spacing and hover states
  • Counter badge styling that's visually distinct

37-38: Property definition follows Lit best practices.

The group property is correctly defined with attribute: false since it's a complex object that shouldn't be serialized to an HTML attribute.


49-49: Custom element registration follows conventions.

The component is properly registered with a descriptive tag name that follows the project's naming conventions.

NorkzYT and others added 2 commits June 3, 2025 15:14
Revert of accidental removal.

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
improved date formatting for better user experience
♻️ (group-by): refactor group settings menu to conditionally display
options based on group configuration
🐛 (group-by): fix sorting order for date keys to ensure correct
relative date comparisons
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (1)
blocksuite/affine/data-view/src/core/group-by/trait.ts (1)

336-336: 💡 Verification agent

🧩 Analysis chain

Verify API change from valueSet to jsonValueSet.

The change from valueSet to jsonValueSet could be a breaking change. Ensure this is intentional and that the new method properly handles the data transformation.


🏁 Script executed:

#!/bin/bash
# Description: Check for other uses of valueSet that might need updating
# Expected: Find inconsistent usage of valueSet vs jsonValueSet

rg "\.valueSet\(" --type ts -A 1 -B 1
rg "\.jsonValueSet\(" --type ts -A 1 -B 1

Length of output: 5852


Fix inconsistent method usage in group-by trait

Two calls in blocksuite/affine/data-view/src/core/group-by/trait.ts still use .valueSet(newValue) even though the grouping logic now requires JSON values. Update both to .jsonValueSet(newValue) for correct behavior:

• In the initial aggregation block (around line 336):

- this.view.cellGetOrCreate(rowId, propertyId).valueSet(newValue);
+ this.view.cellGetOrCreate(rowId, propertyId).jsonValueSet(newValue);

• In the rows.forEach loop:

- this.view.cellGetOrCreate(rowId, propertyId).valueSet(value);
+ this.view.cellGetOrCreate(rowId, propertyId).jsonValueSet(value);

Ensure that jsonValueSet on the cell correctly accepts and persists the JSON payload.

🧹 Nitpick comments (2)
blocksuite/affine/data-view/src/core/group-by/define.ts (1)

38-59: Consider improving type safety and documentation.

The helper constants and buildDateCfg function are well-structured, but could benefit from better type annotations and documentation for the complex grouper function parameter.

+/**
+ * Creates a date grouping configuration with standardized structure
+ * @param name - Unique identifier for the grouping mode
+ * @param grouper - Function that converts ISO date string to group keys/values
+ * @param groupName - Function that formats group values for display
+ */
 function buildDateCfg(
   name: string,
-  grouper: (iso: string | null) => { key: string; value: number | null }[],
+  grouper: (iso: string | null) => Array<{ key: string; value: number | null }>,
   groupName: (v: number | null) => string,
 ): GroupByConfig {
blocksuite/affine/data-view/src/core/group-by/trait.ts (1)

191-197: Consider performance implications of date sorting.

The date group sorting logic runs on every group data change. For large datasets, this could impact performance. Consider memoization or incremental updates.

+ // Consider memoizing the sort result based on group keys and sort direction
+ const sortedKeys$ = computed(() => {
+   const map = this.groupDataMap$.value;
+   const gi = this.groupInfo$.value;
+   if (!map || !gi) return [];
+   
+   return [...Object.keys(map)].sort(
+     compareDateKeys(gi.config.name, this.sortAsc$.value)
+   );
+ });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83ad609 and 4d2d43d.

📒 Files selected for processing (3)
  • blocksuite/affine/data-view/src/core/group-by/define.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (6 hunks)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts
🔇 Additional comments (6)
blocksuite/affine/data-view/src/core/group-by/define.ts (4)

10-10: LGTM: Clean imports for date functionality.

The imports are well-organized and include all necessary date-fns functions and the new DateGroupView component.

Also applies to: 13-22


147-147: LGTM: Clean refactoring to ternary operators.

The refactoring from verbose if/else blocks to ternary operators improves code conciseness while maintaining the same logic. The select, multi-select, and other existing configurations are correctly updated.

Also applies to: 157-160, 169-184


237-242:

✅ Verification successful

Verify integration with date grouping system.

The new date configurations are properly added to the groupByMatchers array. Ensure these work correctly with the DateGroupView component and trait system.


🏁 Script executed:

#!/bin/bash
# Description: Verify DateGroupView component exists and integrates correctly
# Expected: Find the DateGroupView implementation and its usage

fd "date-group" --type f --extension ts
rg "DateGroupView" --type ts -A 3

Length of output: 1321


Integration with DateGroupView Verified

  • DateGroupView is implemented in blocksuite/affine/data-view/src/core/group-by/renderer/date-group.ts and registered as data-view-date-group-view.
  • It’s correctly imported in define.ts and used via createUniComponentFromWebComponent(DateGroupView) alongside the new date configs.

All integrations look correct—no further action needed.


61-84:

✅ Verification successful

Verify edge cases in relative date grouping.

The relative date logic handles most cases well, but consider edge cases around daylight saving time transitions and timezone boundaries.


🏁 Script executed:

#!/bin/bash
# Description: Check for potential timezone-related issues in date grouping
# Expected: Find any hardcoded timezone assumptions or DST handling

rg -A 3 -B 3 "startOfDay|differenceInCalendarDays" --type ts

Length of output: 2966


No DST or timezone issues in relative date grouping

The startOfDay and differenceInCalendarDays functions from date-fns already account for local timezone offsets and daylight-saving shifts (the latter via an explicit timezone-offset correction and rounding). There are no hardcoded timezone assumptions here, so this grouping logic will remain correct across DST transitions and at timezone boundaries.

blocksuite/affine/data-view/src/core/group-by/trait.ts (2)

256-265: LGTM: Clean method for changing group modes.

The changeGroupMode method properly integrates with the new date grouping configurations and maintains sort state consistency.


110-133:

✅ Verification successful

Verify reactive state synchronization.

The effect that syncs sortAsc$ with the groupBy sort descriptor looks correct, but ensure it doesn't create circular dependencies or infinite loops.


🏁 Script executed:

#!/bin/bash
# Description: Check for potential circular dependencies in reactive signals
# Expected: Find other places where sortAsc$ or groupBy sort is modified

rg "sortAsc\$\.value\s*=" --type ts -A 2 -B 2
rg "sort.*desc" --type ts -A 2 -B 2

Length of output: 8413


🏁 Script executed:

#!/bin/bash
# Inspect the GroupBy trait implementation to understand two-way binding
sed -n '1,200p' blocksuite/affine/data-view/src/core/group-by/trait.ts

Length of output: 5838


No circular reactive dependencies detected

Verified that the effect only updates sortAsc$ when groupBy$.value.sort.desc changes, and all user-driven toggles go through groupBySet({ …, sort: { desc: !this.sortAsc$.value } }). Because the effect immediately reconciles sortAsc$ to !desc (and desc was set to !sortAsc$), it’s a no-op on the next run and doesn’t loop.
No further fixes required.

Copy link

codecov bot commented Jun 5, 2025

Codecov Report

Attention: Patch coverage is 32.43243% with 350 lines in your changes missing coverage. Please review.

Project coverage is 55.77%. Comparing base (29826e1) to head (17e25d0).
Report is 1 commits behind head on canary.

Files with missing lines Patch % Lines
...ksuite/affine/data-view/src/core/group-by/trait.ts 40.00% 106 Missing and 2 partials ⚠️
...suite/affine/data-view/src/core/group-by/define.ts 15.18% 67 Missing ⚠️
...uite/affine/data-view/src/core/group-by/setting.ts 39.39% 60 Missing ⚠️
...ite/affine/components/src/context-menu/sub-menu.ts 29.03% 22 Missing ⚠️
...presets/tools/presets/view-options/view-options.ts 0.00% 19 Missing ⚠️
...resets/quick-setting-bar/filter/root-panel-view.ts 6.25% 15 Missing ⚠️
...-view/src/view-presets/table/table-view-manager.ts 0.00% 14 Missing ⚠️
...iew/src/view-presets/kanban/kanban-view-manager.ts 35.71% 9 Missing ⚠️
...e/data-view/src/core/group-by/compare-date-keys.ts 66.66% 3 Missing and 5 partials ⚠️
...presets/quick-setting-bar/filter/condition-view.ts 0.00% 7 Missing ⚠️
... and 8 more
Additional details and impacted files
@@            Coverage Diff             @@
##           canary   #12679      +/-   ##
==========================================
- Coverage   55.79%   55.77%   -0.03%     
==========================================
  Files        2658     2660       +2     
  Lines      125984   126249     +265     
  Branches    19960    20051      +91     
==========================================
+ Hits        70298    70413     +115     
- Misses      54063    54209     +146     
- Partials     1623     1627       +4     
Flag Coverage Δ
server-test 78.87% <ø> (+0.03%) ⬆️
unittest 31.62% <13.12%> (-0.06%) ⬇️

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.

NorkzYT and others added 3 commits June 5, 2025 06:43
… sort date keys

♻️ (define.ts): refactor addToGroup functions to include type annotations for better clarity and maintainability

♻️ (trait.ts): refactor date comparison logic to improve readability and maintainability by removing unnecessary complexity
✨ (trait.ts): update group sorting logic to use new comparison method for better performance
📝 (vitest.config.ts): add Vitest configuration file to set up testing environment and coverage reporting
@github-actions github-actions bot added the test Related to test cases label Jun 5, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
blocksuite/affine/data-view/src/core/group-by/compare-date-keys.ts (1)

38-59: Consider documenting the sorting priority for date-relative mode.

The function implements a sophisticated fallback strategy: relative keys → numeric keys → lexicographic comparison. This behavior should be documented for maintainability.

Add JSDoc documentation to clarify the sorting priority:

+/**
+ * Creates a comparator function for date keys
+ * 
+ * For 'date-relative' mode, sorting priority is:
+ * 1. Relative date keys (today, yesterday, etc.)
+ * 2. Numeric keys (timestamps)
+ * 3. Lexicographic comparison (fallback)
+ * 
+ * For other modes, uses numeric comparison followed by lexicographic.
+ * 
+ * @param mode - The date grouping mode
+ * @param asc - Whether to sort in ascending order
+ * @returns Comparator function for Array.sort()
+ */
 export function compareDateKeys(mode: string | undefined, asc: boolean) {
blocksuite/affine/data-view/src/__tests__/compare-date-keys.unit.spec.ts (1)

28-34: Add more specific assertions for mixed key sorting behavior.

While the test validates the general sorting priority (numeric keys before relative keys), it could be more explicit about the expected ordering.

Consider adding more specific assertions to document the expected behavior:

 it('handles mixed relative and numeric keys', () => {
     const cmp = compareDateKeys('date-relative', true);
     const keys = ['today', '1', 'yesterday', '2'];
     const sorted = [...keys].sort(cmp);
-    expect(sorted[0]).toBe('1');
-    expect(sorted[sorted.length - 1]).toBe('today');
+    // Numeric keys should come first, then relative keys in chronological order
+    expect(sorted).toEqual(['1', '2', 'yesterday', 'today']);
 });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0d5693 and 6f1bbfb.

📒 Files selected for processing (5)
  • blocksuite/affine/data-view/src/__tests__/compare-date-keys.unit.spec.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/compare-date-keys.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/define.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (6 hunks)
  • blocksuite/affine/data-view/vitest.config.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • blocksuite/affine/data-view/vitest.config.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • blocksuite/affine/data-view/src/core/group-by/define.ts
  • blocksuite/affine/data-view/src/core/group-by/trait.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
blocksuite/affine/data-view/src/__tests__/compare-date-keys.unit.spec.ts (1)
blocksuite/affine/data-view/src/core/group-by/compare-date-keys.ts (1)
  • compareDateKeys (38-59)
🔇 Additional comments (5)
blocksuite/affine/data-view/src/core/group-by/compare-date-keys.ts (3)

1-7: LGTM! Clean constant definitions with good naming.

The constants are well-defined and the use of as const ensures proper type inference. The reverse operation for descending order is clean and efficient.


27-36: Good use of Number.isFinite for robust numeric validation.

The function correctly uses Number.isFinite() to avoid issues with NaN and Infinity values, and appropriately returns 0 when keys aren't both numeric.


12-22:

✅ Verification successful

Verify the sorting logic for mixed relative/non-relative keys.

The function correctly handles the case where both keys are relative date keys, but the logic for mixed cases (one relative, one non-relative) needs verification.


🏁 Script executed:

#!/bin/bash
# Verify that the sorting behavior matches the expected test cases
# by examining how relative keys are prioritized over non-relative keys

# Look for test cases that validate mixed key sorting
rg -A 10 -B 2 "mixed.*relative" 
rg -A 10 -B 2 "handles mixed"

Length of output: 2189


Mixed relative/non-relative key sorting is already covered by existing tests

The unit test in blocksuite/affine/data-view/src/__tests__/compare-date-keys.unit.spec.ts (“handles mixed relative and numeric keys”) confirms that numeric keys are sorted before relative keys when asc is true. The sortRelativeKeys implementation correctly returns positive for relative vs. non-relative (and negative for the inverse), matching the test expectations. No changes required.

blocksuite/affine/data-view/src/__tests__/compare-date-keys.unit.spec.ts (2)

6-18: Excellent test coverage for relative date key sorting.

These tests properly validate both ascending and descending order for relative date keys, with clear expectations that match the defined constants.


20-26: Good numeric key sorting validation.

The tests correctly verify numeric string comparison in both ascending and descending order for non-relative date modes.

@zzj3720 zzj3720 changed the title feat(group-by): add date grouping configurations feat(editor): add date grouping configurations Jun 6, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/sort/root-panel.ts (1)

226-228: 🛠️ Refactor suggestion

Sub-menu misses the middleware you just introduced

popCreateSort is now middleware–aware, but the inner invocation doesn’t forward the array you already accepted in popSortRoot.
Forward it so submenu placement is consistent:

-            popCreateSort(popupTargetFromElement(ele), {
-              sortUtils: props.sortUtils,
-            });
+            popCreateSort(
+              popupTargetFromElement(ele),
+              { sortUtils: props.sortUtils },
+              { middleware: (middleware ?? []).filter(Boolean) as Middleware[] }
+            );

Without this, the “Add sort” panel falls back to its default placement while the parent obeys the new middleware, resulting in a jarring UI jump.

♻️ Duplicate comments (2)
blocksuite/affine/data-view/src/core/group-by/setting.ts (2)

276-284: Redundant type === 'date' guard is still present
This block is already inside a 'date' branch; the extra ternary wrapper only adds noise.


295-300: Nested ternary for week mode not addressed

The earlier review suggested extracting this into a weekMode constant for clarity. The nested ternary is still here and remains hard to scan.

🧹 Nitpick comments (8)
blocksuite/affine/data-view/src/core/sort/add-sort.ts (1)

18-24: Guard against undefined middleware & keep the payload lean

popMenu receives middleware: ops?.middleware, which will explicitly set the property to undefined when ops is not supplied.
While many implementations tolerate an undefined, it is cleaner – and safer for future refactors – to omit the key entirely when no middleware is provided:

-  popMenu(target, {
-    middleware: ops?.middleware,
+  popMenu(target, {
+    ...(ops?.middleware && { middleware: ops.middleware }),

This prevents unnecessary property injections and spares a needless runtime check inside popMenu.

blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts (1)

384-390: Omit middleware key when absent

Same nit as in add-sort.ts: pass the key conditionally to avoid { middleware: undefined }.

-  popMenu(target, {
-    middleware,
+  popMenu(target, {
+    ...(middleware && { middleware }),

Keeps the payload minimal and future-proof.

blocksuite/affine/data-view/src/core/common/properties.ts (1)

240-244: Consistent conditional spread for middleware

Replicate the conditional-spread pattern here as well:

-  popMenu(target, {
-    middleware,
+  popMenu(target, {
+    ...(middleware && { middleware }),

Prevents sending an explicit undefined value downstream.

blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (1)

121-125: DRY up the duplicated Floating-UI middleware arrays

The exact three-element array (autoPlacement, offset, shift) is repeated many times in this file (and others).
Extracting it into a constant will:

  1. Eliminate copy-paste noise.
  2. Make future tuning (e.g., offset tweaks) one-shot.
  3. Reduce the risk of accidental drift.

Example:

// utils/floating-ui.ts (or local to this file)
import { autoPlacement, offset, shift, type Middleware } from '@floating-ui/dom';

export const defaultMenuMiddleware: Middleware[] = [
  autoPlacement({ allowedPlacements: ['bottom-start', 'top-start'] }),
  offset({ mainAxis: 12, crossAxis: -75 }),
  shift({ crossAxis: true }),
];

// usage
popPropertiesSetting(target, props, defaultMenuMiddleware);
popCreateFilter(target, opts, { middleware: defaultMenuMiddleware });
...

Makes the intent crystal clear and trims ~60 lines of boilerplate.

Also applies to: 167-174

blocksuite/affine/data-view/src/core/group-by/setting.ts (4)

32-48: Replace switch with a map to reduce boilerplate

A simple object literal is more concise and scales better when more modes are introduced.

-const dateModeLabel = (key?: string) => {
-  switch (key) {
-    case 'date-relative':
-      return 'Relative';
-    case 'date-day':
-      return 'Day';
-    case 'date-week-mon':
-    case 'date-week-sun':
-      return 'Week';
-    case 'date-month':
-      return 'Month';
-    case 'date-year':
-      return 'Year';
-    default:
-      return '';
-  }
-};
+const DATE_MODE_LABEL: Record<string, string> = {
+  'date-relative': 'Relative',
+  'date-day': 'Day',
+  'date-week-mon': 'Week',
+  'date-week-sun': 'Week',
+  'date-month': 'Month',
+  'date-year': 'Year',
+};
+const dateModeLabel = (key?: string) => DATE_MODE_LABEL[key ?? ''] ?? '';

213-217: UI copy is inconsistent (‘Remove Grouping’ vs ‘Remove grouping’)

Two actions that perform the same operation use different capitalisation. This will look sloppy in the menu.

Unify the wording (either title-case or sentence-case) in both places.

Also applies to: 465-471


291-323: Repeated inline colour logic ⇒ extract helper

isSelected ? emphasisedColor : secondaryColor is duplicated multiple times across the menu construction. Extracting a small helper (e.g., selectedColor(is) or a styled <span> wrapper) will shrink this block dramatically and make future tweaks painless.


443-449: Toggle action lacks keyboard shortcut hint

Many AFFiNE menu actions show shortcut hints; adding one here (e.g. ⇧E?) keeps the UX coherent.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13bcb4d and e633793.

📒 Files selected for processing (6)
  • blocksuite/affine/data-view/src/core/common/properties.ts (2 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (7 hunks)
  • blocksuite/affine/data-view/src/core/sort/add-sort.ts (2 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts (2 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/sort/root-panel.ts (2 hunks)
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (7 hunks)

NorkzYT added 2 commits June 14, 2025 12:54
…and UI elements

feat(group-by): implement group hide state management in Group and GroupTrait classes
feat(kanban/table): add changeGroupHide method to update group visibility in view managers
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
blocksuite/affine/data-view/src/core/group-by/trait.ts (2)

152-156: Side-effect inside computed is still present

groupInfo$ calls view.propertyGetOrCreate, which mutates state during a reactive computation – the exact concern raised in a previous review. This risks subtle re-compute loops and violates computed purity.
Consider using a side-effect-free getter (or ensuring the property exists before the signal is accessed).


191-202: Rows keep accumulating on every recomputation

groupDataMap$ clones staticMap but re-uses the same Group instances whose rows arrays aren’t cleared. Each re-evaluation appends the same rows again, leading to duplicates & memory growth – the issue flagged earlier but still unfixed.

-const map: Record<string, Group> = { ...staticMap };
+const map: Record<string, Group> = { ...staticMap };
+Object.values(map).forEach(g => (g.rows.length = 0)); // reset
🧹 Nitpick comments (2)
blocksuite/affine/data-view/src/view-presets/kanban/kanban-view-manager.ts (1)

139-150: Avoid needless writes when hide flag is unchanged

dataUpdate will still enqueue a patch even when the existing hide value already matches the requested one, triggering an extra re-render.
Guard early to save a signal emission:

-const idx = list.findIndex(g => g.key === key);
-if (idx >= 0) {
-  list[idx] = { ...list[idx], hide };
+const idx = list.findIndex(g => g.key === key);
+if (idx >= 0) {
+  if (list[idx].hide === hide) {
+    return {};              // no-op – bail early
+  }
+  list[idx] = { ...list[idx], hide };
blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts (1)

167-176: Same early-exit optimisation applies here

Replicate the short-circuit to avoid emitting an identical groupProperties array when nothing actually changed.

-if (idx >= 0) {
-  list[idx] = { ...list[idx], hide };
+if (idx >= 0) {
+  if (list[idx].hide === hide) {
+    return {};          // nothing to update
+  }
+  list[idx] = { ...list[idx], hide };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c89c160 and 72dba9d.

📒 Files selected for processing (4)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (8 hunks)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (5 hunks)
  • blocksuite/affine/data-view/src/view-presets/kanban/kanban-view-manager.ts (1 hunks)
  • blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts

feat(group-by): introduce groupsDataListAll$ to include hidden groups for better UI management

style(group-by): add group-hidden class to visually indicate hidden groups with reduced opacity
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
blocksuite/affine/data-view/src/core/group-by/setting.ts (2)

421-426: Nested ternary resurrected – please extract to a constant for readability

The Week entry still relies on the double ternary that was already flagged in a previous review.

['Week',
  group.groupInfo$.value?.config.name === 'date-week-mon'
    ? 'date-week-mon'
    : 'date-week-sun']

A one-liner constant (e.g. const weekMode = …) keeps the JSX block clean and prevents the visual “arrow of doom”.


402-404: Redundant type === 'date' guard still present

popGroupSetting has already short-circuited when type is not 'date', yet we wrap the whole date submenu in

...(type === 'date' ? [ /* … */ ] : [])

The extra check bloats diff noise and future maintenance.
Delete the guard and keep the array literal directly.

blocksuite/affine/data-view/src/core/group-by/trait.ts (1)

152-156: Side-effect inside computed – still uses propertyGetOrCreate

groupInfo$ calls view.propertyGetOrCreate, which creates a property during reactive evaluation.
Computed signals are expected to be pure; creating state here can trigger subtle recomputation loops.
Switch to propertyGet and ensure the caller creates the column beforehand.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72dba9d and 44ed1f6.

📒 Files selected for processing (2)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (8 hunks)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (5 hunks)

…remove unused postfix function in popGroupSetting and maintain order when inserting new entries in lists to improve code clarity and functionality
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
blocksuite/affine/data-view/src/core/group-by/setting.ts (2)

384-386: Redundant type === 'date' guard still present
Same issue pointed out in previous review – this block already sits inside a 'date' type branch.


404-408: Nested ternary for week mode unchanged
Consider extracting into const weekMode = … for readability as suggested earlier.

🧹 Nitpick comments (2)
blocksuite/affine/data-view/src/core/group-by/setting.ts (2)

34-50: Replace switch with a declarative map for scalability

A mapping object + fallback is easier to extend and avoids repetitive case blocks.

-const dateModeLabel = (key?: string) => {
-  switch (key) {
-    case 'date-relative':
-      return 'Relative';
-    case 'date-day':
-      return 'Day';
-    case 'date-week-mon':
-    case 'date-week-sun':
-      return 'Week';
-    case 'date-month':
-      return 'Month';
-    case 'date-year':
-      return 'Year';
-    default:
-      return '';
-  }
-};
+const DATE_MODE_LABEL: Record<string, string> = {
+  'date-relative': 'Relative',
+  'date-day': 'Day',
+  'date-week-mon': 'Week',
+  'date-week-sun': 'Week',
+  'date-month': 'Month',
+  'date-year': 'Year',
+};
+
+const dateModeLabel = (key?: string) => DATE_MODE_LABEL[key ?? ''] ?? '';

Cleaner, easier to read, and O(1) look-ups.


63-92: Scrollbar styling: add prefers-reduced-motion & non-WebKit support

Nice UX touch, but consider:

  1. Firefox: colours set, but width cannot be styled – fine.
  2. Windows high-contrast / accessibility: expose via prefers-reduced-motion or allow users to override.

Optional, but improves accessibility cross-platform.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44ed1f6 and 589f0a9.

📒 Files selected for processing (3)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (8 hunks)
  • blocksuite/affine/data-view/src/view-presets/kanban/kanban-view-manager.ts (1 hunks)
  • blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts
  • blocksuite/affine/data-view/src/view-presets/kanban/kanban-view-manager.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
blocksuite/affine/data-view/src/core/group-by/setting.ts (7)
blocksuite/affine/data-view/src/core/group-by/trait.ts (3)
  • property (54-56)
  • GroupTrait (98-499)
  • view (75-77)
blocksuite/affine/data-view/src/view-presets/kanban/kanban-view-manager.ts (3)
  • type (191-193)
  • view (195-197)
  • KanbanSingleView (22-284)
blocksuite/affine/data-view/src/view-presets/table/table-view-manager.ts (2)
  • type (213-215)
  • TableSingleView (29-287)
blocksuite/affine/data-view/src/core/group-by/types.ts (1)
  • GroupRenderProps (5-11)
blocksuite/affine/data-view/src/core/view-manager/property.ts (1)
  • icon (142-145)
blocksuite/affine/components/src/icons/text.ts (2)
  • ViewIcon (270-273)
  • DeleteIcon (154-157)
blocksuite/affine/components/src/context-menu/menu-renderer.ts (2)
  • PopupTarget (402-406)
  • popMenu (520-565)

…ns to MenuSubMenu for enhanced customization

fix(context-menu): adjust event handling for mouse enter based on openOnHover setting
feat(group-by): integrate dropdownSubMenuMiddleware and new options in popGroupSetting for improved submenu behavior
refactor(group-by): separate visible and hidden groups in GroupTrait for better management of group visibility
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
blocksuite/affine/data-view/src/core/group-by/trait.ts (4)

152-156: ⚠️ Potential issue

Side-effect inside a computed is still present

view.propertyGetOrCreate mutates application state every time the reactive
computation runs, breaking computed-purity and risking infinite
re-computations.
The exact same problem was pointed out in a previous review but has not been
addressed.

-const prop = this.view.propertyGetOrCreate(gb.columnId);
+// Do not create the property here – just read it
+const prop = this.view.propertyGet(gb.columnId);
+if (!prop) return;

Move the “create if missing” logic outside the reactive graph (e.g. right
before groupInfo$ is first accessed).


191-202: ⚠️ Potential issue

Rows accumulate & cellGetOrCreate mutates during recompute

  1. Group.rows is never cleared, so every recomputation appends another copy of
    each row, causing duplicates and an ever-growing memory footprint.
  2. view.cellGetOrCreate inside the loop mutates the data model, again violating
    computed-purity.

Both issues were flagged earlier.

// before assigning rows
-const map: Record<string, Group> = { ...staticMap };
+const map: Record<string, Group> = { ...staticMap };
+Object.values(map).forEach(g => (g.rows.length = 0));

-const cell = this.view.cellGetOrCreate(row.rowId, groupInfo.property.id);
+const cell = this.view.cellGet(row.rowId, groupInfo.property.id);
+if (!cell) return; // or skip the row

331-338: ⚠️ Potential issue

Inconsistent setter – should use jsonValueSet

addToGroup writes via valueSet, while the rest of the codebase (including
moveCardTo) uses jsonValueSet. Mixing the two risks stale caches or
conversion bugs.

- this.view.cellGetOrCreate(rowId, info.property.id).valueSet(newVal);
+ this.view.cellGetOrCreate(rowId, info.property.id).jsonValueSet(newVal);

490-496: ⚠️ Potential issue

Same setter inconsistency in removeFromGroup

The value is written with valueSet; for parity with other paths use
jsonValueSet.

- this.view.cellGetOrCreate(rowId, propId).valueSet(newValue);
+ this.view.cellGetOrCreate(rowId, propId).jsonValueSet(newValue);
🧹 Nitpick comments (2)
blocksuite/affine/components/src/context-menu/sub-menu.ts (2)

25-28: Consider adding JSDoc / comments for the three new flags.

openOnHover, middleware, and autoHeight subtly change submenu behaviour. A short doc-comment in the type alias (or at least inline /** … */) will spare future readers a trip through the implementation each time they wonder what the defaults are and how they interact.


90-93: autoHeight style duplication—extract helper or CSS class.

The identical four-line block that forces minHeight/maxHeight to fit-content now appears three times.
Factor it into a tiny helper or dedicate a CSS utility class to keep the logic DRY and easier to adjust later.

Also applies to: 112-115, 174-177

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 589f0a9 and 1db0071.

📒 Files selected for processing (3)
  • blocksuite/affine/components/src/context-menu/sub-menu.ts (8 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (8 hunks)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts
🔇 Additional comments (2)
blocksuite/affine/components/src/context-menu/sub-menu.ts (2)

38-42: dropdownSubMenuMiddleware is declared but not used in this file.

If it’s intended for external consumption, export it; if it’s only a convenience for callers, place it next to the code that actually references it to avoid “dead-code” alarms.


90-93: Verify cross-browser support for fit-content on min/max-height.

fit-content is well-supported in modern evergreen browsers but still elicits issues in some older Safari/Chromium versions. If you need broader coverage consider a fallback (auto) or feature test.

…bMenu for better control over menu behavior

feat(data-view): add onClose callback to popGroupSetting and popFilterRoot for enhanced closure handling
feat(view-manager): add data$ property to SingleView interface for improved data management
feat(sort): add onClose option to popSortRoot for consistent closure handling across components

feat(view-options.ts): add closeMenu callback to createSettingMenus for better menu management
feat(view-options.ts): set closeOnSelect to false for multiple menu actions to prevent premature closure
refactor(view-options.ts): update popViewOptions to handle menu close events more effectively
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
blocksuite/affine/data-view/src/core/group-by/setting.ts (4)

176-179: Constant fallback key 'k' still risks collisions
Same concern raised previously: different undefined groups share the same DnD id, breaking pointer re-ordering.

See earlier review; please address or acknowledge.


400-404: Redundant type === 'date' wrapper
Already inside a date-specific branch; condition adds noise without value.

(Flagged in prior review.)


423-427: Nested ternary for week mode is still present

Readability issue noted earlier remains. Extract a helper or constant instead of the inline ternary.


584-588: columnId prop passed but not declared

<data-view-group-setting … .columnId=${gProp.id}>GroupSetting lacks such a property, causing TS / lit warnings.

(Same remark in previous review.)

blocksuite/affine/components/src/context-menu/sub-menu.ts (1)

93-107: Missing middleware fallback in click path

When openOnHover === false, popMenu is called with middleware: this.data.middleware directly.
If callers omit the field, the default placement stack is lost and sub-menus may spawn at (0,0).

Identical issue reported earlier; please apply the fallback used further below:

-        middleware: this.data.middleware,
+        middleware: this.data.middleware ?? subMenuMiddleware,

(and mirror in MobileSubMenu’s openSubMenu).

🧹 Nitpick comments (3)
blocksuite/affine/data-view/src/core/view-manager/single-view.ts (1)

88-90: Mark data$ as public readonly

-  data$ = computed(() => {
+  public readonly data$ = computed(() => {

Making intent explicit eliminates doubts around mutation and keeps the declaration aligned with the interface above.
No functional impact, purely clarity.

blocksuite/affine/components/src/context-menu/button.ts (1)

84-90: Duplication between desktop & mobile click-handlers

MenuButton.onClick and MobileMenuButton.onClick now share identical logic (lines 84-90 and 146-152). Consider extracting a small helper to avoid future drift:

-  onClick() {
-    if (this.data.select(this) !== false) {
-      this.menu.options.onComplete?.();
-      if (this.data.closeOnSelect !== false) {
-        this.menu.close();
-      }
-    }
-  }
+  private handleSelect() {
+    if (this.data.select(this) !== false) {
+      this.menu.options.onComplete?.();
+      if (this.data.closeOnSelect !== false) this.menu.close();
+    }
+  }

Then invoke this.handleSelect() from both subclasses.
Keeps behaviour in one place, easier to tweak later.

blocksuite/affine/data-view/src/core/group-by/setting.ts (1)

35-51: Hard-coded English labels & fall-through default

dateModeLabel hard-codes English strings and returns an empty string for unknown keys.

  1. Surface labels through your i18n utility or constant map.
  2. Consider returning the key itself (or throwing) when unmapped to avoid silent blanks.
-const dateModeLabel = (key?: string) => {
-  switch (key) {
-    case 'date-relative': return 'Relative';
-    ...
-    default: return '';
-  }
-};
+const DATE_MODE_LABEL: Record<string, string> = {
+  'date-relative': 'Relative',
+  'date-day': 'Day',
+  'date-week-mon': 'Week',
+  'date-week-sun': 'Week',
+  'date-month': 'Month',
+  'date-year': 'Year',
+};
+const dateModeLabel = (k?: string) => DATE_MODE_LABEL[k ?? ''] ?? k ?? '';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1db0071 and 9270b94.

📒 Files selected for processing (7)
  • blocksuite/affine/components/src/context-menu/button.ts (5 hunks)
  • blocksuite/affine/components/src/context-menu/sub-menu.ts (8 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (8 hunks)
  • blocksuite/affine/data-view/src/core/view-manager/single-view.ts (1 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts (2 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/sort/root-panel.ts (2 hunks)
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/sort/root-panel.ts
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts

… performance and flexibility in rendering options
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
blocksuite/affine/data-view/src/core/group-by/setting.ts (4)

175-179: Potential duplicate/empty drag-and-drop IDs remain
Fallback key 'k' can still collide and break dnd-kit. Previous feedback is still applicable.


220-223: repeat key fallback may collide
Using constant 'k' risks duplicate keys; please generate a stable unique fallback or filter undefined items.


426-430: Nested ternary hampers readability
The earlier suggestion to extract weekMode into a variable is still unaddressed.


598-602: columnId attribute is still undeclared in GroupSetting
Passing an undeclared reactive property triggers no-unknown-property lint errors and wastes memory. Either declare it or remove the attribute.

🧹 Nitpick comments (1)
blocksuite/affine/data-view/src/core/group-by/setting.ts (1)

35-51: Replace verbose switch with a constant map for easier maintenance

A lookup table shortens the code and avoids future switch-statement churn when new modes are added.

-const dateModeLabel = (key?: string) => {
-  switch (key) {
-    case 'date-relative':
-      return 'Relative';
-    case 'date-day':
-      return 'Day';
-    case 'date-week-mon':
-    case 'date-week-sun':
-      return 'Week';
-    case 'date-month':
-      return 'Month';
-    case 'date-year':
-      return 'Year';
-    default:
-      return '';
-  }
-};
+const DATE_MODE_LABEL: Record<string, string> = {
+  'date-relative': 'Relative',
+  'date-day': 'Day',
+  'date-week-mon': 'Week',
+  'date-week-sun': 'Week',
+  'date-month': 'Month',
+  'date-year': 'Year',
+};
+const dateModeLabel = (key?: string) => DATE_MODE_LABEL[key ?? ''] ?? '';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9270b94 and b1cad86.

📒 Files selected for processing (1)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (8 hunks)

…o allow dynamic height adjustment

fix(view-options.ts): set min-height for menu dynamically to ensure consistent UI across components
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (2)

124-128: DRY up the repeated floating-UI middleware array

The identical middleware tuple (autoPlacement, offset, shift) is inlined nine times. Centralising it:

+const POSITION_MIDDLEWARE = [
+  autoPlacement({ allowedPlacements: ['bottom-start', 'top-start'] }),
+  offset({ mainAxis: 15, crossAxis: -162 }),
+  shift({ crossAxis: true }),
+] as const;

and replacing all occurrences with POSITION_MIDDLEWARE:

- middleware: [
-   autoPlacement({ allowedPlacements: ['bottom-start', 'top-start'] }),
-   offset({ mainAxis: 15, crossAxis: -162 }),
-   shift({ crossAxis: true }),
- ],
+ middleware: POSITION_MIDDLEWARE,

• removes >70 duplicated LOC
• guarantees future tweaks stay consistent.

Also applies to: 173-179, 185-190, 203-208, 247-253, 300-304, 309-314, 318-323, 499-503


504-505: Hard-coded min-height – favour CSS over runtime mutation

Direct style mutation couples logic & presentation and is hard to theme. Prefer a CSS rule:

.affine-view-settings-menu {
  min-height: 550px;
}

and assign the class to menu.menuElement once, instead of mutating style at runtime.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1cad86 and 0fcb2a6.

📒 Files selected for processing (2)
  • blocksuite/affine/components/src/context-menu/menu-renderer.ts (2 hunks)
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • blocksuite/affine/components/src/context-menu/menu-renderer.ts
🔇 Additional comments (1)
blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (1)

469-480: closeOnSelect: false looks wrong for destructive actions

Keeping the menu open after Duplicate or Delete feels counter-intuitive and risks repeat activation. Unless UX research explicitly asked for this, flip the flag or call closeMenu inside the select handler.

- closeOnSelect: false,
+ closeOnSelect: true,

(or invoke closeMenu() manually).

@NorkzYT
Copy link
Contributor Author

NorkzYT commented Jun 15, 2025

This PR is not ready. Still working on it.

…, sort/root-panel.ts, view-options.ts): set minimum height for menu elements to improve UI consistency and usability
Copy link

socket-security bot commented Jun 15, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License

View full report

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (5)
blocksuite/affine/data-view/src/core/group-by/setting.ts (5)

165-172: Still no guard when indices are -1

Previous review flagged this; calling moveGroupTo with -1 will throw or corrupt state if drag context becomes stale.

-const aIndex = groups.findIndex(g => g?.key === activeId);
-const oIndex = groups.findIndex(g => g?.key === over.id);
-if (aIndex > oIndex ? ... )
+const aIndex = groups.findIndex(g => g?.key === activeId);
+const oIndex = groups.findIndex(g => g?.key === over.id);
+if (aIndex === -1 || oIndex === -1) return; // stale drag context
+this.groupTrait.moveGroupTo(
+  activeId,
+  aIndex > oIndex ? { before: true, id: over.id } : { before: false, id: over.id }
+);

176-179: Duplicate key danger with constant 'k' fallback

Re-using 'k' for undefined groups can collapse virtual-DOM sub-trees and break DnD. Either filter out undefined first or generate a UUID.


404-406: Redundant type === 'date' check

You already bailed out when !type, but not when type !== 'date'; however all subsequent branches are inside this extra check, repeating earlier reviewer note.


430-434: Nested ternary still hurts readability

Extract to a weekMode variable as previously suggested.


601-606: columnId prop still undeclared in GroupSetting

You pass .columnId=${gProp.id} but @property accessor columnId is missing. TS/ESLint will warn, and the value is discarded.

🧹 Nitpick comments (5)
blocksuite/affine/data-view/src/core/group-by/setting.ts (3)

35-51: Prefer mapping object over long switch for dateModeLabel

Inline switches grow unwieldy as new modes are added and make i18n harder. A constant lookup table is clearer and O(1).

-const dateModeLabel = (key?: string) => {
-  switch (key) {
-    case 'date-relative':
-      return 'Relative';
-    ...
-    default:
-      return '';
-  }
-};
+const DATE_MODE_LABEL: Record<string, string> = {
+  'date-relative': 'Relative',
+  'date-day': 'Day',
+  'date-week-mon': 'Week',
+  'date-week-sun': 'Week',
+  'date-month': 'Month',
+  'date-year': 'Year',
+};
+const dateModeLabel = (key?: string) => DATE_MODE_LABEL[key ?? ''] ?? '';

64-93: Hard-coded scrollbar colours bypass theme vars

#b0b0b0 ignores dark/light themes. Expose these via cssVarV2 or dataViewCssVariable() so they react to theme changes.


325-330: Magic number 550px duplicated

minHeight is hard-coded in several popups; centralise as a CSS var or constant to keep look-&-feel consistent.

blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/condition-view.ts (2)

110-123: Repeated inline styling – extract menu sizing constant

Setting handler.menu.menuElement.style.minHeight = '550px' is duplicated across codebase. Consider a helper (e.g. applyDefaultMenuSizing(handler)) or a CSS class.


146-147: Same magic 550px value duplicated here

See previous comment – centralising avoids divergence when the design token changes.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fcb2a6 and 7a10ab5.

📒 Files selected for processing (6)
  • blocksuite/affine/data-view/src/core/common/properties.ts (3 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (8 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/condition-view.ts (3 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts (4 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/sort/root-panel.ts (3 hunks)
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • blocksuite/affine/data-view/src/core/common/properties.ts
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/sort/root-panel.ts
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/condition-view.ts (1)
blocksuite/affine/components/src/context-menu/menu-renderer.ts (1)
  • popupTargetFromElement (406-436)

…ableSimpleMenu for better positioning control

fix(add-filter.ts, add-sort.ts, setting.ts): update popMenu calls to use subHandler for consistent menu behavior
style(cell-renderer-css.ts): adjust dateValueContainer height for improved UI consistency
chore(root-panel-view.ts): refactor filter menu to use new popMenu structure and improve readability
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
blocksuite/affine/data-view/src/core/group-by/setting.ts (5)

165-172: Still no guard for -1 indices when reordering

aIndex / oIndex can be -1 when groups mutate during DnD, propagating an invalid id to moveGroupTo.
This was raised previously and remains unfixed.

-const aIndex = groups.findIndex(g => g?.key === activeId);
-const oIndex = groups.findIndex(g => g?.key === over.id);
-this.groupTrait.moveGroupTo(
-  activeId,
-  aIndex > oIndex ? { before: true, id: over.id } : { before: false, id: over.id }
-);
+const aIndex = groups.findIndex(g => g?.key === activeId);
+const oIndex = groups.findIndex(g => g?.key === over.id);
+if (aIndex === -1 || oIndex === -1) return; // stale drag context
+this.groupTrait.moveGroupTo(
+  activeId,
+  aIndex > oIndex ? { before: true, id: over.id } : { before: false, id: over.id }
+);

175-179: Constant fallback key 'k' can still collide

Using the same fallback key for every undefined group repeats the React-style key-collision bug.

Consider the earlier suggestion (UUID or filter-out).


405-409: Redundant type === 'date' guard persists

The whole block is already inside the date type branch. Removing the ternary wrapper simplifies the array spread.


431-435: Nested ternary for week mode hurts readability

Previously flagged; still present.

Extract into a variable:

const weekMode =
  group.groupInfo$.value?.config.name === 'date-week-mon'
    ? 'date-week-mon'
    : 'date-week-sun';
['Week', weekMode]

605-610: columnId prop still not declared in GroupSetting

The element receives .columnId=${gProp.id} but the class defines no @property for it, continuing to raise no-unknown-property warnings.

export class GroupSetting extends SignalWatcher(
  WithDisposable(ShadowlessElement)
) {
+  @property({attribute:false}) accessor columnId?: string;
🧹 Nitpick comments (3)
blocksuite/affine/data-view/src/core/group-by/setting.ts (2)

35-51: Hard-coded English labels limit i18n flexibility

dateModeLabel returns literal English strings. If the rest of the UI is localised, these literals will bypass the translation pipeline.

-const dateModeLabel = (key?: string) => {
+// TODO(i18n): Replace hard-coded literals with translation look-ups.
+const dateModeLabel = (key?: string): string => {

195-202: Batch hide-all operation to avoid N reactive flushes

clickChangeAll loops over every group and calls setGroupHide individually, triggering a signal update per group.
Wrap the updates in a single batch (e.g. untracked / batch utility) or add a bulk helper in GroupTrait to reduce unnecessary re-renders.

blocksuite/affine/data-view/src/core/filter/add-filter.ts (1)

27-68: Avoid hard-coding the same inline style in every popup helper

Directly mutating subHandler.menu.menuElement.style.minHeight to 550px works, but the exact same statement now appears across multiple pop* helpers in this PR.

Consider centralising this concern either:

  1. Extend popMenu to accept a minHeight (and/or maxHeight) option, or
  2. Export a shared constant / utility that applies the sizing once.

This keeps the styling decision in a single place and avoids scattering magic numbers through the codebase.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a10ab5 and 36aafde.

📒 Files selected for processing (6)
  • blocksuite/affine/components/src/context-menu/menu-renderer.ts (7 hunks)
  • blocksuite/affine/data-view/src/core/filter/add-filter.ts (2 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (9 hunks)
  • blocksuite/affine/data-view/src/core/sort/add-sort.ts (3 hunks)
  • blocksuite/affine/data-view/src/property-presets/date/cell-renderer-css.ts (1 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts (5 hunks)
✅ Files skipped from review due to trivial changes (1)
  • blocksuite/affine/data-view/src/property-presets/date/cell-renderer-css.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • blocksuite/affine/data-view/src/core/sort/add-sort.ts
  • blocksuite/affine/components/src/context-menu/menu-renderer.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
blocksuite/affine/data-view/src/core/filter/add-filter.ts (1)
blocksuite/affine/components/src/context-menu/menu-renderer.ts (1)
  • popMenu (522-569)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (1)
blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts (1)

279-281: CSS value fit-content for min/max-height is not universally supported

handler.menu.menuElement.style.minHeight = 'fit-content' (and the same for maxHeight) may be ignored in some browsers; the spec requires the functional notation fit-content(<length-percentage>) for min/max-height.

If true cross-browser behaviour is required, consider:

elem.style.height = 'fit-content';
elem.style.minHeight = 'auto';
elem.style.maxHeight = 'auto';

or providing a concrete pixel / percentage fallback.

NorkzYT added 4 commits June 15, 2025 21:48
… layout options

feat(date-picker): add size property to date-picker for better customization
feat(condition-view): enhance menu behavior by allowing dynamic sizing based on content
…iew render method to clean up code

refactor(condition-view.ts): replace subMenuMiddleware with an array of middleware functions for better customization
refactor(root-panel-view.ts): change popMenu placement to 'bottom-end' and adjust middleware for improved positioning and responsiveness
…ility

feat(data-view): add onClose handler to properties setting for improved user experience
feat(group-by): introduce hideEmpty property to manage visibility of empty groups
fix(group-by): synchronize hideEmpty state with GroupBy data for consistency
style(filter): set minimum width for filter menus to enhance layout stability

fix(view-options.ts): restrict allowed placements in autoPlacement to 'bottom-start' for better UI consistency
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (8)
blocksuite/affine/data-view/src/core/group-by/setting.ts (4)

165-173: Guard against stale drag indices
aIndex/oIndex can be -1 when the groups array mutates mid-drag, triggering an out-of-bounds insert. Add the early-exit proposed in the earlier review to prevent state corruption.


220-224: 'k' fallback key duplicates
Using the constant key 'k' still risks React/DOM subtree reuse bugs when multiple undefined groups exist. Generate a stable unique key (e.g. crypto.randomUUID()) or filter falsy items first.


405-435: Nested ternary + redundant type === 'date' check remain
The readability / redundancy issues flagged in the previous round are still present here.


609-612: columnId attribute still undeclared
GroupSetting doesn’t declare a columnId accessor, so Lit emits no-unknown-property warnings. Either add

@property({attribute:false}) accessor columnId?: string;

or drop the attribute from the template.

blocksuite/affine/data-view/src/core/group-by/trait.ts (4)

163-168: Avoid mutations inside computed

view.propertyGetOrCreate can create a property, violating computed-purity guarantees and triggering extra recomputations.
Switch to view.propertyGet inside the signal and ensure callers create the property beforehand.


205-208: State mutation during reactive compute

Calling cellGetOrCreate here will allocate cells every time the signal re-evaluates. Use cellGet to keep the computation side-effect-free and move creation outside.


355-358: Persist via jsonValueSet, not valueSet

The project convention (and other methods in this file) uses jsonValueSet for JSON payloads. Using valueSet risks inconsistent caching.


512-518: Consistent setter required

Same mismatch here – replace valueSet with jsonValueSet to stay consistent.

🧹 Nitpick comments (1)
blocksuite/affine/data-view/src/core/common/types.ts (1)

5-5: Add brief JSDoc for the new flag

A one-liner like /** hide groups whose row count is zero */ helps downstream maintainers understand the intent without scanning other files.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64aae9c and 288e44a.

📒 Files selected for processing (9)
  • blocksuite/affine/components/src/context-menu/sub-menu.ts (7 hunks)
  • blocksuite/affine/data-view/src/core/common/properties.ts (3 hunks)
  • blocksuite/affine/data-view/src/core/common/types.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/default.ts (1 hunks)
  • blocksuite/affine/data-view/src/core/group-by/setting.ts (9 hunks)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (5 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/condition-view.ts (5 hunks)
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts (5 hunks)
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • blocksuite/affine/data-view/src/core/common/properties.ts
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/condition-view.ts
  • blocksuite/affine/data-view/src/widget-presets/quick-setting-bar/filter/root-panel-view.ts
  • blocksuite/affine/components/src/context-menu/sub-menu.ts
  • blocksuite/affine/data-view/src/widget-presets/tools/presets/view-options/view-options.ts
🔇 Additional comments (1)
blocksuite/affine/data-view/src/core/group-by/default.ts (1)

21-22: Re-evaluate the hard-coded hideEmpty: true default

For existing workspaces this flips behaviour from “show” to “hide” empty groups without an explicit user choice.
Consider either:

  1. Leaving the flag undefined so the UI keeps previous default, or
  2. Reading a user/tenant preference before opting-in.

Otherwise you risk reports of “my empty groups disappeared after upgrade”.

…idden to enhance group visibility management

feat(table-view): implement message display when all groups are hidden to improve user experience
style(table-view): add styles for the groups hidden message to ensure proper layout and visibility
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (5)
blocksuite/affine/data-view/src/core/group-by/trait.ts (5)

139-146: Side-effect loop still possible – add an equality guard

Same concern raised previously: writing to sortAsc$ inside the effect
without checking current value can ping-pong with external updates.


159-168: Computed should stay pure – avoid propertyGetOrCreate inside computed

Creating properties from inside a reactive computation mutates state and may
cause subtle recompute storms. Fetch with propertyGet, and move the creation
logic to the caller.


205-207: Mutation inside reactive graph – replace cellGetOrCreate with cellGet

groupDataMap$ recomputes often; the current implementation can create a new
cell on every run, introducing hidden writes and cascading updates.


523-525: Repeat of value-vs-json setter mismatch

removeFromGroup should align with jsonValueSet as well.

- this.view.cellGetOrCreate(rowId, propId).valueSet(newValue);
+ this.view.cellGetOrCreate(rowId, propId).jsonValueSet(newValue);

360-365: Setter inconsistency (valueSet vs jsonValueSet) – can corrupt caches

Everywhere else JSON payloads are written with jsonValueSet, but
addToGroup uses valueSet, risking stale conversions.

- this.view.cellGetOrCreate(rowId, info.property.id).valueSet(newVal);
+ this.view.cellGetOrCreate(rowId, info.property.id).jsonValueSet(newVal);

Do the same for removeFromGroup.

🧹 Nitpick comments (1)
blocksuite/affine/data-view/src/view-presets/table/pc/table-view-ui-logic.ts (1)

158-162: Redundant filter & missed optional-chain – simplify the groups lookup

groupsDataList$ already omits undefined, so the extra filter is unnecessary.
Removing it also lets you adopt the optional-chain Biome hints pointed out:

-const groups = this.logic.view.groupTrait.groupsDataList$.value?.filter(
-  (g): g is Group => g !== undefined
-);
+const groups = this.logic.view.groupTrait.groupsDataList$.value;

-if (groups && groups.length) {
+if (groups?.length) {

This trims a few iterations and quiets the linter.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 288e44a and 17e25d0.

📒 Files selected for processing (3)
  • blocksuite/affine/data-view/src/core/group-by/trait.ts (5 hunks)
  • blocksuite/affine/data-view/src/view-presets/table/pc/table-view-style.ts (1 hunks)
  • blocksuite/affine/data-view/src/view-presets/table/pc/table-view-ui-logic.ts (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • blocksuite/affine/data-view/src/view-presets/table/pc/table-view-style.ts
🧰 Additional context used
🪛 Biome (1.9.4)
blocksuite/affine/data-view/src/view-presets/table/pc/table-view-ui-logic.ts

[error] 161-161: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines +210 to +214
${this.logic.view.groupTrait.allHidden$.value
? html`<div class="${groupsHiddenMessageStyle}">
All groups are hidden
</div>`
: this.renderTable()}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

“All groups are hidden” message never appears for the hide-empty case

allHidden$ only inspects explicit hide flags, ignoring groups hidden
implicitly by hideEmpty$.
When every group is empty and hideEmpty$ === true, the UI falls back to an
empty group placeholder instead of the informative banner, confusing users.

See the proposed fix for allHidden$ in group-by/trait.ts.

🤖 Prompt for AI Agents
In blocksuite/affine/data-view/src/view-presets/table/pc/table-view-ui-logic.ts
around lines 210 to 214, the condition for showing the "All groups are hidden"
message only checks the explicit hide flags via allHidden$, ignoring groups
hidden due to hideEmpty$. Update the logic to also consider the hideEmpty$ state
when determining if all groups are hidden, ensuring the message appears when all
groups are empty and hideEmpty$ is true, matching the fix suggested in
group-by/trait.ts.

Comment on lines +307 to 309
isGroupHidden(key: string): boolean {
return this.groupPropertiesMap$.value[key]?.hide ?? false;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

isGroupHidden should honour hideEmpty$ for consistency

Right now it mirrors groupPropertiesMap only. Consider:

isGroupHidden(key: string): boolean {
-  return this.groupPropertiesMap$.value[key]?.hide ?? false;
+  const g = this.groupDataMap$.value?.[key];
+  return g ? g.hide$.value : false;
}

Ensures every consumer sees the same visibility semantics.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
isGroupHidden(key: string): boolean {
return this.groupPropertiesMap$.value[key]?.hide ?? false;
}
isGroupHidden(key: string): boolean {
const g = this.groupDataMap$.value?.[key];
return g ? g.hide$.value : false;
}
🤖 Prompt for AI Agents
In blocksuite/affine/data-view/src/core/group-by/trait.ts around lines 307 to
309, the isGroupHidden method currently only checks groupPropertiesMap$ for the
hide property, but it should also consider the hideEmpty$ observable to maintain
consistent visibility semantics. Update the method to incorporate the hideEmpty$
state along with groupPropertiesMap$ so that the returned boolean reflects both
conditions, ensuring all consumers see the same group visibility behavior.

Comment on lines +289 to +293
allHidden$ = computed(() => {
const map = this.groupDataMap$.value;
if (!map) return false;
return Object.keys(map).every(key => this.isGroupHidden(key));
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

allHidden$ ignores hideEmpty$ – banner can mis-report state

The signal only checks explicit hides, so empty groups hidden by the
hideEmpty toggle aren’t counted.

-allHidden$ = computed(() => {
-  const map = this.groupDataMap$.value;
-  if (!map) return false;
-  return Object.keys(map).every(key => this.isGroupHidden(key));
-});
+allHidden$ = computed(() => {
+  const groups = Object.values(this.groupDataMap$.value ?? {});
+  if (!groups.length) return false;
+  return groups.every(g => g?.hide$.value);
+});

This leverages the existing hide$ logic, covering both explicit hides and
empty-group hides.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
allHidden$ = computed(() => {
const map = this.groupDataMap$.value;
if (!map) return false;
return Object.keys(map).every(key => this.isGroupHidden(key));
});
allHidden$ = computed(() => {
const groups = Object.values(this.groupDataMap$.value ?? {});
if (!groups.length) return false;
return groups.every(g => g?.hide$.value);
});
🤖 Prompt for AI Agents
In blocksuite/affine/data-view/src/core/group-by/trait.ts around lines 289 to
293, the allHidden$ computed signal currently only checks explicit group hides
and ignores groups hidden due to the hideEmpty$ toggle, causing incorrect state
reporting. Update the logic to leverage the existing hide$ method or property
that accounts for both explicit hides and empty-group hides, ensuring all hidden
groups are correctly considered in the allHidden$ computation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
app:server test Related to test cases
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

3 participants