@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 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-Zustand per `data-*`-Attribut, nie als CSS-Klassen-Toggle: `document.body.setAttribute('data-menu-active', 'true')`.
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
  });
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profitlich/template-toolkit",
3
- "version": "5.5.1",
3
+ "version": "5.8.0",
4
4
  "description": "Shared SCSS layout system, JS utilities, components and build scripts for profitlich template repos",
5
5
  "type": "module",
6
6
  "sass": "scss/forward.scss",