|
2 | 2 |
|
3 | 3 | This document outlines the plan for refactoring the `apps/email` application into a showcase for the Neo.mjs functional component architecture and multi-window capabilities. |
4 | 4 |
|
5 | | -## Phase 1: Foundation and Basic Layout (Completed) |
| 5 | +## Architectural Milestones & Key Learnings |
6 | 6 |
|
7 | | -**Goal:** Implement the main 3-pane layout as a functional `MainView` component and set it as the content of the existing classic `Viewport`. |
| 7 | +This epic has driven significant architectural improvements to the functional component system. For a full understanding of the current state, review these tickets: |
8 | 8 |
|
9 | | -**Sub-Tasks:** |
| 9 | +- **[#ticket-functional-child-component-diffing.md](/.github/ticket-functional-child-component-diffing.md):** The most critical enhancement. We implemented a VDOM config diffing mechanism in `FunctionalBase` to prevent the re-creation of stateful child components (like stores). This allows for a truly declarative VDOM while preserving child state. |
| 10 | +- **[#ticket-functional-base-windowid-propagation.md](/.github/ticket-functional-base-windowid-propagation.md):** Fixed a crucial bug where the `windowId` was not being propagated to child components, which is essential for multi-window support and event handling. |
| 11 | +- **[#ticket-functional-recursive-config-diffing.md](/.github/ticket-functional-recursive-config-diffing.md):** A plan for a future enhancement to make the diffing logic recursive, allowing for deep, declarative control over nested component configurations. |
10 | 12 |
|
11 | | -1. **Create `view/MainView.mjs`:** |
12 | | - - Used `defineComponent` to create a new functional component. |
13 | | - - Implemented a flexbox-based 3-pane layout (Folders, Email List, Email Detail). |
14 | | -2. **Update `view/Viewport.mjs`:** |
15 | | - - Configured the `Viewport` with `layout: 'fit'`. |
16 | | - - Replaced the old `TabContainer` with the new `MainView` component as its single item. |
17 | | -3. **Cleanup:** |
18 | | - - Removed the `stateProvider` from the `Viewport` as it is not currently needed. |
| 13 | +--- |
19 | 14 |
|
20 | | -**Learnings & Decisions:** |
| 15 | +## Phase 1: Foundation and Basic Layout (Completed) |
21 | 16 |
|
22 | | -- **Interoperability:** Decided to keep the classic `Viewport` and embed the new functional `MainView` component within it. This is a pragmatic approach that leverages existing structures and showcases the interoperability between classic and functional components. |
23 | | -- **Naming:** Renamed the main functional component from `Main.mjs` to `MainView.mjs` for better clarity and to align with the `mainView` config in `app.mjs`. |
| 17 | +**Goal:** Implement the main 3-pane layout as a functional `MainView` component and set it as the content of the existing classic `Viewport`. |
24 | 18 |
|
| 19 | +**Learnings & Decisions:** |
| 20 | +- **Interoperability:** Kept the classic `Viewport` and embedded the new functional `MainView` component, showcasing seamless integration. |
| 21 | +- **Naming:** Renamed `Main.mjs` to `MainView.mjs` for clarity. |
25 | 22 |
|
26 | 23 | --- |
27 | 24 |
|
28 | 25 | ## Phase 2: Email List View (Completed) |
29 | 26 |
|
30 | | -**Goal:** Implement the email list pane using a Neo.mjs grid to display a list of emails. |
31 | | - |
32 | | -**Sub-Tasks:** |
33 | | - |
34 | | -1. **Create Mock Data:** |
35 | | - - Populated the `apps/email/store/Emails.mjs` with hardcoded sample email data. |
36 | | -2. **Integrate Grid:** |
37 | | - - Replaced the "Email List" placeholder in `MainView.mjs` with a `Neo.grid.Container`. |
38 | | - - Configured the grid to use the `Emails` store and defined the columns. |
39 | | -3. **Styling & Layout:** |
40 | | - - Wrapped the grid in a styled `div` to ensure correct flexbox layout. |
41 | | - - Used the `wrapperStyle` config on the grid to control its internal dimensions, which is necessary for the grid's layout engine. |
42 | | -4. **Enable Interoperability:** |
43 | | - - Enhanced `functional.component.Base` to propagate the parent's `windowId` to all child components. This was a critical fix to ensure the classic grid component could function correctly when rendered inside our functional `MainView`. |
| 27 | +**Goal:** Implement the email list pane using a `Neo.grid.Container`. |
44 | 28 |
|
45 | 29 | **Learnings & Decisions:** |
46 | | - |
47 | | -- **Complex Component Integration:** Integrating a complex classic component like `grid.Container` into a functional component requires more than just placing it in the VDOM. We must provide layout-critical styles (like `height` and `width`) via the component's specific config (`wrapperStyle`) for it to render correctly. |
48 | | -- **`windowId` is Crucial:** The `windowId` must be manually propagated from functional parents to classic children. This is a fundamental requirement for interoperability and ensuring that events, theming, and other window-specific functionalities work correctly. This led to enhancing `functional.component.Base` and creating a dedicated ticket for it. |
49 | | - |
50 | | -**Next Steps:** |
51 | | -- Implement selection handling on the grid to prepare for the detail view. |
52 | | - |
| 30 | +- **Complex Component Integration:** Discovered that complex classic components require specific configs (e.g., `wrapperStyle`) to manage their own layout when nested inside functional components. |
| 31 | +- **Stateful Child Problem:** Uncovered the core issue of stateful children (stores, columns) being re-created on every parent render. This led directly to the architectural work on VDOM diffing. |
53 | 32 |
|
54 | 33 | --- |
55 | 34 |
|
56 | 35 | ## Phase 3: Email Detail View (Completed) |
57 | 36 |
|
58 | 37 | **Goal:** Display the content of a selected email from the grid. |
59 | | - |
60 | | -**Sub-Tasks:** |
61 | | - |
62 | | -1. **Grid Selection:** |
63 | | - - Configured a `selection.RowModel` on the grid's `bodyConfig`. |
64 | | - - Set `singleSelect: true` to allow only one row to be selected. |
65 | | -2. **State Management:** |
66 | | - - Used the `useConfig()` hook in `MainView` to create a `selectedEmail` state variable. |
67 | | -3. **Event Handling:** |
68 | | - - Added a `selectionChange` listener to the selection model. |
69 | | - - The listener updates the `selectedEmail` state with the selected record. |
70 | | -4. **Detail View:** |
71 | | - - The "Email Details" pane now conditionally renders the `title`, `sender`, and `content` of the `selectedEmail`. |
72 | | - - If no email is selected, it displays a placeholder message. |
73 | | - |
74 | | -**Next Steps:** |
75 | | -- Implement "Compose" functionality. |
76 | | - |
| 38 | +- Implemented a `selection.RowModel` on the grid and used a `useConfig` state variable (`selectedEmail`) to drive a conditional render of the detail pane. |
77 | 39 |
|
78 | 40 | --- |
79 | 41 |
|
80 | | -## Phase 4: Compose Email Functionality |
| 42 | +## Phase 4: Compose Email Functionality (Next) |
81 | 43 |
|
82 | 44 | **Goal:** Implement the ability to compose a new email, initially within the main window. |
83 | 45 |
|
84 | | -**Potential Tools to Explore:** |
85 | | - |
86 | | -- `src/dialog/Base.mjs`: To create a modal or non-modal dialog for the compose window. |
87 | | -- Custom Functional Overlay: Build a new overlay component from scratch using functional components. |
88 | | - |
89 | | -**Sub-Tasks:** |
90 | | -*(To be defined)* |
| 46 | +**Proposed Plan:** |
| 47 | +1. **Compose Button:** Add a `Neo.button.Base` to the "Folders" pane. |
| 48 | +2. **State Management:** Use `useConfig` in `MainView` to manage an `isComposing` boolean state. |
| 49 | +3. **Compose View:** Create a new functional component, `view/ComposeView.mjs`, containing the form for the new email. |
| 50 | +4. **Conditional Rendering:** Use the `isComposing` flag to conditionally render the `ComposeView` as an overlay on top of the main view. |
| 51 | +5. **Event Handling:** The "Compose" button will set `isComposing` to `true`. The `ComposeView` will have a "Close" button that sets it back to `false`. |
| 52 | +6. **Overlay Implementation:** We will build a custom functional overlay for this, as it's a good exercise and provides maximum flexibility. |
91 | 53 |
|
92 | 54 | --- |
93 | 55 |
|
94 | 56 | ## Phase 5: Multi-Window Detach |
95 | 57 |
|
96 | 58 | **Goal:** Implement the "detach" functionality to move the compose view into a separate browser window. |
| 59 | +**Sub-Tasks:** *(To be defined)* |
97 | 60 |
|
98 | | -**Sub-Tasks:** |
99 | | -*(To be defined)* |
0 commit comments