Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Component, useRef, useState, onWillStart, onWillUpdateProps } from "@odoo/owl";
import { useService, useAutofocus } from "@web/core/utils/hooks";
import { debounce } from "@web/core/utils/timing";
import { basicContainerBuilderComponentProps } from "./utils";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { Dropdown } from "@web/core/dropdown/dropdown";

export class BasicMany2ManySearchInput extends Component {
static template = "html_builder.BasicMany2ManySearchInput";
static props = {
onSearch: Function,
};
setup() {
useAutofocus();
}
}

export class BasicMany2Many extends Component {
static template = "html_builder.BasicMany2Many";
static props = {
...basicContainerBuilderComponentProps,
model: String,
fields: { type: Array, element: String, optional: true },
domain: { type: Array, optional: true },
limit: { type: Number, optional: true },
// createAction: { type: String, optional: true },
selection: { type: Array, element: Object },
setSelection: Function,
canCreate: { type: Boolean, optional: true },
};
static defaultProps = {
fields: [],
domain: [],
limit: 10,
canCreate: false,
};
static components = { Dropdown, DropdownItem, BasicMany2ManySearchInput };

setup() {
this.orm = useService("orm");
this.createInputRef = useRef("createInput");
this.state = useState({
searchResults: [],
});
this.onSearch = debounce(this.search.bind(this), 300);
onWillStart(async () => {
await this.handleProps(this.props);
});
onWillUpdateProps(async (newProps) => {
await this.handleProps(newProps);
});
}
async handleProps(props) {
this.state.searchResults = [];
}
search(ev) {
this._search(ev.target.value);
}
async _search(searchValue) {
const tuples = await this.orm.call(this.props.model, "name_search", [], {
name: searchValue,
args: Object.values(this.props.domain).filter((item) => item !== null),
operator: "ilike",
limit: this.props.limit + 1,
});
this.state.searchResults = [];
for (const tuple of tuples) {
this.state.searchResults.push({
id: tuple[0],
name: tuple[1],
});
}
/* TODO handle types
const records = await this.orm.read(
this.props.model,
tuples.map(([id, _name]) => id),
this.props.fields
);
*/
}
select(entry) {
this.props.setSelection([...this.props.selection, entry]);
}
unselect(id) {
this.props.setSelection([...this.props.selection.filter((item) => item.id !== id)]);
}
create() {
// const name = this.createInputRef.el.value;
// TODO implement create ?
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<t t-name="html_builder.BasicMany2ManySearchInput">
<input placeholder="Search for records..." t-on-input="props.onSearch" t-on-focus="props.onSearch" t-ref="autofocus"/>
</t>

<t t-name="html_builder.BasicMany2Many">
<div>
<table>
<tr t-foreach="props.selection" t-as="entry" t-key="entry.id">
<td>
<input type="text" disabled="" t-att-value="entry.name"/>
</td>
<td>
<button class="o_we_link o_we_text_danger fa fa-fw fa-minus" t-on-click="() => this.unselect(entry.id)"/>
</td>
</tr>
</table>
<Dropdown>
<button class="btn">Choose a record...</button>
<t t-set-slot="content">
<div>
<BasicMany2ManySearchInput onSearch="onSearch.bind(this)"/>
</div>
<t t-foreach="state.searchResults" t-as="entry" t-key="entry.id">
<t t-if="!props.selection.some((sel) => sel.id == entry.id)">
<DropdownItem onSelected="() => this.select(entry)">
<div t-out="entry.name"/>
</DropdownItem>
</t>
</t>
<t t-if="props.hasCreate">
<div class="o_we_m2o_create">
<input type="text" autocomplete="chrome-off" placeholder="" class="text-start" t-ref="createInput"/>
<button class="btn active" t-on-click="() => this.create()">Create</button>
</div>
</t>
</t>
</Dropdown>
</div>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { Component, useState } from "@odoo/owl";
import { useService, useAutofocus } from "@web/core/utils/hooks";
import { debounce } from "@web/core/utils/timing";
import { Component } from "@odoo/owl";
import {
basicContainerBuilderComponentProps,
getAllActionsAndOperations,
useBuilderComponent,
useDomState,
} from "./utils";
import { BuilderComponent } from "./builder_component";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { BasicMany2Many } from "./basic_many2many";

export class BuilderMany2Many extends Component {
static template = "html_builder.BuilderMany2Many";
Expand All @@ -18,20 +15,18 @@ export class BuilderMany2Many extends Component {
model: String,
fields: { type: Array, element: String, optional: true },
domain: { type: Array, optional: true },
limit: Number,
limit: { type: Number, optional: true },
id: { type: String, optional: true },
// currently always fakem2m
// currently always allowDelete
};
static defaultProps = {
...BuilderComponent.defaultProps,
fields: [],
domain: [],
limit: 10,
};
static components = { BuilderComponent, Dropdown, DropdownItem };
static components = { BuilderComponent, BasicMany2Many };

setup() {
this.orm = useService("orm");
useBuilderComponent();
const { getAllActions, callOperation } = getAllActionsAndOperations(this);
this.callOperation = callOperation;
Expand All @@ -49,66 +44,24 @@ export class BuilderMany2Many extends Component {
editingElement: el,
param: actionParam,
});
const selection = JSON.parse(actionValue || "[]");
return {
selection: selection,
selection: JSON.parse(actionValue || "[]"),
};
});
this.state = useState({
searchResults: [],
});
this.onSearch = debounce(this.search.bind(this), 300);
// TODO focus on open dropdown, does not seem to work
useAutofocus();
}
callApply(applySpecs) {
for (const applySpec of applySpecs) {
applySpec.apply({
editingElement: applySpec.editingElement,
param: applySpec.actionParam,
value: JSON.stringify(this.selectionToApply),
value: this.selectionToApply,
loadResult: applySpec.loadResult,
dependencyManager: this.env.dependencyManager,
});
}
}
unselect(id) {
this.selectionToApply = [...this.domState.selection.filter((item) => item.id !== id)];
this.callOperation(this.applyOperation.commit);
}
search(ev) {
this._search(ev.target.value);
}
async _getSearchDomain() {
// TODO
return [];
}
async _search(needle) {
const recTuples = await this.orm.call(this.props.model, "name_search", [], {
name: needle,
args: (
await this._getSearchDomain()
).concat(Object.values(this.props.domain).filter((item) => item !== null)),
operator: "ilike",
limit: this.props.limit + 1,
});
this.state.searchResults.length = 0;
for (const tuple of recTuples) {
this.state.searchResults.push({
id: tuple[0],
name: tuple[1],
});
}
/* TODO handle types
const records = await this.orm.read(
this.props.model,
recTuples.map(([id, _name]) => id),
this.props.fields
);
*/
}
select(entry) {
this.selectionToApply = [...this.domState.selection, entry];
setSelection(newSelection) {
this.selectionToApply = JSON.stringify(newSelection);
this.callOperation(this.applyOperation.commit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,15 @@

<t t-name="html_builder.BuilderMany2Many">
<BuilderComponent>
<div>
<table>
<tr t-foreach="domState.selection" t-as="entry" t-key="entry.id">
<td>
<input type="text" disabled="" t-att-value="entry.name"/>
</td>
<td>
<button class="o_we_link o_we_text_danger fa fa-fw fa-minus" t-on-click="() => this.unselect(entry.id)"/>
</td>
</tr>
</table>
<Dropdown>
<button class="btn">Choose a record...</button>
<t t-set-slot="content">
<div>
<input placeholder="Search for records..." t-on-input="onSearch" t-on-focus="onSearch" t-ref="autofocus"/>
</div>
<t t-foreach="state.searchResults" t-as="entry" t-key="entry.id">
<t t-if="!domState.selection.some((sel) => sel.id == entry.id)">
<DropdownItem onSelected="() => this.select(entry)">
<div t-out="entry.name"/>
</DropdownItem>
</t>
</t>
</t>
</Dropdown>
</div>
<!--
<div class="d-flex flex-row flex-nowrap align-items-center" t-att-data-action-id="props.action" style="width: 60px">
<input
type="text"
autocomplete="chrome-off"
t-att-placeholder="props.placeholder"
t-att-title="props.title"
t-on-change="this.onChange"
t-on-blur="this.onChange"
t-on-input="this.onInput"
t-att-value="this.state.value" />
</div>
-->
<!--
<div class="o_we_user_value_widget o_we_m2m" data-name="event_tag_opt"
data-domain="[[&quot;category_id.website_published&quot;, &quot;=&quot;, true], [&quot;color&quot;, &quot;not in&quot;, [&quot;0&quot;, false]]]"
data-limit="10" data-attribute-name="filterByTagIds" data-fields="[&quot;category_id&quot;]" data-select-data-attribute="" data-no-preview="true" style="position: relative;"
>
<we-title>Event Tags</we-title>
<div>
<we-list class="o_we_user_value_widget o_we_fw" data-unsortable="true" data-not-editable="true" data-allow-empty="true">
<div>
<div class="o_we_table_wrapper">
<table>
<tr>
<td class="o_we_list_record_name">
<input type="text" name="5" data-name="Culture" data-category_id="2,Activity" disabled="">
</td>
<td>
<we-button class="o_we_select_remove_option o_we_link o_we_text_danger fa fa-fw fa-minus" data-remove-option="5"></we-button>
</td>
</tr>
</table>
</div>
<we-select class="o_we_user_value_widget o_we_many2one o_we_widget_opened" data-add-record="" style="position: static;">
<div>
<we-toggler data-placeholder-text="None" class="active">Choose a record...</we-toggler>
<we-selection-items data-placeholder-text="None">
<div class="o_we_m2o_search">
<input placeholder="Search for records...">
</div>
<we-button class="o_we_user_value_widget" data-record-data="{&quot;id&quot;:6,&quot;category_id&quot;:[2,&quot;Activity&quot;],&quot;display_name&quot;:&quot;Music&quot;}" data-add-record="6">
<div>Music</div>
</we-button>
...
</we-selection-items>
<span class="o_we_dropdown_caret"></span>
</div>
</we-select>
</div>
</we-list>
</div>
</div>
-->
</BuilderComponent>
<BasicMany2Many
model="props.model"
limit="props.limit"
domain="props.domain"
fields="props.fields"
selection="domState.selection"
setSelection="setSelection.bind(this)"
/>
</BuilderComponent>
</t>

</templates>
Loading