@profitlich/template-toolkit 5.5.1 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.project.md +72 -1
- package/dev/Dev.js +6 -2
- package/dev/toolbar/Toolbar.js +65 -1
- package/package.json +1 -1
package/CLAUDE.project.md
CHANGED
|
@@ -22,7 +22,78 @@ Dieser Abschnitt stammt aus `@profitlich/template-toolkit` und wird bei jedem `c
|
|
|
22
22
|
|
|
23
23
|
- Zustandskommunikation zwischen Komponenten über `CustomEvent`, nicht direkte Methodenaufrufe.
|
|
24
24
|
- Event-Namenskonvention: `event` + PascalCase → `eventMenuStatus`, `eventBodyScrolled`.
|
|
25
|
-
- DOM
|
|
25
|
+
- DOM-**Zustand** per `data-*`-Attribut, nie als CSS-Klassen-Toggle: `document.body.setAttribute('data-menu-active', 'true')`.
|
|
26
|
+
|
|
27
|
+
Das betrifft den Zustand eines Elements. Wie ein Skript seine Elemente überhaupt **findet**, regelt der nächste Abschnitt.
|
|
28
|
+
|
|
29
|
+
### Wie JavaScript seine Elemente findet
|
|
30
|
+
|
|
31
|
+
Zwei Fragen, die leicht durcheinandergeraten:
|
|
32
|
+
|
|
33
|
+
*Welches Skript lädt diese Seite?* → immer `craft.vite.register` im Template, das das Markup rendert. Gilt unabhängig von allem Folgenden.
|
|
34
|
+
|
|
35
|
+
*Woran erkennt das Skript seine Elemente?* → dafür gilt:
|
|
36
|
+
|
|
37
|
+
> **Wer entscheidet, dort steht es.** Entscheidet der Code, steht der Selektor im Code. Entscheidet der Inhalt, steht es im Markup.
|
|
38
|
+
|
|
39
|
+
**Klassen-Selektor**, wenn das Verhalten untrennbar zur Komponente gehört — jedes Element dieser Klasse verhält sich immer so, es gibt nichts zu entscheiden:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
new Subcategory('.subcategory');
|
|
43
|
+
new HoverImages('.client-list__link');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**`data`-Attribut**, sobald eine der beiden Bedingungen zutrifft:
|
|
47
|
+
|
|
48
|
+
1. **Die Redaktion entscheidet, ob.** Ein CMS-Feld schaltet das Verhalten — `module.fixiert` → `data-sticky`, `module.raster` → `data-grid-type`. Der Code kann das nicht wissen.
|
|
49
|
+
2. **Das Element trägt einen Wert**, den das Skript braucht — `data-hover-image`, `data-categories`, `data-map-zoom`.
|
|
50
|
+
|
|
51
|
+
Fallen beide zusammen, ist der Selektor das Attribut:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
new Grid('[data-grid-type]');
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Nicht überall Attribute: Ein `data-swiper="true"` an jeder `.subcategory` wäre eine zweite Stelle zum Ändern und suggeriert eine Wahlmöglichkeit, die es nicht gibt. Nicht überall Selektoren: Redaktionelle Entscheidungen und Werte pro Element kann der Code nicht kennen.
|
|
58
|
+
|
|
59
|
+
### Entry oder Klassendatei
|
|
60
|
+
|
|
61
|
+
Eine JS-Datei ist das eine oder das andere, nie beides:
|
|
62
|
+
|
|
63
|
+
- **Entry** — initialisiert sich selbst auf `DOMContentLoaded` und wird von keinem Modul importiert. Geladen durch `craft.vite.register`.
|
|
64
|
+
- **Klassendatei** — wird importiert und tut nichts, bis jemand sie konstruiert.
|
|
65
|
+
|
|
66
|
+
Vermischt man beides, wird ein Import zur versteckten Ladeanweisung: Er sieht ungenutzt aus, ist aber das Einzige, was die Funktion startet. Wer ihn entfernt — oder ein Linter, der ihn meldet — legt sie lautlos still.
|
|
67
|
+
|
|
68
|
+
### Debug-Code
|
|
69
|
+
|
|
70
|
+
Debug-Code bleibt im Quelltext — man braucht ihn beim nächsten Mal wieder. Er darf nur die **Produktion** nicht erreichen; auf Dev **und Staging** bleibt er vollständig erhalten.
|
|
71
|
+
|
|
72
|
+
Dafür zwei Hebel, je nach Fall:
|
|
73
|
+
|
|
74
|
+
- **`console.*`** — entfernt Terser beim Produktions-Build über `drop_console: mode === 'production'` in der `vite.config.js`. Kein Zutun nötig.
|
|
75
|
+
- **Alles andere** (Debug-Ausgaben ins DOM, Messungen, Overlays) — in `if (__DEBUG__) { … }` einschliessen. Vite ersetzt die Konstante zur Bauzeit per `define: { __DEBUG__: mode !== 'production' }`; daraus wird `if (false)`, und der Minifier wirft den Zweig weg.
|
|
76
|
+
|
|
77
|
+
An `mode` hängen, **nicht** an `import.meta.env.DEV` — Letzteres ist auf Staging bereits `false` und würde den Debug-Code dort verschlucken.
|
|
78
|
+
|
|
79
|
+
**Debug-Funktionen auf Modulebene schreiben, nicht als private Klassenmethoden.** Der Minifier entfernt zwar in beiden Fällen den Aufruf, schüttelt ungenutzte private Klassenmethoden aber nicht ab — deren Rumpf bliebe im Produktions-Bundle liegen. Als Funktion verschwindet er vollständig.
|
|
80
|
+
|
|
81
|
+
Ganze Dateien, die nur der Entwicklung dienen, werden gar nicht erst geladen: `{% if craft.app.env != 'production' %}` um die Registrierung, wie bei `Dev.js`.
|
|
82
|
+
|
|
83
|
+
**Debug-Anzeigen schaltbar machen** — nicht dauerhaft einblenden. Die Dev-Toolbar nimmt dafür projekteigene Checkboxen entgegen:
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
// src/Dev.js
|
|
87
|
+
initDev(config, {
|
|
88
|
+
toggles: [{ key: 'navigateSpace', name: 'Navigate Space' }],
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Der Wert steht danach als `body[data-dev-navigate-space="true"]` bereit — für reine CSS-Anzeigen genügt das, weiteres JS braucht es nicht. Module, die auf den Wechsel *reagieren* müssen statt ihn nur darzustellen, hören auf `eventDevToggle` und lesen `event.detail.key` und `event.detail.value`.
|
|
93
|
+
|
|
94
|
+
Der Schalter ersetzt nicht `__DEBUG__`, er ergänzt es: `__DEBUG__` entscheidet, ob der Code überhaupt ausgeliefert wird, der Schalter, ob man ihn gerade sehen will.
|
|
95
|
+
|
|
96
|
+
Ist ESLint eingerichtet, braucht `__DEBUG__` einen Eintrag unter `languageOptions.globals`, sonst meldet `no-undef`.
|
|
26
97
|
|
|
27
98
|
### SCSS
|
|
28
99
|
|
package/dev/Dev.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { Toolbar } from './toolbar/Toolbar.js';
|
|
2
2
|
|
|
3
3
|
let _config = {};
|
|
4
|
+
let _options = {};
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Hinterlegt die Projekt-Config für die Dev-Toolbar.
|
|
7
8
|
* @param {Object} [config={}] - Inhalt von `src/config.json` des Projekts.
|
|
9
|
+
* @param {import('./toolbar/Toolbar.js').ToolbarOptions} [options={}] - Projekteigene
|
|
10
|
+
* Ergänzungen der Toolbar, derzeit `toggles`.
|
|
8
11
|
*/
|
|
9
|
-
export function initDev(config = {}) {
|
|
12
|
+
export function initDev(config = {}, options = {}) {
|
|
10
13
|
_config = config;
|
|
14
|
+
_options = options;
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
document.addEventListener('DOMContentLoaded', () => {
|
|
14
|
-
new Toolbar();
|
|
18
|
+
new Toolbar(_options);
|
|
15
19
|
console.info('Dev initialized.');
|
|
16
20
|
});
|
package/dev/toolbar/Toolbar.js
CHANGED
|
@@ -2,6 +2,22 @@ import GUI from 'lil-gui';
|
|
|
2
2
|
import { MediaQueries } from '../../utils/MediaQueries.js';
|
|
3
3
|
import './toolbar.scss';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Beschreibt eine projekteigene Checkbox in der Dev-Toolbar.
|
|
7
|
+
* @typedef {Object} ToolbarToggle
|
|
8
|
+
* @property {string} key - Schlüssel im State und in `localStorage.devTools`.
|
|
9
|
+
* @property {string} name - Beschriftung in der Toolbar.
|
|
10
|
+
* @property {string} [attribute] - Data-Attribut am `<body>`. Ohne Angabe aus dem
|
|
11
|
+
* Schlüssel abgeleitet: `navigateSpace` → `data-dev-navigate-space`.
|
|
12
|
+
* @property {boolean} [default=false] - Startwert, solange nichts gespeichert ist.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Optionen, mit denen ein Projekt die Toolbar erweitert.
|
|
17
|
+
* @typedef {Object} ToolbarOptions
|
|
18
|
+
* @property {ToolbarToggle[]} [toggles=[]] - Zusätzliche Checkboxen.
|
|
19
|
+
*/
|
|
20
|
+
|
|
5
21
|
/**
|
|
6
22
|
* Dev-Toolbar (lil-gui) mit Grid-Overlay, Bildgrössen- und Inhaltstyp-Labels.
|
|
7
23
|
* State wird in `localStorage.devTools` persistiert. Toggle via `Ctrl`.
|
|
@@ -12,10 +28,19 @@ export class Toolbar {
|
|
|
12
28
|
#state;
|
|
13
29
|
#pictureElements;
|
|
14
30
|
#contentTypeContainer;
|
|
31
|
+
#toggles;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {ToolbarOptions} [options={}] - Projekteigene Ergänzungen.
|
|
35
|
+
*/
|
|
36
|
+
constructor(options = {}) {
|
|
37
|
+
this.#toggles = options.toggles ?? [];
|
|
15
38
|
|
|
16
|
-
constructor() {
|
|
17
39
|
// State aus localStorage laden
|
|
18
40
|
const defaults = { visible: false, grid: 'aus', imageSize: false, sizes: false, contentType: false };
|
|
41
|
+
for (const toggle of this.#toggles) {
|
|
42
|
+
defaults[toggle.key] = toggle.default ?? false;
|
|
43
|
+
}
|
|
19
44
|
const saved = localStorage.getItem('devTools');
|
|
20
45
|
this.#state = saved ? { ...defaults, ...JSON.parse(saved) } : defaults;
|
|
21
46
|
|
|
@@ -51,6 +76,14 @@ export class Toolbar {
|
|
|
51
76
|
.name('Inhaltstypen')
|
|
52
77
|
.onChange(() => this.#onStateChange());
|
|
53
78
|
|
|
79
|
+
// Projekteigene Schalter ans Ende, damit die Reihenfolge der festen
|
|
80
|
+
// Einträge über alle Projekte hinweg gleich bleibt
|
|
81
|
+
for (const toggle of this.#toggles) {
|
|
82
|
+
this.#gui.add(this.#state, toggle.key)
|
|
83
|
+
.name(toggle.name)
|
|
84
|
+
.onChange(() => this.#onStateChange());
|
|
85
|
+
}
|
|
86
|
+
|
|
54
87
|
// State anwenden
|
|
55
88
|
this.#applyState();
|
|
56
89
|
|
|
@@ -76,6 +109,7 @@ export class Toolbar {
|
|
|
76
109
|
#applyState() {
|
|
77
110
|
document.body.setAttribute('data-dev-grid', this.#state.grid);
|
|
78
111
|
document.body.setAttribute('data-dev-content-types', this.#state.contentType);
|
|
112
|
+
this.#applyToggles();
|
|
79
113
|
this.#updateImageSize();
|
|
80
114
|
this.#updateSizes();
|
|
81
115
|
this.#updateContentTypeLabels();
|
|
@@ -95,6 +129,25 @@ export class Toolbar {
|
|
|
95
129
|
});
|
|
96
130
|
}
|
|
97
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Schreibt jeden projekteigenen Schalter als Data-Attribut ans `<body>` und
|
|
134
|
+
* meldet ihn zusätzlich per Event.
|
|
135
|
+
*
|
|
136
|
+
* Beides, weil beide Seiten gebraucht werden: Das Attribut genügt für reines
|
|
137
|
+
* CSS und gilt auch für Listener, die es zum Zeitpunkt des Umschaltens noch
|
|
138
|
+
* nicht gab; das Event erreicht Module, die auf den Wechsel reagieren müssen,
|
|
139
|
+
* statt ihn nur darzustellen.
|
|
140
|
+
*/
|
|
141
|
+
#applyToggles() {
|
|
142
|
+
for (const toggle of this.#toggles) {
|
|
143
|
+
const value = this.#state[toggle.key];
|
|
144
|
+
document.body.setAttribute(toggleAttribute(toggle), String(value));
|
|
145
|
+
document.dispatchEvent(new CustomEvent('eventDevToggle', {
|
|
146
|
+
detail: { key: toggle.key, value },
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
98
151
|
#saveState() {
|
|
99
152
|
localStorage.setItem('devTools', JSON.stringify(this.#state));
|
|
100
153
|
}
|
|
@@ -159,3 +212,14 @@ export class Toolbar {
|
|
|
159
212
|
}
|
|
160
213
|
}
|
|
161
214
|
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Liefert das Data-Attribut eines Schalters — die explizite Angabe, sonst aus dem
|
|
218
|
+
* Schlüssel abgeleitet: `navigateSpace` → `data-dev-navigate-space`.
|
|
219
|
+
* @param {ToolbarToggle} toggle
|
|
220
|
+
* @returns {string}
|
|
221
|
+
*/
|
|
222
|
+
function toggleAttribute(toggle) {
|
|
223
|
+
return toggle.attribute
|
|
224
|
+
?? `data-dev-${toggle.key.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`)}`;
|
|
225
|
+
}
|
package/package.json
CHANGED