Skip to content

Commit d0a18c6

Browse files
committed
feat: polish splitview demo
1 parent 08303ae commit d0a18c6

11 files changed

+321
-54
lines changed

apps/toolbox/src/pages/list-page-model-sticky.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Observable, Dialogs, DialogStrings, View, EventData, SearchEventData } from '@nativescript/core';
2-
import { getItemCallbacks } from '../split-view/split-view-root';
32
type CountryListType = Array<{ title: string; items: Array<{ name: string; code: string; flag: string; isVisible?: boolean }> }>;
43
export class ListPageModelSticky extends Observable {
54
countries: CountryListType = [
@@ -1386,8 +1385,6 @@ export class ListPageModelSticky extends Observable {
13861385
if (letter.items?.length) {
13871386
const country = letter.items[args.index];
13881387
console.log('Tapped on country: ' + country.name);
1389-
// used in splitview demo
1390-
getItemCallbacks().forEach((callback) => callback(`${country.name} was selected.`));
13911388
}
13921389
}
13931390

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Observable, EventData, Page } from '@nativescript/core';
2+
import { SplitViewBrief, setItemCallbacks } from './split-view-root';
3+
4+
let page: Page;
5+
6+
export function navigatingTo(args: EventData) {
7+
page = <Page>args.object;
8+
page.bindingContext = new SplitViewInspectorModel();
9+
}
10+
11+
export class SplitViewInspectorModel extends Observable {
12+
selectedBrief: SplitViewBrief | null = null;
13+
14+
constructor() {
15+
super();
16+
setItemCallbacks([this.syncBrief.bind(this)]);
17+
}
18+
19+
syncBrief(brief: SplitViewBrief) {
20+
this.selectedBrief = brief;
21+
this.notifyPropertyChange('selectedBrief', brief);
22+
}
23+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
2+
<ActionBar title="Inspector" class="action-bar" iosLargeTitle="true" />
3+
4+
<ScrollView>
5+
<StackLayout padding="24" backgroundColor="#f2f2f7">
6+
<Label text="Inspector" textTransform="uppercase" fontSize="12" color="#636366" marginBottom="8" />
7+
<Label text="{{ selectedBrief ? selectedBrief.title : 'Choose a brief' }}" fontSize="20" fontWeight="700" textWrap="true" marginBottom="8" />
8+
<Label text="{{ selectedBrief ? selectedBrief.tagline : '' }}" textWrap="true" color="#6e6e73" marginBottom="18" />
9+
10+
<Label text="Project Stats" fontSize="15" fontWeight="600" marginBottom="8" />
11+
<Repeater items="{{ selectedBrief ? selectedBrief.metrics : [] }}">
12+
<Repeater.itemsLayout>
13+
<StackLayout />
14+
</Repeater.itemsLayout>
15+
<Repeater.itemTemplate>
16+
<StackLayout padding="14" marginBottom="10" borderRadius="16" backgroundColor="#ffffff">
17+
<Label text="{{ label }}" fontSize="12" color="#636366" textTransform="uppercase" />
18+
<Label text="{{ value }}" fontSize="18" fontWeight="600" />
19+
</StackLayout>
20+
</Repeater.itemTemplate>
21+
</Repeater>
22+
23+
<Label text="Contributors" fontSize="15" fontWeight="600" marginTop="12" marginBottom="8" />
24+
<Repeater items="{{ selectedBrief ? selectedBrief.contributors : [] }}">
25+
<Repeater.itemsLayout>
26+
<FlexboxLayout flexWrap="wrap" />
27+
</Repeater.itemsLayout>
28+
<Repeater.itemTemplate>
29+
<Label text="{{ $value }}" padding="6 14" margin="6 6 0 0" borderRadius="18" backgroundColor="#ffffff" />
30+
</Repeater.itemTemplate>
31+
</Repeater>
32+
33+
<Label text="Tags" fontSize="15" fontWeight="600" marginTop="12" marginBottom="8" />
34+
<Repeater items="{{ selectedBrief ? selectedBrief.tags : [] }}">
35+
<Repeater.itemsLayout>
36+
<FlexboxLayout flexWrap="wrap" />
37+
</Repeater.itemsLayout>
38+
<Repeater.itemTemplate>
39+
<Label text="{{ $value }}" padding="4 12" margin="6 6 0 0" borderRadius="14" backgroundColor="#e5e5ea" color="#1c1c1e" />
40+
</Repeater.itemTemplate>
41+
</Repeater>
42+
</StackLayout>
43+
</ScrollView>
44+
45+
</Page>
Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,127 @@
11
import { Observable, EventData, Page, SplitView, ItemEventData } from '@nativescript/core';
2-
import { getItemCallbacks } from './split-view-root';
2+
import { SplitViewBrief, broadcastSelection } from './split-view-root';
3+
34
let page: Page;
45

6+
const briefData: SplitViewBrief[] = [
7+
{
8+
id: 'brief-morning',
9+
title: 'Morning Briefing',
10+
category: 'Operations',
11+
status: 'Prep for leadership sync',
12+
summary: 'Snapshot of overnight metrics and open questions for the ops pod.',
13+
owner: 'Taylor Brooks',
14+
updated: 'Updated 9:41 AM',
15+
accent: '#0A84FF',
16+
tagline: 'Send digest to the exec channel by 11:00 AM.',
17+
notes: 'Pull final revenue numbers, plug in the refreshed engagement chart, and make sure automation stays enabled before the noon lock.',
18+
actions: ['Drop the updated charts into the keynote deck.', 'Verify Siri Shortcut triggers run on the new data set.', 'Line up quick talking points for the 11:00 AM call.'],
19+
highlights: [
20+
{ label: 'Focus', value: 'Engagement + device health' },
21+
{ label: 'Next Check-in', value: 'Ops sync at 2:30 PM' },
22+
],
23+
metrics: [
24+
{ label: 'Priority', value: 'High' },
25+
{ label: 'Delivery', value: 'Exec brief' },
26+
{ label: 'Attachments', value: '4 files' },
27+
{ label: 'Watchers', value: '6 people' },
28+
],
29+
contributors: ['Taylor', 'Morgan', 'Priya'],
30+
tags: ['Ops', 'Daily', 'Exec'],
31+
},
32+
{
33+
id: 'brief-design',
34+
title: 'Split View polish',
35+
category: 'Design',
36+
status: 'Assets ready for QA',
37+
summary: 'Interaction audit for the compact-to-regular transition on iPadOS.',
38+
owner: 'Isla Raymond',
39+
updated: 'Updated 8:12 AM',
40+
accent: '#FF9F0A',
41+
tagline: 'Need sign-off from Brandon before handoff.',
42+
notes: 'Finalize the inspector column metrics cards and verify the peek gesture targets. Attach the quick recording from TestFlight build 4120.',
43+
actions: ['Export motion specs for the spring animation.', 'Confirm hit targets with accessibility review.', 'Share the color tokens with web once approved.'],
44+
highlights: [
45+
{ label: 'Focus', value: 'Regular width interactions' },
46+
{ label: 'Blocked by', value: 'Motion review @ 3:00 PM' },
47+
],
48+
metrics: [
49+
{ label: 'Priority', value: 'Medium' },
50+
{ label: 'Delivery', value: 'Design QA' },
51+
{ label: 'Attachments', value: '6 files' },
52+
{ label: 'Watchers', value: '4 people' },
53+
],
54+
contributors: ['Isla', 'Brandon', 'Nova'],
55+
tags: ['Design', 'QA'],
56+
},
57+
{
58+
id: 'brief-field',
59+
title: 'Field research recap',
60+
category: 'Research',
61+
status: 'Synthesize recordings',
62+
summary: 'Notes from three in-store sessions about the new onboarding.',
63+
owner: 'Sam Park',
64+
updated: 'Updated yesterday',
65+
accent: '#32D74B',
66+
tagline: 'Clips already in the shared Photos album.',
67+
notes: 'Pull direct quotes for friction points around quick actions, and pair with the heat map captures for the appendix slide.',
68+
actions: ['Flag standout clips for the highlight reel.', 'Post the transcript summary in #research.', 'Open follow-up tasks for the onboarding squad.'],
69+
highlights: [
70+
{ label: 'Focus', value: 'Onboarding swipes' },
71+
{ label: 'Next Check-in', value: 'Squad sync Friday' },
72+
],
73+
metrics: [
74+
{ label: 'Priority', value: 'Medium' },
75+
{ label: 'Delivery', value: 'Research recap' },
76+
{ label: 'Attachments', value: '9 clips' },
77+
{ label: 'Watchers', value: '3 people' },
78+
],
79+
contributors: ['Sam', 'Kaia'],
80+
tags: ['Research', 'Clips', 'Onboarding'],
81+
},
82+
{
83+
id: 'brief-gallery',
84+
title: 'Shortcuts gallery update',
85+
category: 'Product',
86+
status: 'Ready to publish',
87+
summary: 'Curated cards for the iPad productivity spotlight next week.',
88+
owner: 'Luca Nguyen',
89+
updated: 'Updated Monday',
90+
accent: '#BF5AF2',
91+
tagline: 'Publishing window opens Thursday morning.',
92+
notes: 'Verify icon treatments render crisply on mini + 12.9 screens, and double-check localized copy for DE/JA.',
93+
actions: ['Ping localization for the remaining strings.', 'Preview the curated set on device.', 'Coordinate push copy with marketing.'],
94+
highlights: [
95+
{ label: 'Focus', value: 'Automation + Productivity' },
96+
{ label: 'Blocked by', value: 'Awaiting DE copy' },
97+
],
98+
metrics: [
99+
{ label: 'Priority', value: 'High' },
100+
{ label: 'Delivery', value: 'App Store' },
101+
{ label: 'Attachments', value: '12 shortcuts' },
102+
{ label: 'Watchers', value: '5 people' },
103+
],
104+
contributors: ['Luca', 'Amelia', 'Jo'],
105+
tags: ['Launch', 'Localization'],
106+
},
107+
];
108+
5109
export function navigatingTo(args: EventData) {
6110
page = <Page>args.object;
7111
page.bindingContext = new SplitViewPrimaryModel();
8112
}
9113

10114
export class SplitViewPrimaryModel extends Observable {
11-
items: string[] = [];
115+
briefs: SplitViewBrief[] = briefData;
12116

13117
constructor() {
14118
super();
15-
this.items = Array.from({ length: 20 }, (_, i) => `Item ${i + 1}`);
119+
broadcastSelection(this.briefs[0]);
16120
}
17121

18122
onItemTap(args: ItemEventData) {
19-
console.log('args.index', args.index);
123+
const selectedBrief = this.briefs[args.index];
20124
SplitView.getInstance()?.showSecondary();
21-
getItemCallbacks().forEach((callback) => callback(this.items[args.index]));
125+
broadcastSelection(selectedBrief);
22126
}
23127
}
Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
2-
<ActionBar title="Primary View" class="action-bar" iosLargeTitle="true"></ActionBar>
2+
<ActionBar title="Briefs" class="action-bar" iosLargeTitle="true" />
33

4-
<!-- Primary column (master) -->
5-
<GridLayout rows="auto,*">
6-
<!-- <Label text="Primary" marginBottom="12" marginLeft="8" fontSize="22" fontWeight="bold" /> -->
7-
<ListView row="1" items="{{ items }}" itemTap="{{ onItemTap }}" backgroundColor="white">
4+
<GridLayout rows="auto,*" backgroundColor="#f2f2f7">
5+
<StackLayout row="0" padding="24 20 4">
6+
<Label text="Workspaces" textTransform="uppercase" fontSize="12" color="#636366" />
7+
<Label text="Choose a brief to preview" fontSize="24" fontWeight="bold" marginTop="4" />
8+
<Label text="Primary column drives the rest of the split view." color="#8e8e93" />
9+
</StackLayout>
10+
11+
<ListView row="1" items="{{ briefs }}" itemTap="{{ onItemTap }}" separatorColor="#00ffffff" backgroundColor="#f2f2f7">
812
<ListView.itemTemplate>
9-
<GridLayout padding="12">
10-
<Label text="{{ $value }}" fontSize="18" />
13+
<GridLayout rows="auto,auto,auto" columns="auto,*" padding="16" margin="0 16 12" borderRadius="18" backgroundColor="white" rowSpacing="6">
14+
<StackLayout col="0" rowSpan="3" width="5" borderRadius="3" backgroundColor="{{ accent }}" />
15+
16+
<GridLayout col="1" row="0" columns="*,auto">
17+
<Label col="0" text="{{ title }}" fontSize="18" fontWeight="600" />
18+
<Label col="1" text="{{ status }}" fontSize="12" color="#6e6e73" padding="2 8" borderRadius="10" backgroundColor="#f2f2f7" marginLeft="12" />
19+
</GridLayout>
20+
21+
<Label col="1" row="1" text="{{ summary }}" textWrap="true" color="#6e6e73" />
22+
23+
<GridLayout col="1" row="2" columns="auto,*" verticalAlignment="center">
24+
<Label col="0" text="{{ category }}" fontSize="13" color="#0a84ff" />
25+
<Label col="1" text="{{ updated }}" fontSize="12" color="#8e8e93" marginLeft="12" />
26+
</GridLayout>
1127
</GridLayout>
1228
</ListView.itemTemplate>
1329
</ListView>
1430
</GridLayout>
1531

16-
</Page>
32+
</Page>
Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
import { Observable, EventData, Page } from '@nativescript/core';
2+
3+
export interface SplitViewBrief {
4+
id: string;
5+
title: string;
6+
category: string;
7+
status: string;
8+
summary: string;
9+
owner: string;
10+
updated: string;
11+
accent: string;
12+
tagline: string;
13+
notes: string;
14+
actions: string[];
15+
highlights: Array<{ label: string; value: string }>;
16+
metrics: Array<{ label: string; value: string }>;
17+
contributors: string[];
18+
tags: string[];
19+
}
20+
221
let page: Page;
322

423
export function navigatingTo(args: EventData) {
@@ -8,10 +27,23 @@ export function navigatingTo(args: EventData) {
827

928
export class SplitViewModel extends Observable {}
1029

11-
let itemCallbacks: Array<(item: any) => void> = [];
12-
export function setItemCallbacks(changeItem: Array<(item: any) => void>) {
13-
itemCallbacks.push(...changeItem);
30+
type SelectionCallback = (item: SplitViewBrief) => void;
31+
32+
let itemCallbacks: SelectionCallback[] = [];
33+
let hasSelection = false;
34+
let latestSelection: SplitViewBrief;
35+
36+
export function setItemCallbacks(changeItem: SelectionCallback[]) {
37+
changeItem.forEach((callback) => {
38+
itemCallbacks.push(callback);
39+
if (hasSelection) {
40+
callback(latestSelection);
41+
}
42+
});
1443
}
15-
export function getItemCallbacks() {
16-
return itemCallbacks;
44+
45+
export function broadcastSelection(item: SplitViewBrief) {
46+
latestSelection = item;
47+
hasSelection = true;
48+
itemCallbacks.forEach((callback) => callback(item));
1749
}

apps/toolbox/src/split-view/split-view-root.xml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
<SplitView xmlns="http://schemas.nativescript.org/tns.xsd" displayMode="twoBesideSecondary" splitBehavior="tile" preferredPrimaryColumnWidthFraction="0.25" preferredSupplementaryColumnWidthFraction="0.33"
2-
preferredInspectorColumnWidthFraction="0.20">
1+
<SplitView xmlns="http://schemas.nativescript.org/tns.xsd" displayMode="allVisible" splitBehavior="tile"
2+
preferredPrimaryColumnWidthFraction="0.26" preferredSupplementaryColumnWidthFraction="0.28"
3+
preferredInspectorColumnWidthFraction="0.18">
34
<Frame splitRole="primary" defaultPage="split-view/split-view-primary">
45

56
</Frame>
@@ -12,8 +13,8 @@ preferredInspectorColumnWidthFraction="0.20">
1213

1314
</Frame>
1415

15-
<Frame splitRole="inspector" defaultPage="pages/list-page-sticky">
16+
<Frame splitRole="inspector" defaultPage="split-view/split-view-inspector">
1617

1718
</Frame>
1819

19-
</SplitView>
20+
</SplitView>

apps/toolbox/src/split-view/split-view-secondary.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Observable, EventData, Page, SplitView } from '@nativescript/core';
2-
import { setItemCallbacks } from './split-view-root';
2+
import { SplitViewBrief, setItemCallbacks } from './split-view-root';
33
let page: Page;
44

55
export function navigatingTo(args: EventData) {
@@ -8,7 +8,7 @@ export function navigatingTo(args: EventData) {
88
}
99

1010
export class SplitViewSecondaryModel extends Observable {
11-
selectedItem = `Select an item from Primary.`;
11+
selectedBrief: SplitViewBrief | null = null;
1212
showInspectorButton = false;
1313

1414
constructor() {
@@ -28,8 +28,8 @@ export class SplitViewSecondaryModel extends Observable {
2828
SplitView.getInstance()?.showInspector();
2929
}
3030

31-
changeItem(item: any) {
32-
this.selectedItem = item;
33-
this.notifyPropertyChange('selectedItem', item);
31+
changeItem(item: SplitViewBrief) {
32+
this.selectedBrief = item;
33+
this.notifyPropertyChange('selectedBrief', item);
3434
}
3535
}

0 commit comments

Comments
 (0)