diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js
index c6a8580b1..8dbcc3a12 100644
--- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/js/views-basic.js
@@ -75,6 +75,28 @@
displayElement.addEventListener('change', updateLimitElement);
updateLimitElement();
}
+
+ // Unified selectors to handle both cases
+ const entityTypesSelector = 'input[name="settings[block_form][group_user_selection][entity_and_view_mode][entity_types]"], input[name="block_form[group_user_selection][entity_and_view_mode][entity_types]"]';
+ const viewModeSelector = 'input[name="settings[block_form][group_user_selection][entity_and_view_mode][view_mode]"], input[name="block_form[group_user_selection][entity_and_view_mode][view_mode]"]';
+ const eventTimePeriod = document.querySelector('#edit-event-time-period');
+
+ const entityTypes = document.querySelectorAll(entityTypesSelector);
+ const viewModes = document.querySelectorAll(viewModeSelector);
+
+ // Function to handle visibility based on conditions
+ function updateVisibility() {
+ const entityType = Array.from(entityTypes).find(input => input.checked)?.value;
+ const viewMode = Array.from(viewModes).find(input => input.checked)?.value;
+
+ if (eventTimePeriod) {
+ eventTimePeriod.style.display = (entityType === 'event' && viewMode === 'calendar') ? 'none' : '';
+ }
+ }
+
+ entityTypes.forEach(input => input.addEventListener('change', updateVisibility));
+ viewModes.forEach(input => input.addEventListener('change', updateVisibility));
+ updateVisibility();
},
};
})(Drupal);
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Controller/EventsCalendarController.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Controller/EventsCalendarController.php
new file mode 100644
index 000000000..e43e8968d
--- /dev/null
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Controller/EventsCalendarController.php
@@ -0,0 +1,66 @@
+get('ys_views_basic.events_calendar'),
+ );
+ }
+
+ /**
+ * Builds the response.
+ */
+ public function __invoke(Request $request): AjaxResponse {
+ $response = new AjaxResponse();
+
+ if (!$request->request->has('calendar_id') || !$request->request->has('month') || !$request->request->has('year')) {
+ return $response;
+ }
+
+ // Calendar wrapper that needs to be updated.
+ $calendar_id = $request->request->get('calendar_id');
+ $month = $request->request->get('month');
+ $year = $request->request->get('year');
+
+ $events_calendar = $this->eventsCalendar
+ ->getCalendar($month, $year);
+
+ $calendar = [
+ '#theme' => 'views_basic_events_calendar',
+ '#month_data' => $events_calendar,
+ '#cache' => [
+ 'tags' => ['node_list:event'],
+ ],
+ ];
+
+ $response->addCommand(new ReplaceCommand($calendar_id, $calendar));
+
+ return $response;
+ }
+
+}
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php
index 3428c952e..56ffeb944 100644
--- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldFormatter/ViewsBasicDefaultFormatter.php
@@ -6,7 +6,7 @@
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Render\Renderer;
+use Drupal\ys_views_basic\Service\EventsCalendarInterface;
use Drupal\ys_views_basic\ViewsBasicManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -28,14 +28,14 @@ class ViewsBasicDefaultFormatter extends FormatterBase implements ContainerFacto
*
* @var \Drupal\ys_views_basic\ViewsBasicManager
*/
- protected $viewsBasicManager;
+ protected ViewsBasicManager $viewsBasicManager;
/**
- * The renderer service.
+ * The Events Calendar service.
*
- * @var \Drupal\Core\Render\Renderer
+ * @var \Drupal\ys_views_basic\Service\EventsCalendarInterface
*/
- protected $rendererService;
+ protected EventsCalendarInterface $eventsCalendar;
/**
* Constructs an views basic default formatter object.
@@ -54,10 +54,10 @@ class ViewsBasicDefaultFormatter extends FormatterBase implements ContainerFacto
* The view mode.
* @param array $third_party_settings
* Any third party settings.
- * @param \Drupal\ys_views_basic\Plugin\ViewsBasicManager $viewsBasicManager
+ * @param \Drupal\ys_views_basic\ViewsBasicManager $viewsBasicManager
* The views basic manager service.
- * @param \Drupal\Core\Render\Renderer $renderer_service
- * Drupal Core renderer service.
+ * @param \Drupal\ys_views_basic\Service\EventsCalendarInterface $eventsCalendar
+ * The Events Calendar service.
*/
public function __construct(
string $plugin_id,
@@ -68,7 +68,7 @@ public function __construct(
string $view_mode,
array $third_party_settings,
ViewsBasicManager $viewsBasicManager,
- Renderer $renderer_service,
+ EventsCalendarInterface $eventsCalendar,
) {
parent::__construct(
$plugin_id,
@@ -78,20 +78,15 @@ public function __construct(
$label,
$view_mode,
$third_party_settings,
- $this->rendererService = $renderer_service,
);
$this->viewsBasicManager = $viewsBasicManager;
+ $this->eventsCalendar = $eventsCalendar;
}
/**
* {@inheritdoc}
*/
- public static function create(
- ContainerInterface $container,
- array $configuration,
- $plugin_id,
- $plugin_definition,
- ) {
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
@@ -101,33 +96,57 @@ public static function create(
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('ys_views_basic.views_basic_manager'),
- $container->get('renderer'),
+ $container->get('ys_views_basic.events_calendar')
);
}
/**
* Define how the field type is showed.
*/
- public function viewElements(FieldItemListInterface $items, $langcode) {
+ public function viewElements(FieldItemListInterface $items, $langcode): array {
$elements = [];
+
foreach ($items as $delta => $item) {
+ // Get decoded parameters.
+ $paramsDecoded = json_decode($item->getValue()['params'], TRUE);
+
+ if ($paramsDecoded['filters']['types'][0] === 'event' && $paramsDecoded['view_mode'] === 'calendar') {
+ // Calculate the remaining time until the end of the current month.
+ $now = new \DateTime();
+ $end_of_month = new \DateTime('last day of this month 23:59:59');
+ $remaining_time_in_seconds = $end_of_month->getTimestamp() - $now->getTimestamp();
- $view = $this->viewsBasicManager->getView('rendered', $item->getValue()['params']);
+ $events_calendar = $this->eventsCalendar
+ ->getCalendar(date('m'), date('Y'));
- $elements[$delta] = [
- '#theme' => 'views_basic_formatter_default',
- '#view' => $view,
- // Extract exposed filters from the view and place them separately.
- // This is necessary because we are conditionally displaying
- // specific exposed filters based on field configuration.
- // By placing the exposed filters outside of the view rendering
- // context, we ensure that they do not get re-rendered
- // when AJAX operations are performed on the view,
- // allowing for better control over which filters are displayed
- // and maintaining the expected user interface behavior.
- '#exposed' => $view['#view']->exposed_widgets,
- ];
+ $elements[$delta] = [
+ '#theme' => 'views_basic_events_calendar',
+ '#month_data' => $events_calendar,
+ '#cache' => [
+ 'tags' => ['node_list:event'],
+ // Set max-age to the remaining time until the end of the month.
+ 'max-age' => $remaining_time_in_seconds,
+ 'contexts' => ['timezone'],
+ ],
+ ];
+ }
+ else {
+ $view = $this->viewsBasicManager->getView('rendered', $item->getValue()['params']);
+ $elements[$delta] = [
+ '#theme' => 'views_basic_formatter_default',
+ '#view' => $view,
+ // Extract exposed filters from the view and place them separately.
+ // This is necessary because we are conditionally displaying
+ // specific exposed filters based on field configuration.
+ // By placing the exposed filters outside of the view rendering
+ // context, we ensure that they do not get re-rendered
+ // when AJAX operations are performed on the view,
+ // allowing for better control over which filters are displayed
+ // and maintaining the expected user interface behavior.
+ '#exposed' => $view['#view']->exposed_widgets,
+ ];
+ }
}
return $elements;
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php
index 468fbe71d..7680c5f2d 100644
--- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Plugin/Field/FieldWidget/ViewsBasicDefaultWidget.php
@@ -219,6 +219,11 @@ public function formElement(
'#suffix' => '',
];
+ // Define the state for when the view mode is 'calendar' once.
+ $calendarViewInvisibleState = [
+ $formSelectors['view_mode_input_selector'] => ['value' => 'calendar'],
+ ];
+
$fieldOptionValue = ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('field_options', $items[$delta]->params) : [];
$fieldOptionDefaultValue = $fieldOptionValue ?? ['show_thumbnail' => 'show_thumbnail'];
$isNewForm = str_contains($formState->getCompleteForm()['#id'], 'layout-builder-add-block');
@@ -234,6 +239,7 @@ public function formElement(
'#title' => $this->t('Field Display Options'),
'#tree' => TRUE,
'#default_value' => ($isNewForm && empty($fieldOptionValue)) ? ['show_thumbnail'] : $fieldOptionDefaultValue,
+ '#states' => ['invisible' => $calendarViewInvisibleState],
'show_thumbnail' => [
'#states' => [
'visible' => [
@@ -256,14 +262,9 @@ public function formElement(
'#title' => $this->t('Exposed Filter Options'),
'#tree' => TRUE,
'#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('exposed_filter_options', $items[$delta]->params) : [],
+ '#states' => ['invisible' => $calendarViewInvisibleState],
'show_year_filter' => [
- '#states' => [
- 'visible' => [
- $formSelectors['entity_types_ajax'] => [
- 'value' => 'post',
- ],
- ],
- ],
+ '#states' => ['visible' => [$formSelectors['entity_types_ajax'] => ['value' => 'post']]],
],
];
@@ -273,9 +274,8 @@ public function formElement(
'#description' => $this->t("Enter a custom label for the Category Filter. This label will be displayed to users as the filter's name. If left blank, the default label Category will be used."),
'#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('category_filter_label', $items[$delta]->params) : NULL,
'#states' => [
- 'visible' => [
- $formSelectors['show_category_filter_selector'] => ['checked' => TRUE],
- ],
+ 'visible' => [$formSelectors['show_category_filter_selector'] => ['checked' => TRUE]],
+ 'invisible' => $calendarViewInvisibleState,
],
];
@@ -291,9 +291,8 @@ public function formElement(
'#prefix' => '
',
'#suffix' => '
',
'#states' => [
- 'visible' => [
- $formSelectors['show_category_filter_selector'] => ['checked' => TRUE],
- ],
+ 'visible' => [$formSelectors['show_category_filter_selector'] => ['checked' => TRUE]],
+ 'invisible' => $calendarViewInvisibleState,
],
];
@@ -306,6 +305,7 @@ public function formElement(
'#tags' => TRUE,
'#target_type' => 'taxonomy_term',
'#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('terms_include', $items[$delta]->params) : [],
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
$form['group_user_selection']['filter_and_sort']['terms_exclude'] = [
@@ -317,6 +317,7 @@ public function formElement(
'#tags' => TRUE,
'#target_type' => 'taxonomy_term',
'#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('terms_exclude', $items[$delta]->params) : [],
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
$form['group_user_selection']['filter_and_sort']['term_operator'] = [
@@ -333,7 +334,7 @@ public function formElement(
'term-operator-item',
],
],
-
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
// Gets the view mode options based on Ajax callbacks or initial load.
@@ -349,6 +350,7 @@ public function formElement(
'#validated' => 'true',
'#prefix' => '',
'#suffix' => '
',
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
$form['group_user_selection']['entity_specific']['event_time_period'] = [
@@ -360,13 +362,8 @@ public function formElement(
'all' => $this->t('All Events') . '',
],
'#default_value' => ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('event_time_period', $items[$delta]->params) : 'future',
- '#states' => [
- 'visible' => [
- $formSelectors['entity_types_ajax'] => [
- 'value' => 'event',
- ],
- ],
- ],
+ '#prefix' => '',
+ '#suffix' => '
',
];
$displayValue = ($items[$delta]->params) ? $this->viewsBasicManager->getDefaultParamValue('display', $items[$delta]->params) : 'all';
@@ -381,6 +378,7 @@ public function formElement(
'limit' => $this->t('Limit to'),
'pager' => $this->t('Pagination after'),
],
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
$limitTitle = $this->t('Items');
@@ -401,6 +399,7 @@ public function formElement(
'#required' => TRUE,
'#prefix' => '',
'#suffix' => '
',
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
$form['group_user_selection']['options']['offset'] = [
@@ -412,6 +411,7 @@ public function formElement(
'#attributes' => [
'placeholder' => 0,
],
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
$element['group_params']['params'] = [
@@ -424,6 +424,7 @@ public function formElement(
'views-basic--params',
],
],
+ '#states' => ['invisible' => $calendarViewInvisibleState],
];
$form['#attached']['library'][] = 'ys_views_basic/ys_views_basic';
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php
new file mode 100644
index 000000000..9f54e5584
--- /dev/null
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendar.php
@@ -0,0 +1,280 @@
+entityTypeManager = $entity_type_manager;
+ $this->aliasManager = $alias_manager;
+ $this->nodeStorage = $entity_type_manager->getStorage('node');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container): static {
+ return new static(
+ $container->get('entity_type.manager'),
+ $container->get('path_alias.manager')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCalendar(string $month, string $year): array {
+ // Create a date object for the first day of the given month and year.
+ $firstDayOfMonth = new DrupalDateTime("$year-$month-01");
+ $totalDaysInMonth = (int) $firstDayOfMonth->format('t');
+ $startDayOfWeek = (int) $firstDayOfMonth->format('w');
+ $lastDayOfMonth = new DrupalDateTime("$year-$month-$totalDaysInMonth");
+ $endDayOfWeek = (int) $lastDayOfMonth->format('w');
+
+ $paddingStart = $startDayOfWeek;
+ $paddingEnd = 6 - $endDayOfWeek;
+ $totalCells = $totalDaysInMonth + $paddingStart + $paddingEnd;
+ $totalRows = (int) ceil($totalCells / 7);
+ $calendarRows = [];
+
+ // Calculate the previous month and year.
+ $previousMonthDate = clone $firstDayOfMonth;
+ $previousMonthDate->modify('-1 month');
+ $daysInPreviousMonth = (int) $previousMonthDate->format('t');
+ $previousMonth = $previousMonthDate->format('m');
+ $previousYear = $previousMonthDate->format('Y');
+
+ // Calculate the next month and year.
+ $nextMonthDate = clone $lastDayOfMonth;
+ $nextMonthDate->modify('+1 month');
+ $nextMonth = $nextMonthDate->format('m');
+ $nextYear = $nextMonthDate->format('Y');
+
+ // Load all events for the given month and year.
+ $monthlyEvents = $this->loadMonthlyEvents($month, $year);
+
+ $currentDay = 1;
+
+ for ($row = 0; $row < $totalRows; $row++) {
+ $calendarRows[$row] = [];
+ for ($cell = 0; $cell < 7; $cell++) {
+ if ($row == 0 && $cell < $paddingStart) {
+ // Fill in days from the previous month.
+ $day = $daysInPreviousMonth - ($paddingStart - $cell - 1);
+ $calendarRows[$row][] = $this->createCalendarCell($day, $previousMonth, $previousYear, $monthlyEvents);
+ }
+ elseif ($row == $totalRows - 1 && $cell > $endDayOfWeek) {
+ // Fill in days from the next month.
+ $day = $cell - $endDayOfWeek;
+ $calendarRows[$row][] = $this->createCalendarCell($day, $nextMonth, $nextYear, $monthlyEvents);
+ }
+ else {
+ // Normal date cell within the current month.
+ $calendarRows[$row][] = $this->createCalendarCell($currentDay, $month, $year, $monthlyEvents);
+ $currentDay++;
+ }
+ }
+ }
+
+ return $calendarRows;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createCalendarCell(int $day, string $month, string $year, array $events): array {
+ return [
+ 'date' => [
+ 'day' => str_pad($day, 2, '0', STR_PAD_LEFT),
+ 'month' => $month,
+ 'year' => $year,
+ ],
+ 'events' => $this->getEvents($day, $month, $year, $events),
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getEvents(int $day, string $month, string $year, array $events): array {
+ $startDate = new DrupalDateTime("$year-$month-$day 00:00:00");
+ $endDate = new DrupalDateTime("$year-$month-$day 23:59:59");
+
+ $startTimestamp = $startDate->getTimestamp();
+ $endTimestamp = $endDate->getTimestamp();
+
+ $events_data = [];
+ foreach ($events as $event) {
+ if (!$event->get('field_event_date')->isEmpty()) {
+ // Handle recurrence rules if present.
+ if ($event->field_event_date?->rrule) {
+
+ /** @var \Drupal\smart_date_recur\Entity\SmartDateRule $rule */
+ $rule = $this->entityTypeManager->getStorage('smart_date_rule')
+ ->load($event->field_event_date->rrule);
+
+ if ($rule instanceof SmartDateRule) {
+ // Iterate over the stored instances to find occurrences for the
+ // current day.
+ foreach ($rule->getStoredInstances() as $instance) {
+ $instanceStartTimestamp = $instance['value'];
+ $instanceEndTimestamp = $instance['end_value'];
+
+ // Check if the instance overlaps with the current day.
+ if ($instanceStartTimestamp <= $endTimestamp && $instanceEndTimestamp >= $startTimestamp) {
+ $time = $this->isAllDay($instanceStartTimestamp, $instanceEndTimestamp)
+ ? $this->t('All Day')
+ : $this->t('@start to @end', [
+ '@start' => date('g:iA', $instanceStartTimestamp),
+ '@end' => date('g:iA', $instanceEndTimestamp),
+ ]);
+
+ $events_data[] = $this->createEventArray($event, $time, $instanceStartTimestamp);
+ }
+ }
+ }
+ }
+ else {
+ // Iterate through the nodes to extract event details.
+ foreach ($event->get('field_event_date')->getValue() as $eventDate) {
+ $eventStartTimestamp = $eventDate['value'];
+ $eventEndTimestamp = $eventDate['end_value'];
+
+ // Check if the event overlaps with the current day.
+ if ($eventStartTimestamp <= $endTimestamp && $eventEndTimestamp >= $startTimestamp) {
+ if (date('Y-m-d', $eventStartTimestamp) !== date('Y-m-d', $eventEndTimestamp)) {
+ $time = $this->t('Multi-day Event');
+ }
+ else {
+ $time = $this->isAllDay($eventStartTimestamp, $eventEndTimestamp)
+ ? $this->t('All Day')
+ : $this->t('@start to @end', [
+ '@start' => date('g:iA', $eventStartTimestamp),
+ '@end' => date('g:iA', $eventEndTimestamp),
+ ]);
+ }
+
+ // Add event to the list if it overlaps with the current day.
+ $events_data[] = $this->createEventArray($event, $time, $eventStartTimestamp);
+ }
+ }
+ }
+ }
+ }
+
+ // Sort events by the start timestamp.
+ usort($events_data, function ($a, $b) {
+ return $a['timestamp'] <=> $b['timestamp'];
+ });
+
+ return $events_data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createEventArray($node, string $time, int $timestamp): array {
+ // Extract the event's categories.
+ $categories = implode(' | ', array_map(function ($term) {
+ return $term->label();
+ }, $node->get('field_category')->referencedEntities()));
+
+ // Extract the event's tags.
+ $tags = array_map(fn($term) => $term->label(), $node->get('field_tags')->referencedEntities());
+
+ // Build and return the event array.
+ return [
+ 'category' => $categories,
+ 'title' => $node->label(),
+ 'url' => $this->aliasManager->getAliasByPath('/node/' . $node->id()),
+ 'time' => $time,
+ 'type' => $tags,
+ 'timestamp' => $timestamp,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isAllDay(int $start_ts, int $end_ts, ?string $timezone = NULL): bool {
+ if ($timezone) {
+ $default_tz = date_default_timezone_get();
+ date_default_timezone_set($timezone);
+ }
+
+ $temp_start = date('H:i', $start_ts);
+ $temp_end = date('H:i', $end_ts);
+
+ if ($timezone) {
+ date_default_timezone_set($default_tz);
+ }
+
+ return $temp_start == '00:00' && $temp_end == '23:59';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadMonthlyEvents(string $month, string $year): array {
+ $startDate = new DrupalDateTime("$year-$month-01 00:00:00");
+ $endDate = clone $startDate;
+ $endDate->modify('last day of this month 23:59:59');
+
+ // Query to fetch event nodes that overlap with the given month.
+ $query = $this->nodeStorage->getQuery()
+ ->accessCheck()
+ ->condition('type', 'event')
+ ->condition('status', 1)
+ ->condition('field_event_date.value', $endDate->getTimestamp(), '<=')
+ ->condition('field_event_date.end_value', $startDate->getTimestamp(), '>=');
+
+ $nids = $query->execute();
+ return $this->nodeStorage->loadMultiple($nids);
+ }
+
+}
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php
new file mode 100644
index 000000000..4c4640478
--- /dev/null
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/src/Service/EventsCalendarInterface.php
@@ -0,0 +1,102 @@
+ '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/display-type-condensed.svg',
'img_alt' => 'Icon showing 3 generic list items one on top of the other with no images on the items.',
],
+ 'calendar' => [
+ 'label' => 'Calendar',
+ 'img' => '/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/assets/icons/content-type-event.svg',
+ 'img_alt' => 'Calendar',
+ ],
],
'sort_by' => [
'field_event_date:DESC' => 'Event Date - newer first',
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/templates/views-basic-events-calendar.html.twig b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/templates/views-basic-events-calendar.html.twig
new file mode 100644
index 000000000..7d089c2a1
--- /dev/null
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/templates/views-basic-events-calendar.html.twig
@@ -0,0 +1,5 @@
+{{ attach_library('atomic/calendar') }}
+
+{% include "@organisms/calendar/yds-calendar.twig" with {
+ month:month_data
+} %}
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module
index a58c13d94..70ad89fb5 100644
--- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.module
@@ -32,6 +32,11 @@ function ys_views_basic_theme($existing, $type, $theme, $path): array {
'contentType' => NULL,
],
],
+ 'views_basic_events_calendar' => [
+ 'variables' => [
+ 'month_data' => [],
+ ],
+ ],
];
}
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.routing.yml b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.routing.yml
new file mode 100644
index 000000000..818b81880
--- /dev/null
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.routing.yml
@@ -0,0 +1,7 @@
+ys_views_basic.events_calendar:
+ path: '/events-calendar'
+ defaults:
+ _title: 'Events Calendar'
+ _controller: '\Drupal\ys_views_basic\Controller\EventsCalendarController'
+ requirements:
+ _permission: 'access content'
diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml
index 4039bf8a2..0a37a9f47 100644
--- a/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml
+++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_views_basic/ys_views_basic.services.yml
@@ -3,3 +3,7 @@ services:
ys_views_basic.views_basic_manager:
class: Drupal\ys_views_basic\ViewsBasicManager
arguments: ['@entity_type.manager', '@entity_display.repository']
+
+ ys_views_basic.events_calendar:
+ class: Drupal\ys_views_basic\Service\EventsCalendar
+ arguments: ['@entity_type.manager', '@path_alias.manager']