- Calendar set to { args.timeZone ?? 'current' } timezone, - disabling selection for all dates before today, and starting - with a default date of 1 week from today. -
- > - ); - }, - args: { - timeZone: 'Pacific/Auckland', - }, - argTypes: { - selected: { - control: false, - }, - defaultSelected: { - control: false, - }, - disabled: { - control: false, - }, - }, -}; diff --git a/packages/components/src/calendar/stories/date-range-calendar.story.tsx b/packages/components/src/calendar/stories/date-range-calendar.story.tsx deleted file mode 100644 index b51fcb131400c2..00000000000000 --- a/packages/components/src/calendar/stories/date-range-calendar.story.tsx +++ /dev/null @@ -1,242 +0,0 @@ -import { fn } from 'storybook/test'; -import { - enUS, - fr, - es, - de, - it, - he, - ru, - ja, - ptBR, - nl, - ko, - tr, - id, - zhCN, - zhTW, - ar, - sv, -} from 'date-fns/locale'; -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { useState, useEffect } from '@wordpress/element'; -import { DateRangeCalendar, TZDate } from '..'; - -// Storybook date controls pass a number, but react-day-picker expects a Date for `endMonth`. -function toDate( value: Date | number | undefined ): Date | undefined { - return value === undefined ? undefined : new Date( value ); -} - -const meta: Meta< typeof DateRangeCalendar > = { - title: 'Components/Selection & Input/Time & Date/DateRangeCalendar', - component: DateRangeCalendar, - tags: [ 'status-private' ], - render: ( { endMonth, ...args } ) => ( -- Calendar set to { args.timeZone ?? 'current' } timezone, - disabling selection for all dates before today, and starting - with a default date range of 1 week from today to 2 weeks - from today. -
- > - ); - }, - args: { - timeZone: 'Pacific/Auckland', - }, - argTypes: { - disabled: { - control: false, - }, - }, -}; diff --git a/packages/components/src/calendar/utils/constants.ts b/packages/components/src/calendar/utils/constants.ts deleted file mode 100644 index 9e32797cdf9969..00000000000000 --- a/packages/components/src/calendar/utils/constants.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Internal dependencies - */ -import { Day } from './day-cell'; - -const CLASSNAMES = { - root: 'components-calendar', - day: 'components-calendar__day', - day_button: 'components-calendar__day-button', - outside: 'components-calendar__day--outside', - caption_label: 'components-calendar__caption-label', - button_next: 'components-calendar__button-next', - button_previous: 'components-calendar__button-previous', - chevron: 'components-calendar__chevron', - nav: 'components-calendar__nav', - month_caption: 'components-calendar__month-caption', - months: 'components-calendar__months', - month_grid: 'components-calendar__month-grid', - weekday: 'components-calendar__weekday', - today: 'components-calendar__day--today', - selected: 'components-calendar__day--selected', - disabled: 'components-calendar__day--disabled', - hidden: 'components-calendar__day--hidden', - range_start: 'components-calendar__range-start', - range_end: 'components-calendar__range-end', - range_middle: 'components-calendar__range-middle', - weeks_before_enter: 'components-calendar__weeks-before-enter', - weeks_before_exit: 'components-calendar__weeks-before-exit', - weeks_after_enter: 'components-calendar__weeks-after-enter', - weeks_after_exit: 'components-calendar__weeks-after-exit', - caption_after_enter: 'components-calendar__caption-after-enter', - caption_after_exit: 'components-calendar__caption-after-exit', - caption_before_enter: 'components-calendar__caption-before-enter', - caption_before_exit: 'components-calendar__caption-before-exit', -}; -export const MODIFIER_CLASSNAMES = { - preview: 'components-calendar__day--preview', - preview_start: 'components-calendar__day--preview-start', - preview_end: 'components-calendar__day--preview-end', -}; - -export const COMMON_PROPS = { - animate: true, - // Only show days in the current month - showOutsideDays: false, - // Hide week number column - showWeekNumber: false, - // Show weekdays row - hideWeekdays: false, - // Month and year caption are not interactive - captionLayout: 'label', - // Show a variable number of weeks depending on the month - fixedWeeks: false, - // Show navigation buttons - hideNavigation: false, - // Class names - classNames: CLASSNAMES, - // Default role - role: 'application', - components: { - Day, - }, -} as const; diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index 82cd12db36c5e6..b29fa1162817fc 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -15,7 +15,6 @@ import { withIgnoreIMEEvents } from './utils/with-ignore-ime-events'; import { lock } from './lock-unlock'; import Badge from './badge'; -import { DateCalendar, DateRangeCalendar, TZDate } from './calendar'; import { ValidatedCheckboxControl, ValidatedComboboxControl, @@ -43,9 +42,6 @@ lock( privateApis, { withIgnoreIMEEvents, Badge, normalizeTextString, - DateCalendar, - DateRangeCalendar, - TZDate, useDrag, ValidatedInputControl, ValidatedCheckboxControl, diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss index 6d85fd28f7ac60..97d92248de16f0 100644 --- a/packages/components/src/style.scss +++ b/packages/components/src/style.scss @@ -4,7 +4,6 @@ @use "./badge/styles.scss" as *; @use "./button-group/style.scss" as *; @use "./button/style.scss" as *; -@use "./calendar/style.scss" as *; @use "./checkbox-control/style.scss" as *; @use "./circular-option-picker/style.scss" as *; @use "./palette-edit/style.scss" as *; @@ -50,5 +49,5 @@ // Include the default WP Components color variables. // TODO: Remove this once all admin-scheme variables are accounted for in the wp-components fallback values. :root { - @include admin-scheme( #3858e9 ); + @include admin-scheme(#3858e9); } diff --git a/packages/dataviews/CHANGELOG.md b/packages/dataviews/CHANGELOG.md index a6b46354c1adaa..722c56b6b98a03 100644 --- a/packages/dataviews/CHANGELOG.md +++ b/packages/dataviews/CHANGELOG.md @@ -27,6 +27,7 @@ ### Internal +- DataViews: Move the private `DateCalendar` and `DateRangeCalendar` components from `@wordpress/components` into the package as internal components, instead of unlocking them via private cross-package APIs ([#81230](https://github.com/WordPress/gutenberg/issues/81230)). Adds a direct `react-day-picker` dependency; the calendar CSS class prefix changes from `components-calendar` to `dataviews-calendar`; no behavior change. ([#XXXXX](https://github.com/WordPress/gutenberg/pull/XXXXX)) - DataViews: Inline a verbatim copy of the `kebabCase` utility instead of unlocking the private one from `@wordpress/components`, as part of removing the package's reliance on private cross-package APIs ([#81230](https://github.com/WordPress/gutenberg/issues/81230)). Adds a direct `change-case` dependency; no behavior change. ([#81284](https://github.com/WordPress/gutenberg/pull/81284)) - Update `date-fns` to 4.4.0 ([#80763](https://github.com/WordPress/gutenberg/pull/80763)). - Update Jest type definitions to v30 ([#80767](https://github.com/WordPress/gutenberg/pull/80767)). diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 43393bfc3ce3b4..eb59cbcf7a6f0c 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -74,6 +74,7 @@ "date-fns": "^4.4.0", "deepmerge": "^4.3.1", "fast-deep-equal": "^3.1.3", + "react-day-picker": "^9.14.0", "remove-accents": "^0.5.0" }, "devDependencies": { diff --git a/packages/components/src/calendar/date-calendar/index.tsx b/packages/dataviews/src/components/calendar/date-calendar/index.tsx similarity index 96% rename from packages/components/src/calendar/date-calendar/index.tsx rename to packages/dataviews/src/components/calendar/date-calendar/index.tsx index f70807d4d38457..72b0ed439ad0ff 100644 --- a/packages/components/src/calendar/date-calendar/index.tsx +++ b/packages/dataviews/src/components/calendar/date-calendar/index.tsx @@ -12,7 +12,7 @@ import { useCallback } from '@wordpress/element'; */ import { COMMON_PROPS } from '../utils/constants'; import { clampNumberOfMonths } from '../utils/misc'; -import { useControlledValue } from '../../utils/hooks'; +import { useControlledValue } from '../utils/use-controlled-value'; import { useLocalizationProps } from '../utils/use-localization-props'; import type { DateCalendarProps, OnSelectHandler } from '../types'; diff --git a/packages/components/src/calendar/date-range-calendar/index.tsx b/packages/dataviews/src/components/calendar/date-range-calendar/index.tsx similarity index 98% rename from packages/components/src/calendar/date-range-calendar/index.tsx rename to packages/dataviews/src/components/calendar/date-range-calendar/index.tsx index c67c50aadd784f..7f3d7302d078cf 100644 --- a/packages/components/src/calendar/date-range-calendar/index.tsx +++ b/packages/dataviews/src/components/calendar/date-range-calendar/index.tsx @@ -13,7 +13,7 @@ import { useMemo, useState, useCallback } from '@wordpress/element'; */ import { COMMON_PROPS, MODIFIER_CLASSNAMES } from '../utils/constants'; import { clampNumberOfMonths } from '../utils/misc'; -import { useControlledValue } from '../../utils/hooks'; +import { useControlledValue } from '../utils/use-controlled-value'; import { useLocalizationProps } from '../utils/use-localization-props'; import type { DateRangeCalendarProps, diff --git a/packages/components/src/calendar/index.tsx b/packages/dataviews/src/components/calendar/index.tsx similarity index 100% rename from packages/components/src/calendar/index.tsx rename to packages/dataviews/src/components/calendar/index.tsx diff --git a/packages/components/src/calendar/style.scss b/packages/dataviews/src/components/calendar/style.scss similarity index 57% rename from packages/components/src/calendar/style.scss rename to packages/dataviews/src/components/calendar/style.scss index 3d38c8eb3c8763..63a66fe518bf6c 100644 --- a/packages/components/src/calendar/style.scss +++ b/packages/dataviews/src/components/calendar/style.scss @@ -1,25 +1,47 @@ -@use "@wordpress/base-styles/colors" as *; @use "@wordpress/base-styles/variables" as *; -@use "../utils/theme-variables" as *; + +// Color tokens, mapped from the theme variables internal to the +// `@wordpress/components` package (where this component previously lived) to +// the design-system tokens those variables resolve to outside of a themed +// components subtree, so the calendar keeps the same appearance it had there. +$calendar-color-accent: var(--wp-admin-theme-color, #3858e9); +$calendar-color-background: var(--wpds-color-background-surface-neutral-strong); +$calendar-color-foreground: var(--wpds-color-foreground-content-neutral); +$calendar-color-foreground-inverted: var( + --wpds-color-background-surface-neutral +); +$calendar-color-gray-400: var(--wpds-color-stroke-interactive-neutral); +$calendar-color-gray-600: var(--wpds-color-stroke-interactive-neutral); +$calendar-color-gray-700: var(--wpds-color-foreground-content-neutral-weak); +$calendar-color-gray-800: var(--wpds-color-foreground-content-neutral); +$calendar-color-disabled: $calendar-color-gray-600; /* Root of the component. */ // Internal variables -$wp-components-calendar-outline-focus: var(--wp-admin-border-width-focus) - solid $components-color-accent; -$wp-components-calendar-button-height: $grid-unit-40; -$wp-components-calendar-button-width: $grid-unit-40; -$wp-components-calendar-nav-height: $grid-unit-40; -$wp-components-calendar-range-middle-background-color: color-mix(in srgb, $components-color-accent 4%, transparent); -$wp-components-calendar-preview-border-color: color-mix(in srgb, $components-color-accent 16%, transparent); - -.components-calendar { +$wp-dataviews-calendar-outline-focus: var(--wp-admin-border-width-focus) solid + $calendar-color-accent; +$wp-dataviews-calendar-button-height: $grid-unit-40; +$wp-dataviews-calendar-button-width: $grid-unit-40; +$wp-dataviews-calendar-nav-height: $grid-unit-40; +$wp-dataviews-calendar-range-middle-background-color: color-mix( + in srgb, + $calendar-color-accent 4%, + transparent +); +$wp-dataviews-calendar-preview-border-color: color-mix( + in srgb, + $calendar-color-accent 16%, + transparent +); + +.dataviews-calendar { // TODO: add font family rule when Theme is ready position: relative; /* Required to position the navigation toolbar. */ box-sizing: border-box; display: inline flow-root; - color: $components-color-foreground; - background-color: $components-color-background; + color: $calendar-color-foreground; + background-color: $calendar-color-background; font-size: $font-size-medium; font-weight: var(--wpds-typography-font-weight-default); z-index: 0; // Create a stacking context and render on top of the background. @@ -31,7 +53,7 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col } } -.components-calendar__day { +.dataviews-calendar__day { padding: 0; position: relative; @@ -39,17 +61,17 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col // day button to ensure that the color of the dot used to indicate today's // date follows the same color as the button's text, since the button // inherits its text color. - &:has(.components-calendar__day-button:disabled) { + &:has(.dataviews-calendar__day-button:disabled) { // Use a lighter shade of gray for less visual prominence. - color: $components-color-gray-400; + color: $calendar-color-gray-400; } - &:has(.components-calendar__day-button:hover:not(:disabled)), - &:has(.components-calendar__day-button:focus-visible) { - color: $components-color-accent; + &:has(.dataviews-calendar__day-button:hover:not(:disabled)), + &:has(.dataviews-calendar__day-button:focus-visible) { + color: $calendar-color-accent; } } -.components-calendar__day-button { +.dataviews-calendar__day-button { background: none; padding: 0; margin: 0; @@ -59,8 +81,8 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col display: flex; position: relative; - width: $wp-components-calendar-button-width; - height: $wp-components-calendar-button-height; + width: $wp-dataviews-calendar-button-width; + height: $wp-dataviews-calendar-button-height; border: none; border-radius: $radius-small; @@ -94,7 +116,7 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col &:disabled { cursor: revert; - @media ( forced-colors: active ) { + @media (forced-colors: active) { // As an extra visual cue, show a line-through on disabled days // in forced-colors (high-contrast) mode. text-decoration: line-through; @@ -102,12 +124,12 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col } &:focus-visible { - outline: $wp-components-calendar-outline-focus; + outline: $wp-dataviews-calendar-outline-focus; outline-offset: 1px; } } -.components-calendar__caption-label { +.dataviews-calendar__caption-label { z-index: 1; position: relative; @@ -120,8 +142,8 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col text-transform: capitalize; } -.components-calendar__button-next, -.components-calendar__button-previous { +.dataviews-calendar__button-next, +.dataviews-calendar__button-previous { border: none; border-radius: $radius-small; background: none; @@ -134,8 +156,8 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col justify-content: center; position: relative; - width: $wp-components-calendar-button-width; - height: $wp-components-calendar-button-height; + width: $wp-dataviews-calendar-button-width; + height: $wp-dataviews-calendar-button-height; color: inherit; @@ -143,38 +165,38 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col &[aria-disabled="true"] { cursor: revert; - color: $components-color-disabled; + color: $calendar-color-disabled; } &:focus-visible { - outline: $wp-components-calendar-outline-focus; + outline: $wp-dataviews-calendar-outline-focus; } } -.components-calendar__chevron { +.dataviews-calendar__chevron { display: inline-block; fill: currentColor; width: $grid-unit-20; height: $grid-unit-20; } -.components-calendar[dir="rtl"] -.components-calendar__nav -.components-calendar__chevron { +.dataviews-calendar[dir="rtl"] + .dataviews-calendar__nav + .dataviews-calendar__chevron { transform: rotate(180deg); transform-origin: 50%; } -.components-calendar__month-caption { +.dataviews-calendar__month-caption { display: flex; justify-content: center; align-content: center; - height: $wp-components-calendar-nav-height; + height: $wp-dataviews-calendar-nav-height; margin-bottom: $grid-unit-15; } -.components-calendar__months { +.dataviews-calendar__months { position: relative; display: flex; justify-content: center; @@ -183,12 +205,12 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col max-width: fit-content; } -.components-calendar__month-grid { +.dataviews-calendar__month-grid { border-collapse: separate; border-spacing: 0 $grid-unit-05; } -.components-calendar__nav { +.dataviews-calendar__nav { position: absolute; inset-block-start: 0; inset-inline-start: 0; @@ -198,22 +220,22 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col align-items: center; justify-content: space-between; - height: $wp-components-calendar-nav-height; + height: $wp-dataviews-calendar-nav-height; } -.components-calendar__weekday { - width: $wp-components-calendar-button-width; - height: $wp-components-calendar-button-height; +.dataviews-calendar__weekday { + width: $wp-dataviews-calendar-button-width; + height: $wp-dataviews-calendar-button-height; padding: 0; - color: $components-color-gray-700; + color: $calendar-color-gray-700; text-align: center; text-transform: uppercase; } /* DAY MODIFIERS */ // Today's date: show a dot in the top-right corner of the button -.components-calendar__day--today::after { +.dataviews-calendar__day--today::after { content: ""; position: absolute; z-index: 1; @@ -226,17 +248,17 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col } // Selected date button (individual date, range start, and range end) -.components-calendar__day--selected:not(.components-calendar__range-middle) { +.dataviews-calendar__day--selected:not(.dataviews-calendar__range-middle) { &:has( - .components-calendar__day-button, - .components-calendar__day-button:hover:not(:disabled) -) { - color: $components-color-foreground-inverted; + .dataviews-calendar__day-button, + .dataviews-calendar__day-button:hover:not(:disabled) + ) { + color: $calendar-color-foreground-inverted; } - .components-calendar__day-button { + .dataviews-calendar__day-button { &::before { - background-color: $components-color-foreground; + background-color: $calendar-color-foreground; // Render a transparent border to highlight the selected day in // forced-colors (high-contrast) mode, since the background is not // visible. @@ -244,28 +266,28 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col } &:disabled::before { - background-color: $components-color-gray-400; + background-color: $calendar-color-gray-400; } &:hover:not(:disabled)::before { - background-color: $components-color-gray-800; + background-color: $calendar-color-gray-800; } } } // Outside month days (visible when showOutsideDays is true): use a lighter text color. -.components-calendar__day--outside { - color: $components-color-gray-600; +.dataviews-calendar__day--outside { + color: $calendar-color-gray-600; } // Hidden button (ie. outside current month but still rendered) -.components-calendar__day--hidden { +.dataviews-calendar__day--hidden { visibility: hidden; } // Range start button, but not when start and end are the same day. -.components-calendar__range-start:not(.components-calendar__range-end) -.components-calendar__day-button { +.dataviews-calendar__range-start:not(.dataviews-calendar__range-end) + .dataviews-calendar__day-button { // Apply border-radius changes to the button itself too, so that the focus // ring follows the same shape as the button's background. &, @@ -276,9 +298,9 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col } // Middle of date range -.components-calendar__range-middle .components-calendar__day-button { +.dataviews-calendar__range-middle .dataviews-calendar__day-button { &::before { - background-color: $wp-components-calendar-range-middle-background-color; + background-color: $wp-dataviews-calendar-range-middle-background-color; border-radius: 0; // Render a top and bottom transparent border to highlight the selected @@ -291,8 +313,8 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col } // Range end button, but not when start and end are the same day. -.components-calendar__range-end:not(.components-calendar__range-start) -.components-calendar__day-button { +.dataviews-calendar__range-end:not(.dataviews-calendar__range-start) + .dataviews-calendar__day-button { // Apply border-radius changes to the button itself too, so that the focus // ring follows the same shape as the button's background. &, @@ -314,23 +336,23 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col * CSS variables, we're using the SVGs as masks, and using `background-color` * to consume the accent color variable. */ -.components-calendar__day--preview svg { +.dataviews-calendar__day--preview svg { position: absolute; inset: 0; pointer-events: none; - color: $wp-components-calendar-preview-border-color; + color: $wp-dataviews-calendar-preview-border-color; - @media ( forced-colors: active ) { + @media (forced-colors: active) { color: inherit; } - .components-calendar[dir="rtl"] & { + .dataviews-calendar[dir="rtl"] & { transform: scaleX(-1); } } -.components-calendar__day--preview.components-calendar__range-middle -.components-calendar__day-button::before { +.dataviews-calendar__day--preview.dataviews-calendar__range-middle + .dataviews-calendar__day-button::before { // Remove the transparent border shown on the middle of the range // in forced-colors (high-contrast) mode, to allow for the dashed border // to be visible. @@ -392,49 +414,49 @@ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-col } } -.components-calendar__weeks-before-enter, -.components-calendar__weeks-before-exit, -.components-calendar__weeks-after-enter, -.components-calendar__weeks-after-exit, -.components-calendar__caption-after-enter, -.components-calendar__caption-after-exit, -.components-calendar__caption-before-enter, -.components-calendar__caption-before-exit { +.dataviews-calendar__weeks-before-enter, +.dataviews-calendar__weeks-before-exit, +.dataviews-calendar__weeks-after-enter, +.dataviews-calendar__weeks-after-exit, +.dataviews-calendar__caption-after-enter, +.dataviews-calendar__caption-after-exit, +.dataviews-calendar__caption-before-enter, +.dataviews-calendar__caption-before-exit { animation-duration: 0s; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); animation-fill-mode: forwards; - @media not ( prefers-reduced-motion ) { + @media not (prefers-reduced-motion) { animation-duration: 0.3s; } } -.components-calendar__weeks-before-enter, -.components-calendar[dir="rtl"] .components-calendar__weeks-after-enter { +.dataviews-calendar__weeks-before-enter, +.dataviews-calendar[dir="rtl"] .dataviews-calendar__weeks-after-enter { animation-name: slide-in-left; } -.components-calendar__weeks-before-exit, -.components-calendar[dir="rtl"] .components-calendar__weeks-after-exit { +.dataviews-calendar__weeks-before-exit, +.dataviews-calendar[dir="rtl"] .dataviews-calendar__weeks-after-exit { animation-name: slide-out-left; } -.components-calendar__weeks-after-enter, -.components-calendar[dir="rtl"] .components-calendar__weeks-before-enter { +.dataviews-calendar__weeks-after-enter, +.dataviews-calendar[dir="rtl"] .dataviews-calendar__weeks-before-enter { animation-name: slide-in-right; } -.components-calendar__weeks-after-exit, -.components-calendar[dir="rtl"] .components-calendar__weeks-before-exit { +.dataviews-calendar__weeks-after-exit, +.dataviews-calendar[dir="rtl"] .dataviews-calendar__weeks-before-exit { animation-name: slide-out-right; } -.components-calendar__caption-after-enter { +.dataviews-calendar__caption-after-enter { animation-name: fade-in; } -.components-calendar__caption-after-exit { +.dataviews-calendar__caption-after-exit { animation-name: fade-out; } -.components-calendar__caption-before-enter { +.dataviews-calendar__caption-before-enter { animation-name: fade-in; } -.components-calendar__caption-before-exit { +.dataviews-calendar__caption-before-exit { animation-name: fade-out; } diff --git a/packages/components/src/calendar/test/__utils__/index.ts b/packages/dataviews/src/components/calendar/test/__utils__/index.ts similarity index 100% rename from packages/components/src/calendar/test/__utils__/index.ts rename to packages/dataviews/src/components/calendar/test/__utils__/index.ts diff --git a/packages/components/src/calendar/test/date-calendar.tsx b/packages/dataviews/src/components/calendar/test/date-calendar.tsx similarity index 100% rename from packages/components/src/calendar/test/date-calendar.tsx rename to packages/dataviews/src/components/calendar/test/date-calendar.tsx diff --git a/packages/components/src/calendar/test/date-range-calendar.tsx b/packages/dataviews/src/components/calendar/test/date-range-calendar.tsx similarity index 100% rename from packages/components/src/calendar/test/date-range-calendar.tsx rename to packages/dataviews/src/components/calendar/test/date-range-calendar.tsx diff --git a/packages/components/src/calendar/types.ts b/packages/dataviews/src/components/calendar/types.ts similarity index 98% rename from packages/components/src/calendar/types.ts rename to packages/dataviews/src/components/calendar/types.ts index 36934922e90a40..b41bc11f6e787c 100644 --- a/packages/components/src/calendar/types.ts +++ b/packages/dataviews/src/components/calendar/types.ts @@ -294,9 +294,9 @@ export interface BaseProps * for the possible values. * * When working with time zones, use the `TZDate` object exported by this - * package instead of the native `Date` object. + * component's module instead of the native `Date` object. * @example - * import { DateCalendar, TZDate } from "@wordpress/components"; + * import { DateCalendar, TZDate } from '../calendar'; * * export function WithTimeZone() { * const timeZone = "America/New_York"; diff --git a/packages/dataviews/src/components/calendar/utils/constants.ts b/packages/dataviews/src/components/calendar/utils/constants.ts new file mode 100644 index 00000000000000..2090a19c47a159 --- /dev/null +++ b/packages/dataviews/src/components/calendar/utils/constants.ts @@ -0,0 +1,63 @@ +/** + * Internal dependencies + */ +import { Day } from './day-cell'; + +const CLASSNAMES = { + root: 'dataviews-calendar', + day: 'dataviews-calendar__day', + day_button: 'dataviews-calendar__day-button', + outside: 'dataviews-calendar__day--outside', + caption_label: 'dataviews-calendar__caption-label', + button_next: 'dataviews-calendar__button-next', + button_previous: 'dataviews-calendar__button-previous', + chevron: 'dataviews-calendar__chevron', + nav: 'dataviews-calendar__nav', + month_caption: 'dataviews-calendar__month-caption', + months: 'dataviews-calendar__months', + month_grid: 'dataviews-calendar__month-grid', + weekday: 'dataviews-calendar__weekday', + today: 'dataviews-calendar__day--today', + selected: 'dataviews-calendar__day--selected', + disabled: 'dataviews-calendar__day--disabled', + hidden: 'dataviews-calendar__day--hidden', + range_start: 'dataviews-calendar__range-start', + range_end: 'dataviews-calendar__range-end', + range_middle: 'dataviews-calendar__range-middle', + weeks_before_enter: 'dataviews-calendar__weeks-before-enter', + weeks_before_exit: 'dataviews-calendar__weeks-before-exit', + weeks_after_enter: 'dataviews-calendar__weeks-after-enter', + weeks_after_exit: 'dataviews-calendar__weeks-after-exit', + caption_after_enter: 'dataviews-calendar__caption-after-enter', + caption_after_exit: 'dataviews-calendar__caption-after-exit', + caption_before_enter: 'dataviews-calendar__caption-before-enter', + caption_before_exit: 'dataviews-calendar__caption-before-exit', +}; +export const MODIFIER_CLASSNAMES = { + preview: 'dataviews-calendar__day--preview', + preview_start: 'dataviews-calendar__day--preview-start', + preview_end: 'dataviews-calendar__day--preview-end', +}; + +export const COMMON_PROPS = { + animate: true, + // Only show days in the current month + showOutsideDays: false, + // Hide week number column + showWeekNumber: false, + // Show weekdays row + hideWeekdays: false, + // Month and year caption are not interactive + captionLayout: 'label', + // Show a variable number of weeks depending on the month + fixedWeeks: false, + // Show navigation buttons + hideNavigation: false, + // Class names + classNames: CLASSNAMES, + // Default role + role: 'application', + components: { + Day, + }, +} as const; diff --git a/packages/components/src/calendar/utils/day-cell.tsx b/packages/dataviews/src/components/calendar/utils/day-cell.tsx similarity index 100% rename from packages/components/src/calendar/utils/day-cell.tsx rename to packages/dataviews/src/components/calendar/utils/day-cell.tsx diff --git a/packages/components/src/calendar/utils/misc.ts b/packages/dataviews/src/components/calendar/utils/misc.ts similarity index 100% rename from packages/components/src/calendar/utils/misc.ts rename to packages/dataviews/src/components/calendar/utils/misc.ts diff --git a/packages/dataviews/src/components/calendar/utils/use-controlled-value.ts b/packages/dataviews/src/components/calendar/utils/use-controlled-value.ts new file mode 100644 index 00000000000000..725283736f5026 --- /dev/null +++ b/packages/dataviews/src/components/calendar/utils/use-controlled-value.ts @@ -0,0 +1,52 @@ +/** + * WordPress dependencies + */ +import { useCallback, useState } from '@wordpress/element'; + +type Props< T > = { + defaultValue?: T; + value?: T; + onChange?: ( value: T, ...args: any[] ) => void; +}; + +/** + * Simplified and improved implementation of useControlledState. + * + * @param props + * @param props.defaultValue + * @param props.value + * @param props.onChange + * @return The controlled value and the value setter. + */ +export function useControlledValue< T >( { + defaultValue, + onChange, + value: valueProp, +}: Props< T > ) { + const hasValue = typeof valueProp !== 'undefined'; + const initialValue = hasValue ? valueProp : defaultValue; + const [ state, setState ] = useState( initialValue ); + const value = hasValue ? valueProp : state; + + const uncontrolledSetValue = useCallback( + ( nextValue: T, ...args: any[] ) => { + setState( nextValue ); + onChange?.( nextValue, ...args ); + }, + [ onChange ] + ); + + let setValue: typeof onChange; + if ( hasValue && typeof onChange === 'function' ) { + // Controlled mode. + setValue = onChange; + } else if ( ! hasValue && typeof onChange === 'function' ) { + // Uncontrolled mode, plus forwarding to the onChange prop. + setValue = uncontrolledSetValue; + } else { + // Uncontrolled mode, only update internal state. + setValue = setState; + } + + return [ value, setValue ] as const; +} diff --git a/packages/components/src/calendar/utils/use-localization-props.ts b/packages/dataviews/src/components/calendar/utils/use-localization-props.ts similarity index 100% rename from packages/components/src/calendar/utils/use-localization-props.ts rename to packages/dataviews/src/components/calendar/utils/use-localization-props.ts diff --git a/packages/dataviews/src/components/dataform-controls/date.tsx b/packages/dataviews/src/components/dataform-controls/date.tsx index bc3b24db3cc974..e7b3acd5d64484 100644 --- a/packages/dataviews/src/components/dataform-controls/date.tsx +++ b/packages/dataviews/src/components/dataform-controls/date.tsx @@ -19,7 +19,6 @@ import { BaseControl, Button, Icon as WCIcon, - privateApis as componentsPrivateApis, __experimentalInputControl as InputControl, } from '@wordpress/components'; import { speak } from '@wordpress/a11y'; @@ -45,7 +44,7 @@ import { OPERATOR_OVER, OPERATOR_BETWEEN, } from '../../constants'; -import { unlock } from '../../lock-unlock'; +import { DateCalendar, DateRangeCalendar } from '../calendar'; import type { DataFormControlProps, FieldValidity, @@ -54,8 +53,6 @@ import type { } from '../../types'; import getCustomValidity from './utils/get-custom-validity'; -const { DateCalendar, DateRangeCalendar } = unlock( componentsPrivateApis ); - type DateRange = [ string, string ] | undefined; const DATE_PRESETS: { diff --git a/packages/dataviews/src/components/dataform-controls/datetime.tsx b/packages/dataviews/src/components/dataform-controls/datetime.tsx index 3c8b5ff6227791..3ea2ef4cd277ae 100644 --- a/packages/dataviews/src/components/dataform-controls/datetime.tsx +++ b/packages/dataviews/src/components/dataform-controls/datetime.tsx @@ -19,9 +19,10 @@ import RelativeDateControl from './utils/relative-date-control'; import useDisabledDateMatchers from './utils/use-disabled-date-matchers'; import getCustomValidity from './utils/get-custom-validity'; import parseDateTime from '../../field-types/utils/parse-date-time'; +import { DateCalendar } from '../calendar'; import { unlock } from '../../lock-unlock'; -const { DateCalendar, ValidatedInputControl } = unlock( componentsPrivateApis ); +const { ValidatedInputControl } = unlock( componentsPrivateApis ); const formatDateTime = ( value?: string ): string => { if ( ! value ) { diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index 3ceb2935e1250f..bc307464db7c06 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -16,6 +16,7 @@ @use "./components/dataviews-layouts/picker-activity/style.scss" as *; @use "./components/dataviews-picker-footer/style.scss" as *; +@use "./components/calendar/style.scss" as *; @use "./components/dataform-controls/style.scss" as *; @use "./components/dataform-layouts/panel/style.scss" as *; @use "./components/dataform-layouts/regular/style.scss" as *;