Skip to content

Commit 4ed762b

Browse files
joserodolfofreitasnoraleonterita-codes
authored
[scheduler] Split scheduler overview (mui#22453)
Signed-off-by: José Rodolfo Freitas <joserodolfo.freitas@gmail.com> Co-authored-by: noraleonte <noraleonte00@gmail.com> Co-authored-by: Rita <47579513+rita-codes@users.noreply.github.com>
1 parent 342766c commit 4ed762b

9 files changed

Lines changed: 372 additions & 201 deletions

File tree

docs/data/scheduler/overview/overview.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ githubLabel: 'scope: scheduler'
1515

1616
{{"component": "@mui/internal-core-docs/ComponentLinkHeader", "design": false}}
1717

18-
{{"component": "modules/components/overview/scheduler/mainDemo/MainDemo.tsx"}}
18+
## Event Calendar
19+
20+
Start with the MIT-licensed Event Calendar for everyday scheduling, then unlock Premium features when your calendar workflows need recurrence or server-backed loading.
21+
22+
{{"component": "modules/components/overview/scheduler/mainDemo/CalendarDemo.tsx"}}
23+
24+
## Event Timeline [<span class="plan-premium"></span>](/x/introduction/licensing/#premium-plan 'Premium plan')
25+
26+
Use the Premium Event Timeline to plan long-running work across teams, resources, and date ranges.
27+
28+
{{"component": "modules/components/overview/scheduler/mainDemo/TimelineDemo.tsx"}}
29+
1930
{{"component": "modules/components/overview/scheduler/SchedulerFeaturesHighlight.tsx"}}
2031
{{"component": "modules/components/overview/scheduler/SchedulerCommunityOrPremium.tsx"}}

docs/pages/x/react-scheduler/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import { MarkdownDocs } from '@mui/internal-core-docs/MarkdownDocs';
22
import * as pageProps from 'docs/data/scheduler/overview/overview.md?muiMarkdown';
33

44
export default function Page() {
5-
return <MarkdownDocs {...pageProps} disableAd wideLayout />;
5+
return <MarkdownDocs {...pageProps} disableAd wideLayout disableToc />;
66
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import * as React from 'react';
2+
import { alpha } from '@mui/material/styles';
3+
import Box from '@mui/material/Box';
4+
import Paper from '@mui/material/Paper';
5+
import Stack from '@mui/material/Stack';
6+
import Typography from '@mui/material/Typography';
7+
import { grey, blue } from '@mui/material/colors';
8+
9+
type Plan = 'community' | 'premium';
10+
11+
type LicenseCardProps = {
12+
plan: Plan;
13+
title: string;
14+
description: string;
15+
};
16+
17+
const PLAN_CONFIG: Record<
18+
Plan,
19+
{ iconSrc: string; packageName: string; palette: typeof grey | typeof blue }
20+
> = {
21+
community: {
22+
iconSrc: '/static/x/community.svg',
23+
packageName: '@mui/x-scheduler',
24+
palette: grey,
25+
},
26+
premium: {
27+
iconSrc: '/static/x/premium.svg',
28+
packageName: '@mui/x-scheduler-premium',
29+
palette: blue,
30+
},
31+
};
32+
33+
export default function LicenseCard({ plan, title, description }: LicenseCardProps) {
34+
const { iconSrc, packageName, palette } = PLAN_CONFIG[plan];
35+
36+
return (
37+
<Paper
38+
variant="outlined"
39+
sx={(theme) => ({
40+
p: 3,
41+
display: 'flex',
42+
flexDirection: 'column',
43+
gap: 2,
44+
flexGrow: 1,
45+
borderColor: alpha(palette[500], 0.2),
46+
backgroundColor: alpha(palette[100], 0.2),
47+
...theme.applyDarkStyles({
48+
borderColor: alpha(palette[700], 0.2),
49+
backgroundColor: alpha(palette[900], 0.05),
50+
}),
51+
})}
52+
>
53+
<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'space-between', gap: 2 }}>
54+
<Stack direction="row" sx={{ alignItems: 'center', gap: 1.5 }}>
55+
<Box component="img" src={iconSrc} width={16} height={16} alt="" />
56+
<Typography
57+
component="h3"
58+
variant="body1"
59+
sx={(theme) => ({
60+
fontWeight: 'semiBold',
61+
color: palette[900],
62+
...theme.applyDarkStyles({ color: palette[300] }),
63+
})}
64+
>
65+
{title}
66+
</Typography>
67+
</Stack>
68+
<Box
69+
sx={(theme) => ({
70+
px: 1.25,
71+
py: 0.25,
72+
borderRadius: 999,
73+
border: '1px solid',
74+
borderColor: palette[200],
75+
fontFamily: theme.typography.fontFamilyCode,
76+
fontSize: theme.typography.pxToRem(12),
77+
color: palette[800],
78+
...theme.applyDarkStyles({
79+
borderColor: alpha(palette[800], 0.5),
80+
color: palette[300],
81+
}),
82+
})}
83+
>
84+
{packageName}
85+
</Box>
86+
</Stack>
87+
<Typography
88+
variant="body2"
89+
sx={(theme) => ({
90+
color: palette[800],
91+
...theme.applyDarkStyles({ color: palette[200] }),
92+
})}
93+
>
94+
{description}
95+
</Typography>
96+
</Paper>
97+
);
98+
}

docs/src/modules/components/overview/scheduler/SchedulerCommunityOrPremium.tsx

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,65 @@
11
import * as React from 'react';
22
import Divider from '@mui/material/Divider';
3+
import Button from '@mui/material/Button';
34
import Stack from '@mui/material/Stack';
4-
import CommunityOrPro from '../CommunityOrPro';
5+
import Typography from '@mui/material/Typography';
6+
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
57

68
export default function SchedulerCommunityOrPremium() {
79
return (
810
<React.Fragment>
911
<Divider />
10-
<Stack direction="column" spacing={{ xs: 1, md: 2 }} sx={{ py: 8, alignItems: 'center' }}>
11-
<CommunityOrPro
12-
caption={'Community and Premium'}
13-
title={'Two packages for every need'}
14-
description={
15-
'Start with the free-forever Community version, then upgrade to Premium when you are ready for additional features and components.'
16-
}
17-
communityDescription={
18-
'Free forever under MIT license. Includes core features such as resource management, multiple views, or drag and drop.'
19-
}
20-
premiumDescription={
21-
'Requires a commercial license. Includes additional features such as recurring events, lazy loading, and virtualization, as well as premium components such as the Event Timeline.'
22-
}
23-
/>
12+
<Stack
13+
direction={{ xs: 'column', md: 'row' }}
14+
sx={{
15+
alignItems: { md: 'flex-end', xs: 'space-between' },
16+
justifyContent: { md: 'space-between', xs: 'flex-end' },
17+
flexGrow: 1,
18+
maxWidth: { xs: '500px', md: '100%' },
19+
width: '100%',
20+
mt: 6,
21+
mb: 12,
22+
}}
23+
>
24+
<Stack
25+
spacing={1}
26+
sx={{ flexBasis: { xs: '100%', md: '65%' }, marginBottom: { xs: '16px', md: 0 } }}
27+
>
28+
<Typography variant="body2" color="primary" sx={{ fontWeight: 'semiBold' }}>
29+
Plans & licensing
30+
</Typography>
31+
<Typography
32+
variant="h4"
33+
component="h2"
34+
sx={{ fontWeight: 'semiBold', color: 'text.primary', fontSize: '1.625rem' }}
35+
>
36+
Two packages for every need
37+
</Typography>
38+
<Typography variant="body1" sx={{ color: 'text.secondary' }}>
39+
Community is MIT-licensed and free forever. Premium is licensed per developer and adds
40+
advanced calendar features plus the Event Timeline.{' '}
41+
</Typography>
42+
</Stack>
43+
<Stack direction="row" spacing={2} sx={{ flexDirection: { xs: 'column', md: 'row' } }}>
44+
<Button
45+
size="small"
46+
variant="contained"
47+
href="/pricing"
48+
endIcon={<ArrowForwardIcon />}
49+
sx={{ width: 'fit-content' }}
50+
>
51+
Compare plans
52+
</Button>
53+
<Button
54+
size="small"
55+
variant="outlined"
56+
href="/x/introduction/licensing/"
57+
endIcon={<ArrowForwardIcon />}
58+
sx={{ width: 'fit-content' }}
59+
>
60+
About licensing
61+
</Button>
62+
</Stack>
2463
</Stack>
2564
</React.Fragment>
2665
);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import * as React from 'react';
2+
import Box from '@mui/material/Box';
3+
import Paper from '@mui/material/Paper';
4+
import Stack from '@mui/material/Stack';
5+
import { EventCalendarPremium } from '@mui/x-scheduler-premium/event-calendar-premium';
6+
import { SchedulerEvent } from '@mui/x-scheduler/models';
7+
import { calendarEvents, calendarResources, calendarDefaultVisibleDate } from './data';
8+
import {
9+
CustomThemeName,
10+
DemoThemeSelector,
11+
SchedulerDemoThemeProvider,
12+
} from './DemoThemeSelector';
13+
import LicenseCard from '../LicenseCard';
14+
15+
export default function CalendarDemo() {
16+
const [selectedTheme, setSelectedTheme] = React.useState<CustomThemeName>('default');
17+
const [calendarEventsState, setCalendarEventsState] =
18+
React.useState<SchedulerEvent[]>(calendarEvents);
19+
20+
return (
21+
<Stack spacing={2.5} sx={{ width: '100%', mb: 6 }}>
22+
<Stack direction="row" sx={{ justifyContent: 'flex-start' }}>
23+
<DemoThemeSelector
24+
ariaLabel="Calendar demo theme"
25+
selectedTheme={selectedTheme}
26+
onThemeChange={(event) => {
27+
setSelectedTheme(event.target.value as CustomThemeName);
28+
}}
29+
/>
30+
</Stack>
31+
<SchedulerDemoThemeProvider selectedTheme={selectedTheme}>
32+
<Paper
33+
elevation={0}
34+
sx={{
35+
height: 660,
36+
width: '100%',
37+
display: 'flex',
38+
flexDirection: 'column',
39+
gap: 1,
40+
}}
41+
>
42+
<Box sx={{ minHeight: 0, flexGrow: 1 }}>
43+
<EventCalendarPremium
44+
events={calendarEventsState}
45+
onEventsChange={setCalendarEventsState}
46+
resources={calendarResources}
47+
defaultVisibleDate={calendarDefaultVisibleDate}
48+
defaultView="month"
49+
defaultPreferences={{ isSidePanelOpen: false }}
50+
areEventsDraggable
51+
areEventsResizable
52+
/>
53+
</Box>
54+
</Paper>
55+
</SchedulerDemoThemeProvider>
56+
<Stack direction="row" spacing={2} sx={{ flexDirection: { xs: 'column', md: 'row' } }}>
57+
<LicenseCard
58+
plan="community"
59+
title="Community features"
60+
description="Free forever under MIT. Everything you need for everyday calendar workflows: multiple views, resources, drag and drop, resizing, and full theming."
61+
/>
62+
<LicenseCard
63+
plan="premium"
64+
title="Premium features"
65+
description="Adds recurring events, exception dates, DST-aware recurrence, and lazy loading through data sources. Same API as Community, so upgrading is a one-line change."
66+
/>
67+
</Stack>
68+
</Stack>
69+
);
70+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import * as React from 'react';
2+
import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles';
3+
import Select, { SelectChangeEvent } from '@mui/material/Select';
4+
import MenuItem from '@mui/material/MenuItem';
5+
import { getSoftEdgesTheme } from '../theme/softEdgesTheme';
6+
import { getNeutralVibesTheme } from '../theme/neutralVibesTheme';
7+
8+
export type CustomThemeName = 'default' | 'softEdges' | 'neutralVibes';
9+
10+
// Use CSS variables to avoid first load light/dark blink.
11+
const darkThemeManagement = {
12+
colorSchemes: { light: true, dark: true },
13+
cssVariables: {
14+
colorSchemeSelector: 'data-mui-color-scheme',
15+
},
16+
};
17+
18+
const themeOptions: { value: CustomThemeName; label: string }[] = [
19+
{ value: 'default', label: 'Default theme' },
20+
{ value: 'softEdges', label: 'Soft edges' },
21+
{ value: 'neutralVibes', label: 'Neutral vibes' },
22+
];
23+
24+
export type DemoThemeSelectorProps = {
25+
ariaLabel: string;
26+
selectedTheme: CustomThemeName;
27+
onThemeChange: (event: SelectChangeEvent) => void;
28+
};
29+
30+
export function DemoThemeSelector({
31+
ariaLabel,
32+
selectedTheme,
33+
onThemeChange,
34+
}: DemoThemeSelectorProps) {
35+
return (
36+
<Select
37+
aria-label={ariaLabel}
38+
value={selectedTheme}
39+
onChange={onThemeChange}
40+
size="small"
41+
sx={{ minWidth: 160 }}
42+
>
43+
{themeOptions.map((option) => (
44+
<MenuItem key={option.value} value={option.value}>
45+
{option.label}
46+
</MenuItem>
47+
))}
48+
</Select>
49+
);
50+
}
51+
52+
type SchedulerDemoThemeProviderProps = {
53+
children: React.ReactNode;
54+
selectedTheme: CustomThemeName;
55+
};
56+
57+
export function SchedulerDemoThemeProvider({
58+
children,
59+
selectedTheme,
60+
}: SchedulerDemoThemeProviderProps) {
61+
const brandingTheme = useTheme();
62+
const mode = brandingTheme.palette.mode;
63+
const demoThemes = React.useMemo(
64+
() => ({
65+
default: createTheme(darkThemeManagement, { palette: { mode } }),
66+
softEdges: getSoftEdgesTheme(mode),
67+
neutralVibes: getNeutralVibesTheme(mode),
68+
}),
69+
[mode],
70+
);
71+
72+
return <ThemeProvider theme={demoThemes[selectedTheme]}>{children}</ThemeProvider>;
73+
}

0 commit comments

Comments
 (0)