Skip to content

Commit

Permalink
Adding calendar component (microsoft#20356)
Browse files Browse the repository at this point in the history
* FluentUI calendar component

* Change files

* Reverting fast-element and fast-foundation updates

* Updating change file type

* Updating documentation

* Updating api-report

* Updating stories

* Updating styles

* Style cleanup

* Updating disabled and selected styles

* Fixing disabled and selected styles for RTL

* Updating high contrast styles

* Fixing readonly styles.

* Cleaning up calendar styles

* More style cleanup.

* Removing unused fixture

* Removing hard coded values in styles

* Removing an unused import

* Fixing interactive calendar styles

* Style updates
  • Loading branch information
robarbms authored and Marion Le Pontois committed Jan 17, 2022
1 parent 951b46f commit a7cd9e4
Show file tree
Hide file tree
Showing 7 changed files with 513 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "FluentUI calendar component",
"packageName": "@fluentui/web-components",
"email": "robarb@microsoft.com",
"dependentChangeType": "patch"
}
19 changes: 12 additions & 7 deletions packages/web-components/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Breadcrumb } from '@microsoft/fast-foundation';
import { BreadcrumbItem } from '@microsoft/fast-foundation';
import { BreadcrumbItemOptions } from '@microsoft/fast-foundation';
import { Button as Button_2 } from '@microsoft/fast-foundation';
import { CalendarOptions } from '@microsoft/fast-foundation';
import { Card as Card_2 } from '@microsoft/fast-foundation';
import { CheckboxOptions } from '@microsoft/fast-foundation';
import { Combobox as Combobox_2 } from '@microsoft/fast-foundation';
Expand Down Expand Up @@ -181,6 +182,7 @@ export const allComponents: {
fluentBreadcrumb: (overrideDefinition?: OverrideFoundationElementDefinition<FoundationElementDefinition> | undefined) => FoundationElementRegistry<FoundationElementDefinition, Breadcrumb>;
fluentBreadcrumbItem: (overrideDefinition?: OverrideFoundationElementDefinition<BreadcrumbItemOptions> | undefined) => FoundationElementRegistry<BreadcrumbItemOptions, Constructable<FoundationElement>>;
fluentButton: (overrideDefinition?: OverrideFoundationElementDefinition<FoundationElementDefinition> | undefined) => FoundationElementRegistry<FoundationElementDefinition, Button>;
fluentCalendar: (overrideDefinition?: OverrideFoundationElementDefinition<CalendarOptions> | undefined) => FoundationElementRegistry<CalendarOptions, Constructable<FoundationElement>>;
fluentCard: (overrideDefinition?: OverrideFoundationElementDefinition<FoundationElementDefinition> | undefined) => FoundationElementRegistry<FoundationElementDefinition, Card>;
fluentCheckbox: (overrideDefinition?: OverrideFoundationElementDefinition<CheckboxOptions> | undefined) => FoundationElementRegistry<CheckboxOptions, Constructable<FoundationElement>>;
fluentCombobox: (overrideDefinition?: OverrideFoundationElementDefinition<ComboboxOptions> | undefined) => FoundationElementRegistry<ComboboxOptions, Constructable<FoundationElement>>;
Expand Down Expand Up @@ -576,6 +578,9 @@ export const fluentBreadcrumbItem: (overrideDefinition?: OverrideFoundationEleme
// @public
export const fluentButton: (overrideDefinition?: OverrideFoundationElementDefinition<FoundationElementDefinition> | undefined) => FoundationElementRegistry<FoundationElementDefinition, typeof Button>;

// @public
export const fluentCalendar: (overrideDefinition?: OverrideFoundationElementDefinition<CalendarOptions> | undefined) => FoundationElementRegistry<CalendarOptions, Constructable<FoundationElement>>;

// @public
export const fluentCard: (overrideDefinition?: OverrideFoundationElementDefinition<FoundationElementDefinition> | undefined) => FoundationElementRegistry<FoundationElementDefinition, typeof Card>;

Expand Down Expand Up @@ -1623,13 +1628,13 @@ export const typeRampPlus6LineHeight: CSSDesignToken<string>;
//
// dist/dts/color/palette.d.ts:70:5 - (ae-forgotten-export) The symbol "create" needs to be exported by the entry point index.d.ts
// dist/dts/color/palette.d.ts:71:5 - (ae-forgotten-export) The symbol "from" needs to be exported by the entry point index.d.ts
// dist/dts/custom-elements.d.ts:49:5 - (ae-incompatible-release-tags) The symbol "fluentAnchor" is marked as @public, but its signature references "Anchor" which is marked as @internal
// dist/dts/custom-elements.d.ts:51:5 - (ae-incompatible-release-tags) The symbol "fluentBadge" is marked as @public, but its signature references "Badge" which is marked as @internal
// dist/dts/custom-elements.d.ts:54:5 - (ae-incompatible-release-tags) The symbol "fluentButton" is marked as @public, but its signature references "Button" which is marked as @internal
// dist/dts/custom-elements.d.ts:91:5 - (ae-incompatible-release-tags) The symbol "fluentTextArea" is marked as @public, but its signature references "TextArea" which is marked as @internal
// dist/dts/custom-elements.d.ts:92:5 - (ae-incompatible-release-tags) The symbol "fluentTextField" is marked as @public, but its signature references "TextField" which is marked as @internal
// dist/dts/custom-elements.d.ts:93:5 - (ae-incompatible-release-tags) The symbol "fluentToolbar" is marked as @public, but its signature references "Toolbar" which is marked as @internal
// dist/dts/custom-elements.d.ts:94:5 - (ae-incompatible-release-tags) The symbol "fluentTooltip" is marked as @public, but its signature references "Tooltip" which is marked as @internal
// dist/dts/custom-elements.d.ts:50:5 - (ae-incompatible-release-tags) The symbol "fluentAnchor" is marked as @public, but its signature references "Anchor" which is marked as @internal
// dist/dts/custom-elements.d.ts:52:5 - (ae-incompatible-release-tags) The symbol "fluentBadge" is marked as @public, but its signature references "Badge" which is marked as @internal
// dist/dts/custom-elements.d.ts:55:5 - (ae-incompatible-release-tags) The symbol "fluentButton" is marked as @public, but its signature references "Button" which is marked as @internal
// dist/dts/custom-elements.d.ts:93:5 - (ae-incompatible-release-tags) The symbol "fluentTextArea" is marked as @public, but its signature references "TextArea" which is marked as @internal
// dist/dts/custom-elements.d.ts:94:5 - (ae-incompatible-release-tags) The symbol "fluentTextField" is marked as @public, but its signature references "TextField" which is marked as @internal
// dist/dts/custom-elements.d.ts:95:5 - (ae-incompatible-release-tags) The symbol "fluentToolbar" is marked as @public, but its signature references "Toolbar" which is marked as @internal
// dist/dts/custom-elements.d.ts:96:5 - (ae-incompatible-release-tags) The symbol "fluentTooltip" is marked as @public, but its signature references "Tooltip" which is marked as @internal

// (No @packageDocumentation comment for this package)

Expand Down
136 changes: 136 additions & 0 deletions packages/web-components/src/calendar/calendar.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import './index';
import { fluentCalendar } from './index';

const now = new Date();
const years = new Array(9).fill(null).map((_, index) => (now.getFullYear() - 4 + index).toString());
const groupsToDates = matrix =>
matrix.map(days => days.map(day => `${now.getMonth() + 1}-${day}-${now.getFullYear()}`).join(','));
const disabledDates = groupsToDates([
[1, 2, 3, 4, now.getDate()],
[6, 7, 10, 18],
[8, 17, 24, 25],
[4, 11, 18, 25],
]);

export default {
title: 'Components/Calendar',
component: fluentCalendar,
argTypes: {
month: {
description: 'Month of the calendar to display.',
control: { type: 'select' },
options: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
default: 'The current month',
},
year: {
description: 'Year of the calendar to display.',
control: { type: 'select' },
options: years,
default: 'The current year',
},
locale: {
control: { type: 'select' },
options: ['en-US', 'fr-FR', 'de-DE', 'th-TH-u-ca-buddhist-nu-thai', 'ar-XE-u-ca-islamic-nu-arab'],
default: 'en-US',
description:
'Locale information which can include market (language and country), calendar type and numbering type.',
},
dayFormat: {
description: 'Formatting for the numbered days.',
options: ['2-digit', 'numeric'],
control: { type: 'select' },
default: 'numeric',
},
weekdayFormat: {
description: 'Formatting for the weekday titles.',
options: ['long', 'narrow', 'short'],
control: { type: 'select' },
default: 'short',
},
monthFormat: {
description: 'Formatting for the month name in the title.',
options: ['2-digit', 'long', 'narrow', 'numeric', 'short'],
control: { type: 'select' },
default: 'long',
},
yearFormat: {
description: 'Formatting for the year in the title.',
options: ['2-digit', 'numeric'],
control: { type: 'select' },
default: 'numeric',
},
disabledDates: {
description: 'Dates to be shown as disabled.',
options: disabledDates,
control: { type: 'select' },
},
selectedDates: {
description: 'Dates to be shown as selected',
options: disabledDates,
control: { type: 'select' },
},
readonly: {
description: 'A readonly version of the calendar without AT interactions.',
control: { type: 'boolean' },
default: true,
},
},
};

const CalendarTemplate = ({
month,
year,
locale,
dayFormat,
weekdayFormat,
monthFormat,
yearFormat,
disabledDates,
selectedDates,
readonly,
}) =>
`
<style>
div.docs-story > div:first-child {
height: 22em !important;
}
</style>
<fluent-calendar
${month ? `month="${month}"` : ''}
${year ? `year="${year}"` : ''}
${locale ? `locale="${locale}"` : ''}
${dayFormat ? `day-format="${dayFormat}"` : ''}
${weekdayFormat ? `weekday-format="${weekdayFormat}"` : ''}
${monthFormat ? `month-format="${monthFormat}"` : ''}
${yearFormat ? `year-format="${yearFormat}"` : ''}
${disabledDates ? `disabled-dates="${disabledDates}"` : ''}
${selectedDates ? `selected-dates="${selectedDates}"` : ''}
${readonly === false ? `readonly="false"` : ''}
></fluent-calendar>
`;

export const Calendar = CalendarTemplate.bind({});

Calendar.args = {
label: 'Calendar',
month: (now.getMonth() + 1).toString(),
year: now.getFullYear().toString(),
locale: 'en-US',
readonly: true,
dayFormat: 'numeric',
weekdayFormat: 'short',
monthFormat: 'long',
yearFormat: 'numeric',
};

const example = `
<fluent-calendar></fluent-calendar>
`;

Calendar.parameters = {
docs: {
source: {
code: example,
},
},
};
166 changes: 166 additions & 0 deletions packages/web-components/src/calendar/calendar.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { css, ElementStyles } from "@microsoft/fast-element";
import {
display,
ElementDefinitionContext,
forcedColorsStylesheetBehavior,
FoundationElementDefinition
} from '@microsoft/fast-foundation';
import { SystemColors } from '@microsoft/fast-web-utilities';
import {
accentFillRest,
baseHeightMultiplier,
bodyFont,
controlCornerRadius,
density,
designUnit,
fillColor,
foregroundOnAccentRest,
neutralForegroundHint,
neutralForegroundRest,
strokeWidth,
typeRampBaseFontSize,
typeRampBaseLineHeight,
} from '../design-tokens';
import { DirectionalStyleSheetBehavior } from '../styles';

/**
* LTR styles for calendar
* @internal
*/
const ltrStyles = css`
.day.disabled::before {
transform: translate(-50%, 0) rotate(45deg);
}
`;

/**
* RTL styles for calendar
* @internal
*/
const rtlStyles = css`
.day.disabled::before {
transform: translate(50%, 0) rotate(-45deg);
}
`;

/**
* Styles for calendar
* @public
*/
export const calendarStyles: (
context: ElementDefinitionContext,
definition: FoundationElementDefinition
) => ElementStyles = (
context: ElementDefinitionContext,
definition: FoundationElementDefinition
) => css`
${display("inline-block")} :host {
--calendar-cell-size: calc((${baseHeightMultiplier} + 2 + ${density}) * ${designUnit} * 1px);
--calendar-gap: 2px;
font-family: ${bodyFont};
font-size: ${typeRampBaseFontSize};
line-height: ${typeRampBaseLineHeight};
color: ${neutralForegroundRest};
}
.title {
padding: calc(${designUnit} * 2px);
font-weight: 600;
}
.days {
text-align: center;
}
.week-days,
.week {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: var(--calendar-gap);
border: 0;
padding: 0;
}
.day,
.week-day {
border: 0;
width: var(--calendar-cell-size);
height: var(--calendar-cell-size);
line-height: var(--calendar-cell-size);
padding: 0;
box-sizing: initial;
}
.week-day {
font-weight: 600;
}
.day {
border: calc(${strokeWidth} * 1px) solid transparent;
border-radius: calc(${controlCornerRadius} * 1px);
}
.interact .day {
cursor: pointer;
}
.date {
height: 100%;
}
.inactive .date,
.inactive.disabled::before {
color: ${neutralForegroundHint};
}
.disabled::before {
content: '';
display: inline-block;
width: calc(var(--calendar-cell-size) * .8);
height: calc(${strokeWidth} * 1px);
background: currentColor;
position: absolute;
margin-top: calc(var(--calendar-cell-size) / 2);
transform-origin: center;
z-index: 1;
}
.selected {
color: ${accentFillRest};
border: 1px solid ${accentFillRest};
background: ${fillColor};
}
.selected + .selected {
border-start-start-radius: 0;
border-end-start-radius: 0;
border-inline-start-width: 0;
padding-inline-start: calc(var(--calendar-gap) + (${strokeWidth} + ${controlCornerRadius}) * 1px);
margin-inline-start: calc((${controlCornerRadius} * -1px) - var(--calendar-gap));
}
.today.disabled::before {
color: ${foregroundOnAccentRest};
}
.today .date {
color: ${foregroundOnAccentRest};
background: ${accentFillRest};
border-radius: 50%;
position: relative;
}
`.withBehaviors(
forcedColorsStylesheetBehavior(
css`
.day.selected {
color: ${SystemColors.Highlight};
}
.today .date {
background: ${SystemColors.Highlight};
color: ${SystemColors.HighlightText};
}
`
),
new DirectionalStyleSheetBehavior(ltrStyles, rtlStyles)
);
Loading

0 comments on commit a7cd9e4

Please sign in to comment.