Skip to content

Commit bc3467c

Browse files
committed
Remove jQuery
1 parent 20884b1 commit bc3467c

File tree

10 files changed

+1402
-698
lines changed

10 files changed

+1402
-698
lines changed

Configuration/TypoScript/constants.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ plugin.tx_find {
2424

2525
count = 20
2626
# cat=plugin.tx_find/file; type=string; label=Path to CSS file (FE)
27-
CSSPath = EXT:find/Resources/Public/CSS/find.css
27+
CSSPath = EXT:find/Resources/Public/JavaScript/find.css
2828
# cat=plugin.tx_find/file; type=string; label=Path to JavaScript file (FE)
2929
JSPath = EXT:find/Resources/Public/JavaScript/find.js
3030
# cat=plugin.tx_find/file; type=string; label=Path to template localisation files (FE)

Configuration/TypoScript/setup.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ plugin.tx_find {
224224
jumpToID = tx_find
225225

226226
CSSPaths.10 = {$plugin.tx_find.settings.CSSPath}
227-
CSSPaths.20 = EXT:find/Resources/Public/CSS/fontello/css/fontello.css
228227
JSPaths.10 = {$plugin.tx_find.settings.JSPath}
229228

230229
languageRootPath = {$plugin.tx_find.settings.languageRootPath}

README.md

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -992,8 +992,8 @@ depending on the `id` s used for fields and facets.
992992
993993
## Prerequisites
994994
995-
- TYPO3 10.4
996-
- PHP 7.4 or higher
995+
- TYPO3 12.4
996+
- PHP 8.2 or higher
997997
998998
## Testing
999999
@@ -1008,10 +1008,69 @@ Open a pull request on Github.
10081008
Make sure to run `composer lint` before to see, if the coding style is met.
10091009
If not, this can be automatically fixed with `composer fix`.
10101010
1011-
### Building CSS files
1011+
### Building CSS and JavaScript files
1012+
1013+
This extension uses [Vite](https://vitejs.dev/) for bundling JavaScript and CSS.
1014+
All modern JavaScript code (no jQuery) and CSS—including third-party libraries like Awesomplete, Choices.js, Chart.js—are built and optimized for production with Vite.
1015+
1016+
---
1017+
1018+
## Directory Structure
1019+
1020+
```
1021+
Resources/
1022+
Private/
1023+
JavaScript/
1024+
find.js # Main JS entry point (imports and code)
1025+
Css/
1026+
find.css # Main CSS entry point (your custom styles)
1027+
Public/
1028+
JavaScript/
1029+
bundle.js # Output (generated by Vite)
1030+
style.css # Output (generated by Vite)
1031+
vite.config.js
1032+
package.json
1033+
```
1034+
1035+
---
1036+
1037+
## Getting Started
1038+
1039+
### 1. **Install Node.js dependencies**
1040+
1041+
```bash
1042+
npm install
1043+
```
1044+
1045+
### 3. **Build assets for production**
1046+
1047+
This will generate production-optimized JS and CSS
1048+
and move your CSS bundle to `Resources/Public/CSS`:
1049+
1050+
```bash
1051+
npm run build
1052+
```
1053+
---
1054+
1055+
### 4. **Include the bundled assets in TYPO3**
1056+
1057+
**Via TypoScript:**
1058+
```typoscript
1059+
page.includeJSFooter.tx_find = EXT:your_extension/Resources/Public/JavaScript/bundle.js
1060+
page.includeCSS.tx_find = EXT:your_extension/Resources/Public/CSS/style.css
1061+
```
1062+
1063+
**Or in a Fluid template:**
1064+
```html
1065+
<f:asset.script src="{f:uri.resource(path: 'JavaScript/bundle.js', extensionName: 'YourExtension')}" />
1066+
<f:asset.css src="{f:uri.resource(path: 'CSS/style.css', extensionName: 'YourExtension')}" />
1067+
```
1068+
1069+
## Development
1070+
1071+
You can use `vite`'s dev server for *local development* (if your extension is being built into a standalone frontend).
1072+
For pure asset building (as with TYPO3), use only `npm run build`.
10121073

1013-
Install Node.js (version 16 or above), run `npm install` and build the final CSS file
1014-
with `npm run build`.
10151074

10161075
Contact
10171076
-------
File renamed without changes.

Resources/Private/JavaScript/find.js

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import '../../Private/CSS/find.css';
2+
import 'choices.js/public/assets/styles/choices.min.css';
3+
import 'awesomplete/awesomplete.css';
4+
import Awesomplete from "awesomplete";
5+
import Choices from "choices.js";
6+
import Chart from "chart.js/auto";
7+
8+
// Helper short-hands
9+
const qs = (sel, ctx = document) => ctx.querySelector(sel);
10+
const qsa = (sel, ctx = document) => Array.from(ctx.querySelectorAll(sel, ctx));
11+
12+
// Container selector: change if your wrapper class changes
13+
const container = qs('.tx_find');
14+
15+
const URLParameterPrefix = "tx_find_find";
16+
17+
function addURLParameter(url, name, value) {
18+
const [base, hash = ""] = url.split("#");
19+
const paramStr = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
20+
return `${base}${base.includes("?") ? "&" : "?"}${paramStr}${hash ? "#" + hash : ""}`;
21+
}
22+
function removeURLParameter(url, name) {
23+
const param = encodeURIComponent(name);
24+
return url.replace(new RegExp('&?' + param + '=[^&]*'), "").replace(/\?$/, "");
25+
}
26+
function changeURL(url) {
27+
history.pushState?.(null, '', url);
28+
}
29+
function changeURLParameterForPage(name, value) {
30+
const paramName = `${URLParameterPrefix}[${name}]`;
31+
let newURL = removeURLParameter(location.href, paramName);
32+
if (value !== undefined) newURL = addURLParameter(newURL, paramName, value);
33+
changeURL(newURL);
34+
qsa('a:not(.no-change)', container).forEach(a => {
35+
if (value !== undefined) a.href = addURLParameter(a.href, paramName, value);
36+
else a.href = removeURLParameter(a.href, paramName);
37+
});
38+
qsa(`input.${paramName}`, container).forEach(input => {
39+
input.name = value !== undefined ? paramName : "";
40+
});
41+
}
42+
43+
// --- Initialize Awesomplete Autocomplete fields ---
44+
function initAutocompleteFields() {
45+
qsa('input[autocompleteURL]', container)
46+
.forEach(input => {
47+
// Placeholder: real implementation needs your backend's endpoint format
48+
input.addEventListener("input", async (e) => {
49+
const term = input.value.trim().toLowerCase();
50+
if (!term) return;
51+
let url = input.getAttribute("autocompleteURL").replace('%25%25%25%25', encodeURIComponent(term));
52+
// Fetch suggestions
53+
const data = await fetch(url).then(r => r.json()).catch(() => []);
54+
// Awesomplete expects `list` to be set
55+
if (!input.awesomplete) input.awesomplete = new Awesomplete(input);
56+
input.awesomplete.list = data;
57+
});
58+
});
59+
}
60+
61+
// --- Initialize Choices.js facet selects ---
62+
function initFacetSearches() {
63+
qsa(".facetSearch", container)
64+
.forEach(select => {
65+
const choices = new Choices(select, {
66+
searchEnabled: true,
67+
shouldSort: false
68+
// ...add more options as needed
69+
});
70+
select.addEventListener('change', () => {
71+
const selected = select.value;
72+
const li = container.querySelector(`li[value='${selected}']`);
73+
if (li) li.querySelector('a')?.click();
74+
});
75+
});
76+
}
77+
78+
// --- Toggle extended search panel ---
79+
function toggleExtendedSearch(e) {
80+
const form = qs(".searchForm", container);
81+
const link = e.target;
82+
const isExtended = form.classList.contains("search-extended");
83+
const extStr = link.getAttribute('extendedstring');
84+
const simpStr = link.getAttribute('simplestring');
85+
if (!isExtended) {
86+
link.textContent = extStr;
87+
qsa(".field-mode-extended", form).forEach(f => (f.style.display = ""));
88+
changeURLParameterForPage('extended', 1);
89+
} else {
90+
link.textContent = simpStr;
91+
qsa(".field-mode-extended", form).forEach(f => (f.style.display = "none"));
92+
changeURLParameterForPage('extended');
93+
}
94+
form.classList.toggle("search-simple");
95+
form.classList.toggle("search-extended");
96+
e.preventDefault();
97+
}
98+
99+
// --- Histogram using Chart.js ---
100+
// Expect each histogram container to have the necessary dataset attributes e.g. data-facet-config, data-link
101+
function initHistogramFacets() {
102+
qsa(".facetHistogram-container .histogram", container).forEach(hist => {
103+
// Parse facet config from dataset
104+
let facetConfig = JSON.parse(hist.dataset.facetConfig);
105+
const {data: terms, barWidth} = facetConfig;
106+
const labels = Object.keys(terms).map(Number).sort((a, b) => a - b);
107+
const values = labels.map(y => terms[y]);
108+
109+
const ctx = document.createElement('canvas');
110+
hist.appendChild(ctx);
111+
let chart = new Chart(ctx, {
112+
type: 'bar',
113+
data: {
114+
labels,
115+
datasets: [{
116+
label: 'Histogram',
117+
data: values,
118+
backgroundColor: "#8884d8"
119+
}]
120+
},
121+
options: {
122+
responsive: true,
123+
plugins: { legend: { display: false }},
124+
scales: {
125+
x: { title: { display: true, text: 'Value' }},
126+
y: { title: { display: true, text: 'Hits' }, beginAtZero: true }
127+
},
128+
onClick: (evt, elements) => {
129+
if (elements.length > 0) {
130+
const i = elements[0].index;
131+
const year = labels[i];
132+
// In your implementation, select a facet and do a search here
133+
// For demo: construct the RANGE string as in your original
134+
const range = `RANGE ${year} TO ${year + barWidth - 1}`;
135+
const linkTemplate = hist.dataset.link;
136+
if (linkTemplate) {
137+
location.href = linkTemplate.replace('%25%25%25%25', encodeURIComponent(range));
138+
}
139+
}
140+
},
141+
// Add hover tooltip as needed (Chart.js does this by default)
142+
}
143+
});
144+
});
145+
}
146+
147+
// --- Main initialization ---
148+
document.addEventListener('DOMContentLoaded', () => {
149+
if (!container) return;
150+
initAutocompleteFields();
151+
initFacetSearches();
152+
initHistogramFacets();
153+
154+
// Toggle extended search
155+
qsa('a.extendedSearch', container).forEach(link =>
156+
link.addEventListener('click', toggleExtendedSearch)
157+
);
158+
});
159+
160+
// --- Show/hide overflow for facets ("Show all"/"Hide" links) ---
161+
function showAllFacetsOfType(e) {
162+
const containingList = e.target.closest('ol');
163+
const linkShowAll = qs('.facetShowAll', containingList);
164+
const linkHideHidden = qs('.facetHideHidden', containingList);
165+
qsa('.hidden', containingList).forEach(el => {
166+
el.style.display = (el.style.display === 'none' || getComputedStyle(el).display === 'none') ? '' : 'none';
167+
});
168+
if (linkShowAll.style.display === 'none' || getComputedStyle(linkShowAll).display === 'none') {
169+
linkShowAll.style.display = '';
170+
linkHideHidden.style.display = 'none';
171+
} else {
172+
linkShowAll.style.display = 'none';
173+
linkHideHidden.style.display = '';
174+
}
175+
e.preventDefault();
176+
}
177+
178+
// --- Paging handler for detail link POST ---
179+
export function detailViewWithPaging(element, position) {
180+
const underlyingQuery = window.underlyingQuery;
181+
function inputWithNameAndValue(name, value) {
182+
const input = document.createElement('input');
183+
input.name = name;
184+
input.value = value;
185+
input.type = 'hidden';
186+
return input;
187+
}
188+
function inputsWithPrefixForObject(prefix, obj) {
189+
return Object.entries(obj).flatMap(([k, v]) =>
190+
(typeof v === 'object')
191+
? inputsWithPrefixForObject(`${prefix}[${k}]`, v)
192+
: [inputWithNameAndValue(`${prefix}[${k}]`, v)]
193+
);
194+
}
195+
if (underlyingQuery) {
196+
const li = element.closest('li');
197+
const ol = li?.closest('ol');
198+
underlyingQuery.position = position ?? (ol ? (+ol.getAttribute('start') + [...ol.children].indexOf(li)) : undefined);
199+
const form = document.createElement('form');
200+
form.action = element.getAttribute('href');
201+
form.method = 'POST';
202+
form.style.display = 'none';
203+
document.body.appendChild(form);
204+
inputsWithPrefixForObject(URLParameterPrefix + '[underlyingQuery]', underlyingQuery)
205+
.forEach(input => form.appendChild(input));
206+
if (qs('.searchForm.search-extended', container))
207+
form.appendChild(inputWithNameAndValue(`${URLParameterPrefix}[extended]`, '1'));
208+
form.submit();
209+
return false;
210+
}
211+
return true;
212+
}
213+
214+
// --- Optionally export functionality for use elsewhere ---
215+
export default {
216+
showAllFacetsOfType,
217+
changeURLParameterForPage,
218+
detailViewWithPaging
219+
};

0 commit comments

Comments
 (0)