PageSourceSearch

https://www.woelsau-genetics.de/_assets/613472cb0ab23ee7615d4cc8fa9dfd5b/JavaScript/module/app.js?t=1759226020

js woelsau-genetics.de collected 2026-10-09 22:39:40 UTC 15,121 bytes, 392 lines download raw bytes

1import {VirtualizerController} from '@tanstack/lit-virtual';
2import {html, LitElement} from 'lit-element';
3import {repeat} from 'lit/directives/repeat.js';
4import {when} from 'lit/directives/when.js';
5import {createRef, ref} from 'lit/directives/ref.js';
6import {unsafeHTML} from 'lit/directives/unsafe-html.js';
7
8window.process = {
9    env: {
10        NODE_ENV: 'production',
11    },
12};
13
14/**
15 * @typedef {{
16 *     identifier: string,
17 *     preview: string
18 * }} Icon
19 */
20
21/**
22 * @typedef {{
23 *     name: string,
24 *     identifier: string,
25 *     iconCount: number,
26 *     previews: Icon[],
27 *     meta: {
28 *         version: string,
29 *         license: {
30 *             title: string,
31 *         }
32 *     }
33 * }} IconSet
34 */
35
36/**
37 * @typedef {{
38 *     link: string,
39 *     previews: string[],
40 *     set: IconSet
41 * }} IconSetRow
42 */
43
44customElements.define(
45    'c3icons-set-list',
46    /**
47     * @property {{sets: IconSetRow[], selectedIconCountBySet: Record<string, number>}} data
48     */
49    class extends LitElement {
50        static properties = {
51            data: {
52                type: Object,
53            },
54        };
55
56        container = createRef();
57        searchInput = createRef();
58        /**
59         * @type {IconSetRow[]}
60         */
61        filteredSets = [];
62        /**
63         * @type {number|null}
64         */
65        resizeTimeout = null;
66
67        connectedCallback() {
68            super.connectedCallback();
69            this.filteredSets = this.data.sets;
70            if (Array.isArray(this.data.selectedIconCountBySet)) {
71                this.data.selectedIconCountBySet = {};
72            }
73            this.#attachVirtualListController();
74            window.addEventListener('resize', this.#onResize);
75        }
76
77        disconnectedCallback() {
78            super.disconnectedCallback();
79            window.removeEventListener('resize', this.#onResize);
80        }
81
82        createRenderRoot() {
83            return this;
84        }
85
86        render() {
87            const virtualizer = this.virtualizerController.getVirtualizer();
88            const virtualRows = virtualizer.getVirtualItems();
89            return html`
90                <div class="input-group mb-4">
91                    <span class="input-group-text">
92                        <typo3-backend-icon identifier="actions-search" size="small"></typo3-backend-icon>
93                    </span>
94                    <input
95                        type="search"
96                        class="form-control"
97                        ${ref(this.searchInput)}
98                        @input="${this.#updateSearch}"
99                        placeholder="${TYPO3.lang['content.search.placeholder']}"
100                    />
101                </div>
102                <div
103                    class="card-container virtual-container"
104                    ${ref(this.container)}
105                    style="height: ${virtualizer.getTotalSize()}px;"
106                >
107                    ${repeat(
108                        virtualRows,
109                        virtualRow => virtualRow.key,
110                        virtualRow => {
111                            const row = this.filteredSets[virtualRow.index];
112                            const set = row.set;
113                            return html`
114                                <a
115                                    href="${row.link}"
116                                    class="card card-size-fixed-small virtual-item"
117                                    style="
118                                       left: ${virtualRow.lane * 300 + virtualRow.lane * 16}px; 
119                                       width: 300px; 
120                                       height: ${virtualRow.size}px; 
121                                       transform: translateY(${virtualRow.start}px)
122                                   "
123                                >
124                                    <div class="card-header">
125                                        <div class="card-header-body">
126                                            <h2 class="card-title">${set.name}</h2>
127                                            <span class="card-subtitle">${set.identifier}</span>
128                                        </div>
129                                    </div>
130                                    <div class="card-body">
131                                        <ul class="list-unstyled">
132                                            ${when(
133                                                this.data.selectedIconCountBySet[set.identifier],
134                                                () => html`
135                                                    <li>
136                                                        <b
137                                                            >${this.data.selectedIconCountBySet[set.identifier]}
138                                                            ${TYPO3.lang['content.iconsSelected']}</b
139                                                        >
140                                                    </li>
141                                                `,
142                                            )}
143                                            <li>${set.iconCount} Icons</li>
144                                            ${when(
145                                                set.meta.version,
146                                                () => html`
147                                                    <li>${TYPO3.lang['content.version']}: ${set.meta.version}</li>
148                                                `,
149                                            )}
150                                            ${when(
151                                                set.meta.license,
152                                                () => html`
153                                                    <li>${TYPO3.lang['content.license']}: ${set.meta.license.title}</li>
154                                                `,
155                                            )}
156                                        </ul>
157                                        <div class="row row-cols-6 gx-2">
158                                            ${repeat(
159                                                set.previews,
160                                                preview => html`
161                                                    <div class="col">
162                                                        <div class="p-1 bg-white rounded-3">
163                                                            <div class="ratio ratio-1x1">
164                                                                <img
165                                                                    src="${preview.preview}"
166                                                                    alt="${preview.identifier}"
167                                                                    loading="lazy"
168                                                                />
169                                                            </div>
170                                                        </div>
171                                                    </div>
172                                                `,
173                                            )}
174                                        </div>
175                                    </div>
176                                </a>
177                            `;
178                        },
179                    )}
180                </div>
181            `;
182        }
183
184        #onResize = () => {
185            clearTimeout(this.resizeTimeout);
186            this.resizeTimeout = setTimeout(() => {
187                this.#attachVirtualListController();
188            }, 250);
189        };
190
191        #updateSearch() {
192            this.filteredSets = this.data.sets.filter(item =>
193                item.set.name.toLowerCase().includes(this.searchInput.value?.value.toLowerCase() ?? ''),
194            );
195            this.#attachVirtualListController();
196        }
197
198        #attachVirtualListController() {
199            const rect = this.getBoundingClientRect();
200            const lanes = Math.floor(rect.width / 316);
201
202            this.virtualizerController?.cleanup();
203            this.virtualizerController = new VirtualizerController(this, {
204                getScrollElement: () => document.querySelector('.module-body'),
205                count: this.filteredSets.length,
206                estimateSize: () => 200,
207                overscan: 5,
208                lanes,
209                gap: 16,
210            });
211
212            this.requestUpdate();
213        }
214    },
215);
216
217customElements.define(
218    'c3icons-icon-list',
219    /**
220     *
221     * @property {{icons: Icon[], selectedIcons: string[]}} data
222     */
223    class extends LitElement {
224        static properties = {
225            data: {
226                type: Object,
227            },
228        };
229
230        container = createRef();
231        searchInput = createRef();
232        /**
233         * @type {Icon[]}
234         */
235        filteredIcons = [];
236        /**
237         * @type {number|null}
238         */
239        resizeTimeout = null;
240        /**
241         * @type {Set<string>}
242         */
243        selectedIcons;
244        /**
245         * @type {HTMLInputElement}
246         */
247        selectionInput;
248
249        connectedCallback() {
250            super.connectedCallback();
251            this.selectionInput = document.querySelector('#c3icons-icon-list-selected');
252            this.filteredIcons = this.data.icons;
253            this.selectedIcons = new Set(this.data.selectedIcons);
254            this.#attachVirtualListController();
255            window.addEventListener('resize', this.#onResize);
256        }
257
258        disconnectedCallback() {
259            super.disconnectedCallback();
260            window.removeEventListener('resize', this.#onResize);
261        }
262
263        createRenderRoot() {
264            return this;
265        }
266
267        render() {
268            const virtualizer = this.virtualizerController.getVirtualizer();
269            const virtualRows = virtualizer.getVirtualItems();
270            return html`
271                <h1 class="mb-4">${this.data.set.name}</h1>
272                <div class="input-group mb-4">
273                    <span class="input-group-text">
274                        <typo3-backend-icon identifier="actions-search" size="small"></typo3-backend-icon>
275                    </span>
276                    <input
277                        type="search"
278                        class="form-control"
279                        ${ref(this.searchInput)}
280                        @input="${this.#updateSearch}"
281                        placeholder="${TYPO3.lang['content.search.placeholder']}"
282                    />
283                </div>
284                <div class="mb-4">
285                    <button class="btn btn-default" @click="${this.#onUseAll}" type="button">
286                        ${TYPO3.lang['content.iconSet.toggleAll']}
287                    </button>
288                </div>
289                <div ${ref(this.container)} class="virtual-container" style="height: ${virtualizer.getTotalSize()}px;">
290                    ${repeat(
291                        virtualRows,
292                        virtualRow => virtualRow.key,
293                        virtualRow => {
294                            const icon = this.filteredIcons[virtualRow.index];
295                            return html`
296                                <div
297                                    class="col virtual-item"
298                                    style="
299                                         left: ${virtualRow.lane * 66 + virtualRow.lane * 8}px; 
300                                         width: 66px; 
301                                         height: ${virtualRow.size}px; 
302                                         transform: translateY(${virtualRow.start}px)
303                                     "
304                                >
305                                    <label class="c3icons-icon mb-0" title="${icon.identifier}">
306                                        <img
307                                            src="${icon.preview}"
308                                            class="rounded"
309                                            loading="lazy"
310                                            alt="${icon.identifier}"
311                                            width="32"
312                                            height="32"
313                                        />
314                                        <input
315                                            type="checkbox"
316                                            class="form-check-input mt-1 me-1 position-absolute top-0 end-0 shadow-none"
317                                            ?checked="${this.selectedIcons.has(icon.identifier)}"
318                                            @change="${this.#toggleIcon}"
319                                            value="${icon.identifier}"
320                                        />
321                                    </label>
322                                </div>
323                            `;
324                        },
325                    )}
326                </div>
327            `;
328        }
329
330        #onUseAll() {
331            if (this.selectedIcons.size) {
332                this.selectedIcons.clear();
333            } else {
334                for (const icon of this.data.icons) {
335                    this.selectedIcons.add(icon.identifier);
336                }
337            }
338            this.#updateSelection();
339            this.requestUpdate();
340        }
341
342        /**
343         * @param {Event} event
344         */
345        #toggleIcon(event) {
346            /** @type {HTMLInputElement} */
347            const target = event.target;
348
349            if (this.selectedIcons.has(target.value)) {
350                this.selectedIcons.delete(target.value);
351            } else {
352                this.selectedIcons.add(target.value);
353            }
354            this.#updateSelection();
355        }
356
357        #updateSelection() {
358            this.selectionInput.value = [...this.selectedIcons].join(',');
359        }
360
361        #onResize = () => {
362            clearTimeout(this.resizeTimeout);
363            this.resizeTimeout = setTimeout(() => {
364                this.#attachVirtualListController();
365            }, 250);
366        };
367
368        #updateSearch() {
369            this.filteredIcons = this.data.icons.filter(icon =>
370                icon.identifier.toLowerCase().includes(this.searchInput.value?.value.toLowerCase() ?? ''),
371            );
372            this.#attachVirtualListController();
373        }
374
375        #attachVirtualListController() {
376            const rect = this.getBoundingClientRect();
377            const lanes = Math.floor(rect.width / 74);
378
379            this.virtualizerController?.cleanup();
380            this.virtualizerController = new VirtualizerController(this, {
381                getScrollElement: () => document.querySelector('.module-body'),
382                count: this.filteredIcons.length,
383                estimateSize: () => 66,
384                overscan: lanes,
385                lanes,
386                gap: 8,
387            });
388
389            this.requestUpdate();
390        }
391    },
392);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.