[IMP] todo: make the name of a to-do editable from the breadcrumbs

In the module note, the name of a note was automatically computed based
on the begining of its content. Fot the module todo, we want the name to
be editable in the form view, while still keeping a clean interface for
the form. This commit makes therefore the name of a to-do directly
editable in the breadcrumbs. The behavior is a mix between the editable
breadcrumb name in Spreadsheet and the classical edition behavior for a
field in a form view, i.e:
- When creating a new to-do, a placeholder for the name is set to
  _Untitled to-do_. A name must be entered before saving the record.
- A name entered in the Breadcrumb will be saved either if the save
  button is pressed, either when the form view is quit.
- The discard button allows to go back to the last saved name.

The implementation relies, for its biggest part, on a new component
inheriting _CharField_ that copies some functions from the component
_SpreadsheetName_ used in Spreadsheet (see task-2621549) for the edition
part and use the autoResize hook from discuss recently moved into the
framework (see https://github.com/odoo/odoo/pull/117355).

The rest of the implementation consists of overrides of
_FormControlPanel_, _FormController_ and _formView_ to adapt the use of
this input field in the context of a form view (>< Spreadsheet).

task-3085077
This commit is contained in:
Hugo Carlier (Huca)
2023-05-15 09:01:27 +02:00
parent bc8332808f
commit 3e9512b57c
10 changed files with 176 additions and 0 deletions
+1
View File
@@ -23,6 +23,7 @@
'uninstall_hook': '_todo_uninstall',
'assets': {
'web.assets_backend': [
'todo/static/src/components/**/*',
'todo/static/src/scss/todo.scss',
'todo/static/src/views/**/*',
'todo/static/src/web/**/*',
@@ -0,0 +1,57 @@
/** @odoo-module */
import { useState, onRendered, useRef } from "@odoo/owl";
import { _lt } from "@web/core/l10n/translation";
import { CharField } from "@web/views/fields/char/char_field";
import { useAutoresize } from "@web/core/utils/autoresize";
export class TodoEditableBreadcrumbName extends CharField {
setup() {
super.setup();
this.placeholder = _lt("Untitled to-do");;
this.input = useRef("input");
this.stateTodo = useState({
isUntitled: true,
});
onRendered(() => {
this.stateTodo.isUntitled = this._isUntitled(this.props.record.data[this.props.name]);
});
useAutoresize(this.input);
}
/**
* Check if the name is empty or is the generic name
* for untitled todos.
* @param {string} name
* @returns {boolean}
*/
_isUntitled(name) {
name = name && name.trim();
return !name || name === this.placeholder.toString();
}
/**
* @private
* @param {InputEvent} ev
*/
_onFocus(ev) {
if (this._isUntitled(ev.target.value)) {
ev.target.value = this.placeholder;
ev.target.select();
}
}
/**
* @private
* @param {InputEvent} ev
*/
_onInput(ev) {
const value = ev.target.value;
this.stateTodo.isUntitled = this._isUntitled(value);
}
}
TodoEditableBreadcrumbName.template = 'todo.TodoEditableBreadcrumbName';
@@ -0,0 +1,30 @@
.o_todo_breadcrumb_name_input{
width: 100%;
text-overflow: clip;
overflow: hidden;
border: none;
padding: 0px 5px 0px 2px;
color: $o-brand-secondary;
&:focus {
outline: 1px solid #ccc;
outline-offset: -1px;
color: $dark;
}
&.o-todo-untitled {
font-style: italic;
}
&.o-todo-untitled:focus {
font-style: normal;
}
&:disabled {
background-color: inherit;
}
}
.o_todo_breadcrumb_name_input:hover {
border-bottom: $input-border-width solid $o-brand-primary;
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="todo.TodoEditableBreadcrumbName" t-inherit="web.CharField" t-inherit-mode="primary" owl="1">
<xpath expr="//input[hasclass('o_input')]" position="attributes">
<attribute name="class">o_input o_todo_breadcrumb_name_input</attribute>
<attribute name="t-on-focus">_onFocus</attribute>
<attribute name="t-att-placeholder">placeholder</attribute>
<attribute name="t-on-input">_onInput</attribute>
<attribute name="t-att-class">stateTodo.isUntitled ? 'o-todo-untitled' : ''</attribute>
</xpath>
</t>
</templates>
@@ -0,0 +1,7 @@
/** @odoo-module **/
import { ControlPanel } from "@web/search/control_panel/control_panel";
export class TodoFormControlPanel extends ControlPanel {
static template = "todo.TodoFormControlPanel";
}
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="todo.TodoFormControlPanel" t-inherit="web.ControlPanel" t-inherit-mode="primary" owl="1">
<xpath expr="//div/t/t[@t-slot='control-panel-breadcrumb']" position="replace">
<t t-slot="control-panel-breadcrumb">
<t t-call="todo.TodoBreadcrumbs"/>
</t>
</xpath>
</t>
<t t-name="todo.TodoBreadcrumbs" t-inherit="web.Breadcrumbs" t-inherit-mode="primary" owl="1">
<xpath expr="//div[hasclass('o_last_breadcrumb_item')]//span" position="replace">
<t t-slot="control-panel-todo-name" />
</xpath>
</t>
</templates>
@@ -0,0 +1,14 @@
/** @odoo-module **/
import { FormController } from "@web/views/form/form_controller";
import { TodoEditableBreadcrumbName } from "@todo/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name";
/**
* The FormController is overridden to be able to manage the edition of the name of a to-do directly
* in the breadcrumb.
*/
export class TodoFormController extends FormController {}
Object.assign(TodoFormController.components, { TodoEditableBreadcrumbName });
TodoFormController.template = 'todo.TodoFormView';
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="todo.TodoFormView" t-inherit="web.FormView" t-inherit-mode="primary" owl="1">
<xpath expr="//div[hasclass('o_form_view_container')]/Layout" position="inside">
<t t-set-slot="control-panel-todo-name">
<t t-set="record" t-value="model.root"/>
<TodoEditableBreadcrumbName
name="'name'"
record="record"
id="'name'"
/>
</t>
</xpath>
</t>
</templates>
@@ -0,0 +1,14 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { formView } from "@web/views/form/form_view";
import { TodoFormControlPanel } from "./todo_form_control_panel";
import { TodoFormController } from "./todo_form_controller";
export const todoFormView = {
...formView,
ControlPanel: TodoFormControlPanel,
Controller: TodoFormController,
};
registry.category("views").add("todo_form", todoFormView);
+1
View File
@@ -98,6 +98,7 @@
<form string="To-do"
class="o_todo_form_view"
js_class="todo_form">
<field name="name" invisible="1"/>
<header>
<div class="py-1 px-2 border rounded bg-view">
<field name="tag_ids"