diff --git a/.idea/pl_mail.iml b/.idea/pl_mail.iml
index e759430a..17777333 100644
--- a/.idea/pl_mail.iml
+++ b/.idea/pl_mail.iml
@@ -139,7 +139,6 @@
-
@@ -153,6 +152,7 @@
+
diff --git a/assets/controllers.json b/assets/controllers.json
index 8d7580b8..4c681b6d 100644
--- a/assets/controllers.json
+++ b/assets/controllers.json
@@ -1,5 +1,16 @@
{
"controllers": {
+ "@symfony/ux-autocomplete": {
+ "autocomplete": {
+ "enabled": true,
+ "fetch": "eager",
+ "autoimport": {
+ "tom-select/dist/css/tom-select.default.css": true,
+ "tom-select/dist/css/tom-select.bootstrap4.css": false,
+ "tom-select/dist/css/tom-select.bootstrap5.css": false
+ }
+ }
+ },
"@symfony/ux-turbo": {
"turbo-core": {
"enabled": true,
diff --git a/assets/controllers/address_autocomplete_controller.js b/assets/controllers/address_autocomplete_controller.js
index 0564c56e..0bb124cf 100644
--- a/assets/controllers/address_autocomplete_controller.js
+++ b/assets/controllers/address_autocomplete_controller.js
@@ -19,110 +19,110 @@ export default class extends Controller {
#tomSelect = null;
connect() {
- const input = this.inputTarget;
-
- // Forward clicks anywhere in the row to the TS input.
- this._boundRowClick = (e) => {
- console.log("row click", e.target);
- if (e.target.closest(".item")) { return; }
- this.#tomSelect?.focus();
- };
- this.element.addEventListener("click", this._boundRowClick);
-
- // Gather any pre-filled addresses from the hidden input's value
- // (e.g. when editing a draft or doing reply/forward).
- const prefilledJson = input.dataset.prefilled;
- let prefilledItems = [];
- if (prefilledJson) {
- try { prefilledItems = JSON.parse(prefilledJson); } catch (_) {}
- }
-
- this.#tomSelect = new TomSelect(input, {
- // Allow typing any address freely
- create: true,
- createOnBlur: true,
- createFilter: /\S+@\S+\.\S+/, // only create if it looks like an email
- maxItems: null, // unlimited recipients
- persist: false, // don't keep typed text after blur
- valueField: "value",
- labelField: "label",
- searchField: ["label", "email"],
- openOnFocus: false,
- hideSelected: true,
- closeAfterSelect: false, // keep open so you can add another quickly
- preload: false,
- onItemAdd: function() {
- this.setTextboxValue('');
- this.close();
- },
-
- // ── Seed with pre-filled addresses (reply/forward/draft) ───────
- options: prefilledItems,
- items: prefilledItems.map((i) => i.value),
-
- // ── Remote search ─────────────────────────────────────────────
- load: async (query, callback) => {
- if (query.length < MIN_CHARS) {
- callback([]);
- return;
- }
-
- try {
- const response = await fetch(
- `${AUTOCOMPLETE_URL}?q=${encodeURIComponent(query)}`,
- { headers: { "X-Requested-With": "XMLHttpRequest" } },
- );
-
- if (!response.ok) { callback([]); return; }
-
- const contacts = await response.json();
-
- callback(contacts.map((c) => ({
- value: c.email,
- label: c.displayName ? `${c.displayName} <${c.email}>` : c.email,
- email: c.email,
- displayName: c.displayName ?? "",
- initials: c.initials ?? c.email[0].toUpperCase(),
- })));
- } catch (_) {
- callback([]);
- }
- },
-
- // ── Renderers ────────────────────────────────────────────────
- render: {
- // Suggestion row in the dropdown
- option: (data, escape) => `
-
- ${escape(data.initials ?? data.email?.[0]?.toUpperCase() ?? "?")}
-
- ${data.displayName ? `${escape(data.displayName)}` : ""}
- ${escape(data.email ?? data.value)}
-
-
`,
-
- // Selected chip inside the control
- item: (data, escape) => `
-
- ${escape(data.displayName || data.email || data.value)}
-
`,
-
- // "Add " row — rendered subtly at the bottom
- option_create: (data, escape) => `
-
-
- Add ${escape(data.input)}
-
`,
-
- no_results: () => `No contacts found
`,
- loading: () => `Searching…
`,
- },
- });
+ // const input = this.inputTarget;
+ //
+ // // Forward clicks anywhere in the row to the TS input.
+ // this._boundRowClick = (e) => {
+ // console.log("row click", e.target);
+ // if (e.target.closest(".item")) { return; }
+ // this.#tomSelect?.focus();
+ // };
+ // this.element.addEventListener("click", this._boundRowClick);
+ //
+ // // Gather any pre-filled addresses from the hidden input's value
+ // // (e.g. when editing a draft or doing reply/forward).
+ // const prefilledJson = input.dataset.prefilled;
+ // let prefilledItems = [];
+ // if (prefilledJson) {
+ // try { prefilledItems = JSON.parse(prefilledJson); } catch (_) {}
+ // }
+ //
+ // this.#tomSelect = new TomSelect(input, {
+ // // Allow typing any address freely
+ // create: true,
+ // createOnBlur: true,
+ // createFilter: /\S+@\S+\.\S+/, // only create if it looks like an email
+ // maxItems: null, // unlimited recipients
+ // persist: false, // don't keep typed text after blur
+ // valueField: "value",
+ // labelField: "label",
+ // searchField: ["label", "email"],
+ // openOnFocus: false,
+ // hideSelected: true,
+ // closeAfterSelect: false, // keep open so you can add another quickly
+ // preload: false,
+ // onItemAdd: function() {
+ // this.setTextboxValue('');
+ // this.close();
+ // },
+ //
+ // // ── Seed with pre-filled addresses (reply/forward/draft) ───────
+ // options: prefilledItems,
+ // items: prefilledItems.map((i) => i.value),
+ //
+ // // ── Remote search ─────────────────────────────────────────────
+ // load: async (query, callback) => {
+ // if (query.length < MIN_CHARS) {
+ // callback([]);
+ // return;
+ // }
+ //
+ // try {
+ // const response = await fetch(
+ // `${AUTOCOMPLETE_URL}?q=${encodeURIComponent(query)}`,
+ // { headers: { "X-Requested-With": "XMLHttpRequest" } },
+ // );
+ //
+ // if (!response.ok) { callback([]); return; }
+ //
+ // const contacts = await response.json();
+ //
+ // callback(contacts.map((c) => ({
+ // value: c.email,
+ // label: c.displayName ? `${c.displayName} <${c.email}>` : c.email,
+ // email: c.email,
+ // displayName: c.displayName ?? "",
+ // initials: c.initials ?? c.email[0].toUpperCase(),
+ // })));
+ // } catch (_) {
+ // callback([]);
+ // }
+ // },
+ //
+ // // ── Renderers ────────────────────────────────────────────────
+ // render: {
+ // // Suggestion row in the dropdown
+ // option: (data, escape) => `
+ //
+ // ${escape(data.initials ?? data.email?.[0]?.toUpperCase() ?? "?")}
+ //
+ // ${data.displayName ? `${escape(data.displayName)}` : ""}
+ // ${escape(data.email ?? data.value)}
+ //
+ //
`,
+ //
+ // // Selected chip inside the control
+ // item: (data, escape) => `
+ //
+ // ${escape(data.displayName || data.email || data.value)}
+ //
`,
+ //
+ // // "Add " row — rendered subtly at the bottom
+ // option_create: (data, escape) => `
+ //
+ //
+ // Add ${escape(data.input)}
+ //
`,
+ //
+ // no_results: () => `No contacts found
`,
+ // loading: () => `Searching…
`,
+ // },
+ // });
}
disconnect() {
- this.element.removeEventListener("click", this._boundRowClick);
- this.#tomSelect?.destroy();
- this.#tomSelect = null;
+ // this.element.removeEventListener("click", this._boundRowClick);
+ // this.#tomSelect?.destroy();
+ // this.#tomSelect = null;
}
}
diff --git a/assets/styles/tom-select.css b/assets/styles/tom-select.css
deleted file mode 100644
index 4d314c1c..00000000
--- a/assets/styles/tom-select.css
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Tom Select — compose-window theme
- *
- * Tom Select wraps the