feat(editor): add date grouping configurations (#12679)

https://github.com/user-attachments/assets/d5578060-2c8c-47a5-ba65-ef2e9430518b

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


![image](https://github.com/user-attachments/assets/8762342a-999e-444e-afa2-5cfbf7e24907)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Date-based grouping modes (relative, day, week Sun/Mon, month, year),
a date group renderer, and quick lookup for group-by configs by name.

* **Improvements**
* Enhanced group settings: date sub‑modes, week‑start, per‑group
visibility, Hide All/Show All, date sort order, improved drag/drop and
reorder.
* Consistent popup placement/middleware, nested popup positioning,
per‑item close-on-select, and enforced minimum menu heights.
* UI: empty groups now display "No <property>"; views defensively handle
null/hidden groups.

* **Tests**
  * Added unit tests for date-key sorting and comparison.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Norkz <richardlora557@gmail.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
This commit is contained in:
Richard Lora
2025-12-11 18:32:21 -04:00
committed by GitHub
parent b258fc3775
commit f832b28dac
42 changed files with 1642 additions and 575 deletions
@@ -15,6 +15,7 @@ import {
computePosition,
type Middleware,
offset,
type Placement,
type ReferenceElement,
shift,
} from '@floating-ui/dom';
@@ -37,7 +38,9 @@ export class MenuComponent
display: flex;
flex-direction: column;
user-select: none;
min-width: 180px;
min-width: 320px;
max-width: 320px;
max-height: 700px;
box-shadow: ${unsafeCSSVar('overlayPanelShadow')};
border-radius: 4px;
background-color: ${unsafeCSSVarV2('layer/background/overlayPanel')};
@@ -439,6 +442,7 @@ export const createPopup = (
onClose?: () => void;
middleware?: Array<Middleware | null | undefined | false>;
container?: HTMLElement;
placement?: Placement;
}
) => {
const close = () => {
@@ -448,6 +452,7 @@ export const createPopup = (
const modal = createModal(target.root);
autoUpdate(target.targetRect, content, () => {
computePosition(target.targetRect, content, {
placement: options?.placement,
middleware: options?.middleware ?? [shift({ crossAxis: true })],
})
.then(({ x, y }) => {
@@ -520,6 +525,7 @@ export const popMenu = (
options: MenuOptions;
middleware?: Array<Middleware | null | undefined | false>;
container?: HTMLElement;
placement?: Placement;
}
): MenuHandler => {
if (IS_MOBILE) {
@@ -551,6 +557,7 @@ export const popMenu = (
offset(4),
],
container: props.container,
placement: props.placement,
});
return {
close: closePopup,
@@ -563,12 +570,14 @@ export const popMenu = (
export const popFilterableSimpleMenu = (
target: PopupTarget,
options: MenuConfig[],
onClose?: () => void
onClose?: () => void,
placement: Placement = 'bottom-start'
) => {
popMenu(target, {
options: {
items: options,
onClose,
},
placement,
});
};