Skip to content

Commit 7e70ab9

Browse files
committed
Phase 2: Email List View #7052
1 parent 1679983 commit 7e70ab9

6 files changed

Lines changed: 105 additions & 24 deletions

File tree

apps/email/EPIC_PLAN.md

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,55 @@ This document outlines the plan for refactoring the `apps/email` application int
2525

2626
---
2727

28-
## Phase 2: Email List View
28+
## Phase 2: Email List View (Completed)
2929

3030
**Goal:** Implement the email list pane using a Neo.mjs grid to display a list of emails.
3131

32-
**Potential Tools to Explore:**
32+
**Sub-Tasks:**
3333

34-
- `src/grid/Base.mjs`: For a powerful, feature-rich data grid.
35-
- `src/list/Base.mjs`: For a simpler list view if a full grid is not needed initially.
36-
- `src/data/Store.mjs`: To manage the email data.
37-
- `src/collection/Base.mjs`: For sorting and filtering capabilities within the store.
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`.
44+
45+
**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.
3852

39-
**Sub-Tasks:**
40-
*(To be defined)*
4153

4254
---
4355

44-
## Phase 3: Email Detail View
56+
## Phase 3: Email Detail View (Completed)
4557

46-
**Goal:** Display the content of a selected email.
58+
**Goal:** Display the content of a selected email from the grid.
4759

4860
**Sub-Tasks:**
49-
*(To be defined)*
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+
5077

5178
---
5279

apps/email/neo-config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"environment": "development",
55
"mainPath": "./Main.mjs",
66
"themes": [
7-
"neo-theme-light"
7+
"neo-theme-dark"
88
]
9-
}
9+
}

apps/email/store/Emails.mjs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,17 @@ class Emails extends Store {
1515
/**
1616
* @member {Neo.data.Model} model=Email
1717
*/
18-
model: EmailModel
18+
model: EmailModel,
19+
/**
20+
* @member {Object[]} data
21+
*/
22+
data: [
23+
{id: 1, sender: 'John Doe', title: 'Hello World!', content: 'This is the first email.', dateSent: '2025-07-15T10:00:00Z'},
24+
{id: 2, sender: 'Jane Smith', title: 'Re: Project Update', content: 'Here is the project update you requested.', dateSent: '2025-07-15T11:30:00Z'},
25+
{id: 3, sender: 'Peter Jones', title: 'Lunch tomorrow?', content: 'Are we still on for lunch tomorrow at 12:30?', dateSent: '2025-07-15T12:15:00Z'},
26+
{id: 4, sender: 'Mary Williams', title: 'Your order has shipped', content: 'Your order #12345 has shipped and will arrive in 3-5 business days.', dateSent: '2025-07-14T15:45:00Z'},
27+
{id: 5, sender: 'David Brown', title: 'Quick question', content: 'I have a quick question about the new feature.', dateSent: '2025-07-14T09:20:00Z'}
28+
]
1929
}
2030
}
2131

apps/email/view/MainView.mjs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,64 @@
1-
import {defineComponent} from '../../../src/functional/_export.mjs';
1+
import {defineComponent, useConfig} from '../../../src/functional/_export.mjs';
2+
import GridContainer from '../../../src/grid/Container.mjs';
3+
import EmailsStore from '../store/Emails.mjs';
4+
import RowModel from '../../../src/selection/grid/RowModel.mjs';
25

36
export default defineComponent({
47
config: {
58
className: 'Email.view.MainView',
69
cls : ['email-mainview']
710
},
811
createVdom() {
12+
const [selectedEmail, setSelectedEmail] = useConfig(null);
13+
914
const paneStyle = {
10-
border: '1px solid #c0c0c0',
11-
margin: '10px',
15+
border : '1px solid #c0c0c0',
16+
margin : '10px',
1217
padding: '10px'
1318
};
1419

20+
const onSelectionChange = ({records}) => {
21+
setSelectedEmail(records[0] || null);
22+
};
23+
1524
return {
1625
cn: [{
1726
style: {...paneStyle, flex: '0 0 200px'},
1827
text : 'Folders'
1928
}, {
20-
style: {...paneStyle, flex: '1 1 400px'},
21-
text : 'Email List'
29+
style: {...paneStyle, flex: '1 1 600px', padding: '0'},
30+
cn: [{
31+
module : GridContainer,
32+
id : 'email-grid',
33+
store : EmailsStore,
34+
wrapperStyle: {height: '100%', width: '100%'},
35+
bodyConfig: {
36+
selectionModel: {
37+
module : RowModel,
38+
listeners: {selectionChange: onSelectionChange}
39+
}
40+
},
41+
columns: [
42+
{dataField: 'sender', text: 'Sender', width: 150},
43+
{dataField: 'title', text: 'Title', flex: 1, minWidth: 200},
44+
{
45+
cellAlign: 'right',
46+
dataField: 'dateSent',
47+
text : 'Date',
48+
width : 100,
49+
renderer : (({value}) => new Date(value).toLocaleDateString())
50+
}
51+
]
52+
}]
2253
}, {
2354
style: {...paneStyle, flex: '1 1 600px'},
24-
text : 'Email Details'
55+
cn: selectedEmail ? [
56+
{tag: 'h2', text: selectedEmail.title},
57+
{tag: 'p', text: `From: ${selectedEmail.sender}`},
58+
{tag: 'div', style: {marginTop: '10px'}, text: selectedEmail.content}
59+
] : [{
60+
text: 'Select an email to read'
61+
}]
2562
}]
2663
}
2764
}

src/functional/component/Base.mjs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,20 @@ class FunctionalBase extends Base {
178178
* @protected
179179
*/
180180
afterSetWindowId(value, oldValue) {
181+
const me = this;
182+
181183
if (value) {
182-
Neo.currentWorker.insertThemeFiles(value, this.__proto__)
184+
Neo.currentWorker.insertThemeFiles(value, me.__proto__)
183185
}
184186

187+
me.childComponents?.forEach(component => {
188+
component.windowId = value
189+
})
190+
185191
// If a component gets moved into a different window, an update cycle might still be running.
186192
// Since the update might no longer get mapped, we want to re-enable this instance for future updates.
187193
if (oldValue) {
188-
this.isVdomUpdating = false
194+
me.isVdomUpdating = false
189195
}
190196
}
191197

@@ -353,12 +359,12 @@ class FunctionalBase extends Base {
353359
if (!component) {
354360
me.childComponents ??= new Map();
355361

356-
357362
// Instantiate the component
358363
component = Neo[(vdomTree.className || vdomTree.module) ? 'create' : 'ntype']({
359364
...vdomTree,
360365
parentId,
361-
parentIndex
366+
parentIndex,
367+
windowId: me.windowId
362368
})
363369
} else {
364370
const newConfig = {...vdomTree}; // Shallow copy

src/selection/grid/BaseModel.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class BaseModel extends Model {
5353
}
5454

5555
me.fire('selectionChange', {
56+
records : me.selectedRows.map(id => view.store.get(id)),
5657
selection: me.selectedRows
5758
})
5859
} else if (!silent) {

0 commit comments

Comments
 (0)