Repository: obsidian-tasks-group/obsidian-tasks (ts)

Issue:
Add a date-picker/calendar button to the Postpone menu - and/or other more convenient locations
### ⚠️ Please check that this feature request hasn't been suggested before.

- [X] I searched previous [Ideas in Discussions](https://github.com/obsidian-tasks-group/obsidian-tasks/discussions/categories/ideas-any-new-feature-requests-go-in-issues-please?discussions_q=category%3A%22Ideas%3A+Any+New+Feature+Requests+go+in+Issues+please%22+-label%3A%22status%3A+released%22+-label%3Aduplicate+sort%3Atop) didn't find any similar feature requests.
- [X] I searched previous [Issues](https://github.com/obsidian-tasks-group/obsidian-tasks/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+enhancement%22+sort%3Acomments-desc) didn't find any similar feature requests.
- [X] I am only requesting a single feature. Multiple changes should be split into individual requests, with links between them.
- [X] I believe my requested feature will be generally applicable to other users, not just me: it is not uniquely tied to my personal workflow.

### 🔖 Feature description

The Postpone feature is useful, but I often want to chose dates other than those in the Postpone menu.




### ✔️ Solution

This request is to add a date-picker/calendar button to the Postpone menu.

Once #2648 is implemented, this will also add a date-picker/calendar icon to the context menus on the date emojis in Tasks query results too.


### ❓ Alternatives

Keep adding lots more dates to the Postpone menu.

### 📝 Additional Context

_No response_

Pull request:
feat: Add date-picker to Reading Mode and Tasks query results
# Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

Changes visible to users:

- [x] **New feature** (prefix: `feat` - non-breaking change which adds functionality)
  - Issue/discussion: #2649
- [x] **Sample vault** (prefix: `vault` - improvements to the [Tasks-Demo sample vault](https://github.com/obsidian-tasks-group/obsidian-tasks/tree/main/resources/sample_vaults/Tasks-Demo))

## Description

Add a **calendar date-picker** when user left-clicks on date values in **Reading Mode** and **Tasks search results**.
<!--- Remember to provide a general summary of your changes in the Title above -->

<!--- Describe your changes in detail -->

## Motivation and Context

Fix #2649.

## How has this been tested?

- Exploratory testing
- Running the newly reinstated smoke tests for editing of dates
- I've been using virtually identical code in my own vault for months. This code differs in having new Clear and Today buttons, and working correctly on mobile.

## Screenshots (if appropriate)

Demo:

https://github.com/user-attachments/assets/0e386af5-8db5-40c7-87ff-466608855753

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change requires a change to the documentation.
- [ ] I have [updated the documentation](https://publish.obsidian.md/tasks-contributing/Documentation/About+Documentation) accordingly.
- [ ] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

<!--
By submitting this pull request, you must agree to follow our
[contributing guide](https://publish.obsidian.md/tasks-contributing) and
[Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md).
Put an x in the boxes to confirm you agree.
-->

- [x] My contribution follows this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)


Patch:
diff --git a/src/Renderer/TaskLineRenderer.ts b/src/Renderer/TaskLineRenderer.ts
index c0854f70..4cfe09e2 100644
--- a/src/Renderer/TaskLineRenderer.ts
+++ b/src/Renderer/TaskLineRenderer.ts
@@ -11,6 +11,7 @@ import { TaskRegularExpressions } from '../Task/TaskRegularExpressions';
 import { StatusMenu } from '../ui/Menus/StatusMenu';
 import type { AllTaskDateFields } from '../DateTime/DateFieldTypes';
 import { defaultTaskSaver } from '../ui/Menus/TaskEditingMenu';
+import { promptForDate } from '../ui/Menus/DatePicker';
 import { splitDateText } from '../DateTime/Postponer';
 import { DateMenu } from '../ui/Menus/DateMenu';
 import { TaskFieldRenderer } from './TaskFieldRenderer';
@@ -215,11 +216,11 @@ export class TaskLineRenderer {
                     const componentDateField = component as AllTaskDateFields;
 
                     // Note: The more convenient span.onClickEvent() doesn't work here, as it is not available when tests are run.
-                    // span.addEventListener('click', (ev: MouseEvent) => {
-                    //     ev.preventDefault(); // suppress the default click behavior
-                    //     ev.stopPropagation(); // suppress further event propagation
-                    //     promptForDate(span, task, componentDateField, defaultTaskSaver);
-                    // });
+                    span.addEventListener('click', (ev: MouseEvent) => {
+                        ev.preventDefault(); // suppress the default click behavior
+                        ev.stopPropagation(); // suppress further event propagation
+                        promptForDate(span, task, componentDateField, defaultTaskSaver);
+                    });
 
                     span.addEventListener('contextmenu', (ev: MouseEvent) => {
                         ev.preventDefault(); // suppress the default context menu
@@ -229,8 +230,7 @@ export class TaskLineRenderer {
                     });
                     span.setAttribute(
                         'title',
-                        // `Click to edit ${splitDateText(componentDateField)}, Right-click for more options`,
-                        `Right-click to edit ${splitDateText(componentDateField)}`,
+                        `Click to edit ${splitDateText(componentDateField)}, Right-click for more options`,
                     );
                 }
             }
diff --git a/src/ui/Menus/DatePicker.ts b/src/ui/Menus/DatePicker.ts
index 9919b3d8..aa8c2297 100644
--- a/src/ui/Menus/DatePicker.ts
+++ b/src/ui/Menus/DatePicker.ts
@@ -1,29 +1,28 @@
 import flatpickr from 'flatpickr';
 import type { Task } from '../../Task/Task';
-import { SetTaskDate } from '../EditInstructions/DateInstructions';
+import { RemoveTaskDate, SetTaskDate } from '../EditInstructions/DateInstructions';
 import type { AllTaskDateFields } from '../../DateTime/DateFieldTypes';
+import type { TaskSaver } from './TaskEditingMenu';
 
 /**
  * A calendar date picker which edits a date value in a {@link Task} object.
- * See also {@link openDatePicker}
  * @param parentElement
  * @param task
  * @param dateFieldToEdit
  * @param taskSaver
- *
- * See also {@link openDatePicker}
  */
 export function promptForDate(
     parentElement: HTMLElement,
     task: Task,
     dateFieldToEdit: AllTaskDateFields,
-    taskSaver: (originalTask: Task, newTasks: Task | Task[]) => Promise<void>,
+    taskSaver: TaskSaver,
 ) {
     const currentValue = task[dateFieldToEdit];
     // TODO figure out how Today's date is determined: if Obsidian is left
     //      running overnight, the flatpickr modal shows the previous day as Today.
     const fp = flatpickr(parentElement, {
         defaultDate: currentValue ? currentValue.format('YYYY-MM-DD') : new Date(),
+        disableMobile: true,
         enableTime: false, // Optional: Enable time picker
         dateFormat: 'Y-m-d', // Adjust the date and time format as needed
         locale: {
@@ -35,10 +34,53 @@ export function promptForDate(
                 const newTask = new SetTaskDate(dateFieldToEdit, date).apply(task);
                 await taskSaver(task, newTask);
             }
-            instance.destroy(); // Proper cleanup
+            instance.destroy();
+        },
+        onReady: (_selectedDates, _dateStr, instance) => {
+            // Add custom buttons dynamically
+            const buttonContainer = document.createElement('div');
+            buttonContainer.style.display = 'flex';
+            buttonContainer.style.justifyContent = 'space-between';
+            buttonContainer.style.marginTop = '10px';
+
+            // Create "Clear" button
+            addButton(buttonContainer, instance, task, taskSaver, 'Clear', () => {
+                return new RemoveTaskDate(dateFieldToEdit, task).apply(task);
+            });
+
+            // Create "Today" button
+            addButton(buttonContainer, instance, task, taskSaver, 'Today', () => {
+                const today = new Date();
+                return new SetTaskDate(dateFieldToEdit, today).apply(task);
+            });
+
+            // Append the button container to the Flatpickr calendar container
+            const calendarContainer = instance.calendarContainer;
+            calendarContainer.appendChild(buttonContainer);
         },
     });
 
     // Open the calendar programmatically
     fp.open();
 }
+
+function addButton(
+    buttonContainer: HTMLDivElement,
+    instance: flatpickr.Instance,
+    task: Task,
+    taskSaver: TaskSaver,
+    buttonName: string,
+    applyDate: () => Task[],
+) {
+    const button = document.createElement('button');
+    button.type = 'button';
+    button.textContent = buttonName;
+    button.classList.add('flatpickr-button');
+
+    button.addEventListener('click', async () => {
+        const newTask = applyDate();
+        await taskSaver(task, newTask);
+        instance.destroy();
+    });
+    buttonContainer.appendChild(button);
+}


Test patch:
diff --git a/resources/sample_vaults/Tasks-Demo/Manual Testing/Smoke Testing the Tasks Plugin.md b/resources/sample_vaults/Tasks-Demo/Manual Testing/Smoke Testing the Tasks Plugin.md
index a2139269..6eb4916e 100644
--- a/resources/sample_vaults/Tasks-Demo/Manual Testing/Smoke Testing the Tasks Plugin.md
+++ b/resources/sample_vaults/Tasks-Demo/Manual Testing/Smoke Testing the Tasks Plugin.md
@@ -149,13 +149,13 @@ heading includes Rendering of Task Blocks
 
 - View this file in **Reading mode**...
 - On the task line above:
-  - [ ] #task ~~**left**-click on a date value, and use the date picker to select and save a different date. Check that the date is updated.~~
-  - [ ] #task ~~**left**-click on a date value, and click outside the date picker, to confirm that the picker closes.~~
+  - [ ] #task **left**-click on a date value, and use the date picker to select and save a different date. Check that the date is updated.
+  - [ ] #task **left**-click on a date value, and click outside the date picker, to confirm that the picker closes.
   - [ ] #task **right**-click on a date value, and use the context menu to select and save a different date. Check that the date is updated.
   - [ ] #task **right**-click on a date value, and click outside the context menu, to confirm that the menu closes.
 - In the tasks search block below:
-  - [ ] #task ~~**left**-click on a date value, and use the date picker to select and save a different date. Check that the date is updated.~~
-  - [ ] #task ~~**left**-click on a date value, and click outside the date picker, to confirm that the picker closes.~~
+  - [ ] #task **left**-click on a date value, and use the date picker to select and save a different date. Check that the date is updated.
+  - [ ] #task **left**-click on a date value, and click outside the date picker, to confirm that the picker closes.
   - [ ] #task **right**-click on a date value, and use the context menu to select and save a different date. Check that the date is updated.
   - [ ] #task **right**-click on a date value, and click outside the context menu, to confirm that the menu closes.
 - [ ] #task **check**: Checked all above steps for **editing dates** worked
@@ -177,8 +177,8 @@ hide postpone button
     2. **Check** that the text in the list item is copied in to the Description field
     3. Type some values in to the fields
     4. In one of the date fields, type `tm` (including the space afterwards) and **Check** it is expanded in to `tomorrow`
-    5. ~~In one of the date fields, left-click the calendar button, and use the context menu to select and save a date. Check that the date is saved.~~
-    6. ~~In one of the date fields, left-click the calendar button, and click outside the date picker, to confirm that the picker closes and the modal is still usable.~~
+    5. In one of the date fields, left-click the calendar button, and use the context menu to select and save a date. Check that the date is saved.
+    6. In one of the date fields, left-click the calendar button, and click outside the date picker, to confirm that the picker closes and the modal is still usable.
     7. Hit Return or click **Apply**
     8. **Check** that the list item above is converted in to a task
     9. **Check** that values you entered in the modal have been copied in to the list item above
diff --git a/tests/Renderer/QueryResultsRenderer.test.QueryResultsRenderer_tests_fully_populated_task.approved.html b/tests/Renderer/QueryResultsRenderer.test.QueryResultsRenderer_tests_fully_populated_task.approved.html
index 8693b924..46a552e3 100644
--- a/tests/Renderer/QueryResultsRenderer.test.QueryResultsRenderer_tests_fully_populated_task.approved.html
+++ b/tests/Renderer/QueryResultsRenderer.test.QueryResultsRenderer_tests_fully_populated_task.approved.html
@@ -21,22 +21,34 @@
         <span class="task-priority" data-task-priority="medium"><span>🔼</span></span>
         <span class="task-recurring"><span>🔁 every day when done</span></span>
         <span class="task-onCompletion"><span>🏁 delete</span></span>
-        <span class="task-created" data-task-created="past-far" title="Right-click to edit created date">
+        <span
+          class="task-created"
+          data-task-created="past-far"
+          title="Click to edit created date, Right-click for more options">
           <span>➕ 2023-07-01</span>
         </span>
-        <span class="task-start" data-task-start="past-far" title="Right-click to edit start date">
+        <span
+          class="task-start"
+          data-task-start="past-far"
+          title="Click to edit start date, Right-click for more options">
           <span>🛫 2023-07-02</span>
         </span>
-        <span class="task-scheduled" data-task-scheduled="past-far" title="Right-click to edit scheduled date">
+        <span
+          class="task-scheduled"
+          data-task-scheduled="past-far"
+          title="Click to edit scheduled date, Right-click for more options">
           <span>⏳ 2023-07-03</span>
         </span>
-        <span class="task-due" data-task-due="past-far" title="Right-click to edit due date">
+        <span class="task-due" data-task-due="past-far" title="Click to edit due date, Right-click for more options">
           <span>📅 2023-07-04</span>
         </span>
-        <span class="task-cancelled" data-task-cancelled="past-far" title="Right-click to edit cancelled date">
+        <span
+          class="task-cancelled"
+          data-task-cancelled="past-far"
+          title="Click to edit cancelled date, Right-click for more options">
           <span>❌ 2023-07-06</span>
         </span>
-        <span class="task-done" data-task-done="past-far" title="Right-click to edit done date">
+        <span class="task-done" data-task-done="past-far" title="Click to edit done date, Right-click for more options">
           <span>✅ 2023-07-05</span>
         </span>
         <span class="task-block-link"><span>^dcf64c</span></span>
diff --git a/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_full_mode.approved.html b/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_full_mode.approved.html
index 1d3200fe..5360594f 100644
--- a/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_full_mode.approved.html
+++ b/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_full_mode.approved.html
@@ -23,22 +23,31 @@
     <span class="task-priority" data-task-priority="medium"><span>🔼</span></span>
     <span class="task-recurring"><span>🔁 every day when done</span></span>
     <span class="task-onCompletion"><span>🏁 delete</span></span>
-    <span class="task-created" data-task-created="past-4d" title="Right-click to edit created date">
+    <span
+      class="task-created"
+      data-task-created="past-4d"
+      title="Click to edit created date, Right-click for more options">
       <span>➕ 2023-07-01</span>
     </span>
-    <span class="task-start" data-task-start="past-3d" title="Right-click to edit start date">
+    <span class="task-start" data-task-start="past-3d" title="Click to edit start date, Right-click for more options">
       <span>🛫 2023-07-02</span>
     </span>
-    <span class="task-scheduled" data-task-scheduled="past-2d" title="Right-click to edit scheduled date">
+    <span
+      class="task-scheduled"
+      data-task-scheduled="past-2d"
+      title="Click to edit scheduled date, Right-click for more options">
       <span>⏳ 2023-07-03</span>
     </span>
-    <span class="task-due" data-task-due="past-1d" title="Right-click to edit due date">
+    <span class="task-due" data-task-due="past-1d" title="Click to edit due date, Right-click for more options">
       <span>📅 2023-07-04</span>
     </span>
-    <span class="task-cancelled" data-task-cancelled="future-1d" title="Right-click to edit cancelled date">
+    <span
+      class="task-cancelled"
+      data-task-cancelled="future-1d"
+      title="Click to edit cancelled date, Right-click for more options">
       <span>❌ 2023-07-06</span>
     </span>
-    <span class="task-done" data-task-done="today" title="Right-click to edit done date">
+    <span class="task-done" data-task-done="today" title="Click to edit done date, Right-click for more options">
       <span>✅ 2023-07-05</span>
     </span>
     <span class="task-block-link"><span>^dcf64c</span></span>
diff --git a/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_short_mode.approved.html b/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_short_mode.approved.html
index cf2ac965..378b57b8 100644
--- a/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_short_mode.approved.html
+++ b/tests/Renderer/TaskLineRenderer.test.Visualise_HTML_Full_task_-_short_mode.approved.html
@@ -23,18 +23,33 @@
     <span class="task-priority" data-task-priority="medium"><span>🔼</span></span>
     <span class="task-recurring"><span>🔁</span></span>
     <span class="task-onCompletion"><span>🏁</span></span>
-    <span class="task-created" data-task-created="past-4d" title="Right-click to edit created date">
+    <span
+      class="task-created"
+      data-task-created="past-4d"
+      title="Click to edit created date, Right-click for more options">
       <span>➕</span>
     </span>
-    <span class="task-start" data-task-start="past-3d" title="Right-click to edit start date"><span>🛫</span></span>
-    <span class="task-scheduled" data-task-scheduled="past-2d" title="Right-click to edit scheduled date">
+    <span class="task-start" data-task-start="past-3d" title="Click to edit start date, Right-click for more options">
+      <span>🛫</span>
+    </span>
+    <span
+      class="task-scheduled"
+      data-task-scheduled="past-2d"
+      title="Click to edit scheduled date, Right-click for more options">
       <span>⏳</span>
     </span>
-    <span class="task-due" data-task-due="past-1d" title="Right-click to edit due date"><span>📅</span></span>
-    <span class="task-cancelled" data-task-cancelled="future-1d" title="Right-click to edit cancelled date">
+    <span class="task-due" data-task-due="past-1d" title="Click to edit due date, Right-click for more options">
+      <span>📅</span>
+    </span>
+    <span
+      class="task-cancelled"
+      data-task-cancelled="future-1d"
+      title="Click to edit cancelled date, Right-click for more options">
       <span>❌</span>
     </span>
-    <span class="task-done" data-task-done="today" title="Right-click to edit done date"><span>✅</span></span>
+    <span class="task-done" data-task-done="today" title="Click to edit done date, Right-click for more options">
+      <span>✅</span>
+    </span>
     <span class="task-block-link"><span>^dcf64c</span></span>
   </span>
 </li>